aboutsummaryrefslogtreecommitdiff
path: root/circuitpython/extmod/ulab/code/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'circuitpython/extmod/ulab/code/numpy')
-rw-r--r--circuitpython/extmod/ulab/code/numpy/approx.c227
-rw-r--r--circuitpython/extmod/ulab/code/numpy/approx.h29
-rw-r--r--circuitpython/extmod/ulab/code/numpy/carray/carray.c826
-rw-r--r--circuitpython/extmod/ulab/code/numpy/carray/carray.h237
-rw-r--r--circuitpython/extmod/ulab/code/numpy/carray/carray_tools.c28
-rw-r--r--circuitpython/extmod/ulab/code/numpy/carray/carray_tools.h25
-rw-r--r--circuitpython/extmod/ulab/code/numpy/compare.c428
-rw-r--r--circuitpython/extmod/ulab/code/numpy/compare.h150
-rw-r--r--circuitpython/extmod/ulab/code/numpy/create.c783
-rw-r--r--circuitpython/extmod/ulab/code/numpy/create.h79
-rw-r--r--circuitpython/extmod/ulab/code/numpy/fft/fft.c102
-rw-r--r--circuitpython/extmod/ulab/code/numpy/fft/fft.h30
-rw-r--r--circuitpython/extmod/ulab/code/numpy/fft/fft_tools.c287
-rw-r--r--circuitpython/extmod/ulab/code/numpy/fft/fft_tools.h28
-rw-r--r--circuitpython/extmod/ulab/code/numpy/filter.c132
-rw-r--r--circuitpython/extmod/ulab/code/numpy/filter.h20
-rw-r--r--circuitpython/extmod/ulab/code/numpy/linalg/linalg.c541
-rw-r--r--circuitpython/extmod/ulab/code/numpy/linalg/linalg.h27
-rw-r--r--circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.c171
-rw-r--r--circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.h28
-rw-r--r--circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.c66
-rw-r--r--circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.h36
-rw-r--r--circuitpython/extmod/ulab/code/numpy/numerical.c1402
-rw-r--r--circuitpython/extmod/ulab/code/numpy/numerical.h652
-rw-r--r--circuitpython/extmod/ulab/code/numpy/numpy.c383
-rw-r--r--circuitpython/extmod/ulab/code/numpy/numpy.h21
-rw-r--r--circuitpython/extmod/ulab/code/numpy/poly.c250
-rw-r--r--circuitpython/extmod/ulab/code/numpy/poly.h21
-rw-r--r--circuitpython/extmod/ulab/code/numpy/stats.c54
-rw-r--r--circuitpython/extmod/ulab/code/numpy/stats.h20
-rw-r--r--circuitpython/extmod/ulab/code/numpy/transform.c224
-rw-r--r--circuitpython/extmod/ulab/code/numpy/transform.h29
-rw-r--r--circuitpython/extmod/ulab/code/numpy/vector.c844
-rw-r--r--circuitpython/extmod/ulab/code/numpy/vector.h161
34 files changed, 8341 insertions, 0 deletions
diff --git a/circuitpython/extmod/ulab/code/numpy/approx.c b/circuitpython/extmod/ulab/code/numpy/approx.c
new file mode 100644
index 0000000..85cdbf7
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/approx.c
@@ -0,0 +1,227 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Zoltán Vörös
+ * 2020 Diego Elio Pettenò
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "approx.h"
+
+//| """Numerical approximation methods"""
+//|
+
+const mp_obj_float_t approx_trapz_dx = {{&mp_type_float}, MICROPY_FLOAT_CONST(1.0)};
+
+#if ULAB_NUMPY_HAS_INTERP
+//| def interp(
+//| x: ulab.numpy.ndarray,
+//| xp: ulab.numpy.ndarray,
+//| fp: ulab.numpy.ndarray,
+//| *,
+//| left: Optional[_float] = None,
+//| right: Optional[_float] = None
+//| ) -> ulab.numpy.ndarray:
+//| """
+//| :param ulab.numpy.ndarray x: The x-coordinates at which to evaluate the interpolated values.
+//| :param ulab.numpy.ndarray xp: The x-coordinates of the data points, must be increasing
+//| :param ulab.numpy.ndarray fp: The y-coordinates of the data points, same length as xp
+//| :param left: Value to return for ``x < xp[0]``, default is ``fp[0]``.
+//| :param right: Value to return for ``x > xp[-1]``, default is ``fp[-1]``.
+//|
+//| Returns the one-dimensional piecewise linear interpolant to a function with given discrete data points (xp, fp), evaluated at x."""
+//| ...
+//|
+
+STATIC mp_obj_t approx_interp(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_left, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
+ { MP_QSTR_right, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ ndarray_obj_t *x = ndarray_from_mp_obj(args[0].u_obj, 0);
+ ndarray_obj_t *xp = ndarray_from_mp_obj(args[1].u_obj, 0); // xp must hold an increasing sequence of independent values
+ ndarray_obj_t *fp = ndarray_from_mp_obj(args[2].u_obj, 0);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(xp->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(fp->dtype)
+ if((xp->ndim != 1) || (fp->ndim != 1) || (xp->len < 2) || (fp->len < 2) || (xp->len != fp->len)) {
+ mp_raise_ValueError(translate("interp is defined for 1D iterables of equal length"));
+ }
+
+ ndarray_obj_t *y = ndarray_new_linear_array(x->len, NDARRAY_FLOAT);
+ mp_float_t left_value, right_value;
+ uint8_t *xparray = (uint8_t *)xp->array;
+
+ mp_float_t xp_left = ndarray_get_float_value(xparray, xp->dtype);
+ xparray += (xp->len-1) * xp->strides[ULAB_MAX_DIMS - 1];
+ mp_float_t xp_right = ndarray_get_float_value(xparray, xp->dtype);
+
+ uint8_t *fparray = (uint8_t *)fp->array;
+
+ if(args[3].u_obj == mp_const_none) {
+ left_value = ndarray_get_float_value(fparray, fp->dtype);
+ } else {
+ left_value = mp_obj_get_float(args[3].u_obj);
+ }
+ if(args[4].u_obj == mp_const_none) {
+ fparray += (fp->len-1) * fp->strides[ULAB_MAX_DIMS - 1];
+ right_value = ndarray_get_float_value(fparray, fp->dtype);
+ } else {
+ right_value = mp_obj_get_float(args[4].u_obj);
+ }
+
+ xparray = xp->array;
+ fparray = fp->array;
+
+ uint8_t *xarray = (uint8_t *)x->array;
+ mp_float_t *yarray = (mp_float_t *)y->array;
+ uint8_t *temp;
+
+ for(size_t i=0; i < x->len; i++, yarray++) {
+ mp_float_t x_value = ndarray_get_float_value(xarray, x->dtype);
+ xarray += x->strides[ULAB_MAX_DIMS - 1];
+ if(x_value < xp_left) {
+ *yarray = left_value;
+ } else if(x_value > xp_right) {
+ *yarray = right_value;
+ } else { // do the binary search here
+ mp_float_t xp_left_, xp_right_;
+ mp_float_t fp_left, fp_right;
+ size_t left_index = 0, right_index = xp->len - 1, middle_index;
+ while(right_index - left_index > 1) {
+ middle_index = left_index + (right_index - left_index) / 2;
+ temp = xparray + middle_index * xp->strides[ULAB_MAX_DIMS - 1];
+ mp_float_t xp_middle = ndarray_get_float_value(temp, xp->dtype);
+ if(x_value <= xp_middle) {
+ right_index = middle_index;
+ } else {
+ left_index = middle_index;
+ }
+ }
+ temp = xparray + left_index * xp->strides[ULAB_MAX_DIMS - 1];
+ xp_left_ = ndarray_get_float_value(temp, xp->dtype);
+
+ temp = xparray + right_index * xp->strides[ULAB_MAX_DIMS - 1];
+ xp_right_ = ndarray_get_float_value(temp, xp->dtype);
+
+ temp = fparray + left_index * fp->strides[ULAB_MAX_DIMS - 1];
+ fp_left = ndarray_get_float_value(temp, fp->dtype);
+
+ temp = fparray + right_index * fp->strides[ULAB_MAX_DIMS - 1];
+ fp_right = ndarray_get_float_value(temp, fp->dtype);
+
+ *yarray = fp_left + (x_value - xp_left_) * (fp_right - fp_left) / (xp_right_ - xp_left_);
+ }
+ }
+ return MP_OBJ_FROM_PTR(y);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(approx_interp_obj, 2, approx_interp);
+#endif
+
+#if ULAB_NUMPY_HAS_TRAPZ
+//| def trapz(y: ulab.numpy.ndarray, x: Optional[ulab.numpy.ndarray] = None, dx: _float = 1.0) -> _float:
+//| """
+//| :param 1D ulab.numpy.ndarray y: the values of the dependent variable
+//| :param 1D ulab.numpy.ndarray x: optional, the coordinates of the independent variable. Defaults to uniformly spaced values.
+//| :param float dx: the spacing between sample points, if x=None
+//|
+//| Returns the integral of y(x) using the trapezoidal rule.
+//| """
+//| ...
+//|
+
+STATIC mp_obj_t approx_trapz(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_x, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_dx, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&approx_trapz_dx)} },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ ndarray_obj_t *y = ndarray_from_mp_obj(args[0].u_obj, 0);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
+ ndarray_obj_t *x;
+ mp_float_t mean = MICROPY_FLOAT_CONST(0.0);
+ if(y->len < 2) {
+ return mp_obj_new_float(mean);
+ }
+ if((y->ndim != 1)) {
+ mp_raise_ValueError(translate("trapz is defined for 1D iterables"));
+ }
+
+ mp_float_t (*funcy)(void *) = ndarray_get_float_function(y->dtype);
+ uint8_t *yarray = (uint8_t *)y->array;
+
+ size_t count = 1;
+ mp_float_t y1, y2, m;
+
+ if(args[1].u_obj != mp_const_none) {
+ x = ndarray_from_mp_obj(args[1].u_obj, 0); // x must hold an increasing sequence of independent values
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
+ if((x->ndim != 1) || (y->len != x->len)) {
+ mp_raise_ValueError(translate("trapz is defined for 1D arrays of equal length"));
+ }
+
+ mp_float_t (*funcx)(void *) = ndarray_get_float_function(x->dtype);
+ uint8_t *xarray = (uint8_t *)x->array;
+ mp_float_t x1, x2;
+
+ y1 = funcy(yarray);
+ yarray += y->strides[ULAB_MAX_DIMS - 1];
+ x1 = funcx(xarray);
+ xarray += x->strides[ULAB_MAX_DIMS - 1];
+
+ for(size_t i=1; i < y->len; i++) {
+ y2 = funcy(yarray);
+ yarray += y->strides[ULAB_MAX_DIMS - 1];
+ x2 = funcx(xarray);
+ xarray += x->strides[ULAB_MAX_DIMS - 1];
+ mp_float_t value = (x2 - x1) * (y2 + y1);
+ m = mean + (value - mean) / (mp_float_t)count;
+ mean = m;
+ x1 = x2;
+ y1 = y2;
+ count++;
+ }
+ } else {
+ mp_float_t dx = mp_obj_get_float(args[2].u_obj);
+ y1 = funcy(yarray);
+ yarray += y->strides[ULAB_MAX_DIMS - 1];
+
+ for(size_t i=1; i < y->len; i++) {
+ y2 = ndarray_get_float_index(y->array, y->dtype, i);
+ mp_float_t value = (y2 + y1);
+ m = mean + (value - mean) / (mp_float_t)count;
+ mean = m;
+ y1 = y2;
+ count++;
+ }
+ mean *= dx;
+ }
+ return mp_obj_new_float(MICROPY_FLOAT_CONST(0.5)*mean*(y->len-1));
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(approx_trapz_obj, 1, approx_trapz);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/approx.h b/circuitpython/extmod/ulab/code/numpy/approx.h
new file mode 100644
index 0000000..487a98b
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/approx.h
@@ -0,0 +1,29 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Zoltán Vörös
+*/
+
+#ifndef _APPROX_
+#define _APPROX_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+#define APPROX_EPS MICROPY_FLOAT_CONST(1.0e-4)
+#define APPROX_NONZDELTA MICROPY_FLOAT_CONST(0.05)
+#define APPROX_ZDELTA MICROPY_FLOAT_CONST(0.00025)
+#define APPROX_ALPHA MICROPY_FLOAT_CONST(1.0)
+#define APPROX_BETA MICROPY_FLOAT_CONST(2.0)
+#define APPROX_GAMMA MICROPY_FLOAT_CONST(0.5)
+#define APPROX_DELTA MICROPY_FLOAT_CONST(0.5)
+
+MP_DECLARE_CONST_FUN_OBJ_KW(approx_interp_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(approx_trapz_obj);
+
+#endif /* _APPROX_ */
diff --git a/circuitpython/extmod/ulab/code/numpy/carray/carray.c b/circuitpython/extmod/ulab/code/numpy/carray/carray.c
new file mode 100644
index 0000000..a5f8a2b
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/carray/carray.c
@@ -0,0 +1,826 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021-2022 Zoltán Vörös
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/objint.h"
+#include "py/runtime.h"
+#include "py/builtin.h"
+#include "py/misc.h"
+
+#include "../../ulab.h"
+#include "../../ndarray.h"
+#include "../../ulab_tools.h"
+#include "carray.h"
+
+#if ULAB_SUPPORTS_COMPLEX
+
+//| import ulab.numpy
+
+//| def real(val):
+//| """
+//| Return the real part of the complex argument, which can be
+//| either an ndarray, or a scalar."""
+//| ...
+//|
+
+mp_obj_t carray_real(mp_obj_t _source) {
+ if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
+ if(source->dtype != NDARRAY_COMPLEX) {
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
+ ndarray_copy_array(source, target, 0);
+ return MP_OBJ_FROM_PTR(target);
+ } else { // the input is most definitely a complex array
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
+ ndarray_copy_array(source, target, 0);
+ return MP_OBJ_FROM_PTR(target);
+ }
+ } else {
+ mp_raise_NotImplementedError(translate("function is implemented for ndarrays only"));
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(carray_real_obj, carray_real);
+
+//| def imag(val):
+//| """
+//| Return the imaginary part of the complex argument, which can be
+//| either an ndarray, or a scalar."""
+//| ...
+//|
+
+mp_obj_t carray_imag(mp_obj_t _source) {
+ if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
+ if(source->dtype != NDARRAY_COMPLEX) { // if not complex, then the imaginary part is zero
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
+ return MP_OBJ_FROM_PTR(target);
+ } else { // the input is most definitely a complex array
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
+ ndarray_copy_array(source, target, source->itemsize / 2);
+ return MP_OBJ_FROM_PTR(target);
+ }
+ } else {
+ mp_raise_NotImplementedError(translate("function is implemented for ndarrays only"));
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(carray_imag_obj, carray_imag);
+
+#if ULAB_NUMPY_HAS_CONJUGATE
+
+//| def conjugate(val):
+//| """
+//| Return the conjugate of the complex argument, which can be
+//| either an ndarray, or a scalar."""
+//| ...
+//|
+mp_obj_t carray_conjugate(mp_obj_t _source) {
+ if(mp_obj_is_type(_source, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, source->dtype);
+ ndarray_copy_array(source, ndarray, 0);
+ if(source->dtype == NDARRAY_COMPLEX) {
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ array++;
+ for(size_t i = 0; i < ndarray->len; i++) {
+ *array *= MICROPY_FLOAT_CONST(-1.0);
+ array += 2;
+ }
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else {
+ if(mp_obj_is_type(_source, &mp_type_complex)) {
+ mp_float_t real, imag;
+ mp_obj_get_complex(_source, &real, &imag);
+ imag = imag * MICROPY_FLOAT_CONST(-1.0);
+ return mp_obj_new_complex(real, imag);
+ } else if(mp_obj_is_int(_source) || mp_obj_is_float(_source)) {
+ return _source;
+ } else {
+ mp_raise_TypeError(translate("input must be an ndarray, or a scalar"));
+ }
+ }
+ // this should never happen
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(carray_conjugate_obj, carray_conjugate);
+#endif
+
+#if ULAB_NUMPY_HAS_SORT_COMPLEX
+//| def sort_complex(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
+//| """
+//| .. param: a
+//| a one-dimensional ndarray
+//|
+//| Sort a complex array using the real part first, then the imaginary part.
+//| Always returns a sorted complex array, even if the input was real."""
+//| ...
+//|
+
+static void carray_sort_complex_(mp_float_t *array, size_t len) {
+ // array is assumed to be a floating vector containing the real and imaginary parts
+ // of a complex array at alternating positions as
+ // array[0] = real[0]
+ // array[1] = imag[0]
+ // array[2] = real[1]
+ // array[3] = imag[1]
+
+ mp_float_t real, imag;
+ size_t c, q = len, p, r = len >> 1;
+ for (;;) {
+ if (r > 0) {
+ r--;
+ real = array[2 * r];
+ imag = array[2 * r + 1];
+ } else {
+ q--;
+ if(q == 0) {
+ break;
+ }
+ real = array[2 * q];
+ imag = array[2 * q + 1];
+ array[2 * q] = array[0];
+ array[2 * q + 1] = array[1];
+ }
+ p = r;
+ c = r + r + 1;
+ while (c < q) {
+ if(c + 1 < q) {
+ if((array[2 * (c+1)] > array[2 * c]) ||
+ ((array[2 * (c+1)] == array[2 * c]) && (array[2 * (c+1) + 1] > array[2 * c + 1]))) {
+ c++;
+ }
+ }
+ if((array[2 * c] > real) ||
+ ((array[2 * c] == real) && (array[2 * c + 1] > imag))) {
+ array[2 * p] = array[2 * c]; // real part
+ array[2 * p + 1] = array[2 * c + 1]; // imag part
+ p = c;
+ c = p + p + 1;
+ } else {
+ break;
+ }
+ }
+ array[2 * p] = real;
+ array[2 * p + 1] = imag;
+ }
+}
+
+mp_obj_t carray_sort_complex(mp_obj_t _source) {
+ if(!mp_obj_is_type(_source, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("input must be a 1D ndarray"));
+ }
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(_source);
+ if(source->ndim != 1) {
+ mp_raise_TypeError(translate("input must be a 1D ndarray"));
+ }
+
+ ndarray_obj_t *ndarray = ndarray_copy_view_convert_type(source, NDARRAY_COMPLEX);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ carray_sort_complex_(array, ndarray->len);
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(carray_sort_complex_obj, carray_sort_complex);
+#endif
+
+//| def abs(a: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
+//| """
+//| .. param: a
+//| a one-dimensional ndarray
+//|
+//| Return the absolute value of complex ndarray."""
+//| ...
+//|
+
+mp_obj_t carray_abs(ndarray_obj_t *source, ndarray_obj_t *target) {
+ // calculates the absolute value of a complex array and returns a dense array
+ uint8_t *sarray = (uint8_t *)source->array;
+ mp_float_t *tarray = (mp_float_t *)target->array;
+ uint8_t itemsize = mp_binary_get_size('@', NDARRAY_FLOAT, NULL);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t rvalue = *(mp_float_t *)sarray;
+ mp_float_t ivalue = *(mp_float_t *)(sarray + itemsize);
+ *tarray++ = MICROPY_FLOAT_C_FUN(sqrt)(rvalue * rvalue + ivalue * ivalue);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ return MP_OBJ_FROM_PTR(target);
+}
+
+static void carray_copy_part(uint8_t *tarray, uint8_t *sarray, size_t *shape, int32_t *strides) {
+ // copies the real or imaginary part of an array
+ // into the respective part of a dense complex array
+ uint8_t sz = sizeof(mp_float_t);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ memcpy(tarray, sarray, sz);
+ tarray += 2 * sz;
+ sarray += strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1];
+ sarray += strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ sarray -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2];
+ sarray += strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ sarray -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS-3];
+ sarray += strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+}
+
+mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
+ uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides, mp_binary_op_t op) {
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8);
+ results->boolean = 1;
+ uint8_t *array = (uint8_t *)results->array;
+
+ if(op == MP_BINARY_OP_NOT_EQUAL) {
+ memset(array, 1, results->len);
+ }
+
+ if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+
+ ulab_rescale_float_strides(lstrides);
+ ulab_rescale_float_strides(rstrides);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ if((larray[0] == rarray[0]) && (larray[1] == rarray[1])) {
+ *array ^= 0x01;
+ }
+ array++;
+ larray += lstrides[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ larray += lstrides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ larray += lstrides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ larray += lstrides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ rarray += rstrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ } else { // only one of the operands is complex
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ uint8_t *rarray = (uint8_t *)rhs->array;
+
+ // align the complex array to the left
+ uint8_t rdtype = rhs->dtype;
+ int32_t *lstrides_ = lstrides;
+ int32_t *rstrides_ = rstrides;
+
+ if(rhs->dtype == NDARRAY_COMPLEX) {
+ larray = (mp_float_t *)rhs->array;
+ rarray = (uint8_t *)lhs->array;
+ lstrides_ = rstrides;
+ rstrides_ = lstrides;
+ rdtype = lhs->dtype;
+ }
+
+ ulab_rescale_float_strides(lstrides_);
+
+ if(rdtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX_EQUAL(results, array, uint8_t, larray, lstrides_, rarray, rstrides_);
+ } else if(rdtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX_EQUAL(results, array, int8_t, larray, lstrides_, rarray, rstrides_);
+ } else if(rdtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX_EQUAL(results, array, uint16_t, larray, lstrides_, rarray, rstrides_);
+ } else if(rdtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX_EQUAL(results, array, int16_t, larray, lstrides_, rarray, rstrides_);
+ } else if(rdtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX_EQUAL(results, array, mp_float_t, larray, lstrides_, rarray, rstrides_);
+ }
+ }
+ return MP_OBJ_FROM_PTR(results);
+}
+
+mp_obj_t carray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
+ uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
+ mp_float_t *resarray = (mp_float_t *)results->array;
+
+ if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+
+ ulab_rescale_float_strides(lstrides);
+ ulab_rescale_float_strides(rstrides);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ // real part
+ *resarray++ = larray[0] + rarray[0];
+ // imaginary part
+ *resarray++ = larray[1] + rarray[1];
+ larray += lstrides[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ larray += lstrides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ larray += lstrides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ larray += lstrides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ rarray += rstrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ } else { // only one of the operands is complex
+ uint8_t *larray = (uint8_t *)lhs->array;
+ uint8_t *rarray = (uint8_t *)rhs->array;
+
+ // align the complex array to the left
+ uint8_t rdtype = rhs->dtype;
+ int32_t *lstrides_ = lstrides;
+ int32_t *rstrides_ = rstrides;
+
+ if(rhs->dtype == NDARRAY_COMPLEX) {
+ larray = (uint8_t *)rhs->array;
+ rarray = (uint8_t *)lhs->array;
+ lstrides_ = rstrides;
+ rstrides_ = lstrides;
+ rdtype = lhs->dtype;
+ }
+
+ if(rdtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides_, rarray, rstrides_, +);
+ } else if(rdtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides_, rarray, rstrides_, +);
+ } else if(rdtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides_, rarray, rstrides_, +);
+ } else if(rdtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides_, rarray, rstrides_, +);
+ } else if(rdtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides_, rarray, rstrides_, +);
+ }
+
+ // simply copy the imaginary part
+ uint8_t *tarray = (uint8_t *)results->array;
+ tarray += sizeof(mp_float_t);
+
+ if(lhs->dtype == NDARRAY_COMPLEX) {
+ rarray = (uint8_t *)lhs->array;
+ rstrides = lstrides;
+ } else {
+ rarray = (uint8_t *)rhs->array;
+ }
+ rarray += sizeof(mp_float_t);
+ carray_copy_part(tarray, rarray, results->shape, rstrides);
+ }
+ return MP_OBJ_FROM_PTR(results);
+}
+
+static void carray_binary_multiply_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
+ int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
+
+ if(rdtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, *);
+ } else if(rdtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, *);
+ } else if(rdtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, *);
+ } else if(rdtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, *);
+ } else if(rdtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, *);
+ }
+}
+
+mp_obj_t carray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
+ uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
+ mp_float_t *resarray = (mp_float_t *)results->array;
+
+ if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+
+ ulab_rescale_float_strides(lstrides);
+ ulab_rescale_float_strides(rstrides);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ // real part
+ *resarray++ = larray[0] * rarray[0] - larray[1] * rarray[1];
+ // imaginary part
+ *resarray++ = larray[0] * rarray[1] + larray[1] * rarray[0];
+ larray += lstrides[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ larray += lstrides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ larray += lstrides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ larray += lstrides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ rarray += rstrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ } else { // only one of the operands is complex
+
+ uint8_t *larray = (uint8_t *)lhs->array;
+ uint8_t *rarray = (uint8_t *)rhs->array;
+ uint8_t *lo = larray, *ro = rarray;
+ int32_t *left_strides = lstrides;
+ int32_t *right_strides = rstrides;
+ uint8_t rdtype = rhs->dtype;
+
+ // align the complex array to the left
+ if(rhs->dtype == NDARRAY_COMPLEX) {
+ lo = (uint8_t *)rhs->array;
+ ro = (uint8_t *)lhs->array;
+ rdtype = lhs->dtype;
+ left_strides = rstrides;
+ right_strides = lstrides;
+ }
+
+ larray = lo;
+ rarray = ro;
+ // real part
+ carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
+
+ larray = lo + sizeof(mp_float_t);
+ rarray = ro;
+ resarray = (mp_float_t *)results->array;
+ resarray++;
+ // imaginary part
+ carray_binary_multiply_(results, resarray, larray, rarray, left_strides, right_strides, rdtype);
+ }
+ return MP_OBJ_FROM_PTR(results);
+}
+
+mp_obj_t carray_binary_subtract(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
+ uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
+ mp_float_t *resarray = (mp_float_t *)results->array;
+
+ if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+
+ ulab_rescale_float_strides(lstrides);
+ ulab_rescale_float_strides(rstrides);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ // real part
+ *resarray++ = larray[0] - rarray[0];
+ // imaginary part
+ *resarray++ = larray[1] - rarray[1];
+ larray += lstrides[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ larray += lstrides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ larray += lstrides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ larray += lstrides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ rarray += rstrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ } else {
+ uint8_t *larray = (uint8_t *)lhs->array;
+ if(lhs->dtype == NDARRAY_COMPLEX) {
+ uint8_t *rarray = (uint8_t *)rhs->array;
+ if(rhs->dtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, -);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, -);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, -);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, -);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, -);
+ }
+ // copy the imaginary part
+ uint8_t *tarray = (uint8_t *)results->array;
+ tarray += sizeof(mp_float_t);
+
+ larray = (uint8_t *)lhs->array;
+ larray += sizeof(mp_float_t);
+
+ carray_copy_part(tarray, larray, results->shape, lstrides);
+ } else if(rhs->dtype == NDARRAY_COMPLEX) {
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+ ulab_rescale_float_strides(rstrides);
+
+ if(lhs->dtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
+ }
+ }
+ }
+
+ return MP_OBJ_FROM_PTR(results);
+}
+
+static void carray_binary_left_divide_(ndarray_obj_t *results, mp_float_t *resarray, uint8_t *larray, uint8_t *rarray,
+ int32_t *lstrides, int32_t *rstrides, uint8_t rdtype) {
+
+ if(rdtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint8_t, larray, lstrides, rarray, rstrides, /);
+ } else if(rdtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX(results, resarray, int8_t, larray, lstrides, rarray, rstrides, /);
+ } else if(rdtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, uint16_t, larray, lstrides, rarray, rstrides, /);
+ } else if(rdtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX(results, resarray, int16_t, larray, lstrides, rarray, rstrides, /);
+ } else if(rdtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides, /);
+ }
+}
+
+mp_obj_t carray_binary_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs,
+ uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) {
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_COMPLEX);
+ mp_float_t *resarray = (mp_float_t *)results->array;
+
+ if((lhs->dtype == NDARRAY_COMPLEX) && (rhs->dtype == NDARRAY_COMPLEX)) {
+ mp_float_t *larray = (mp_float_t *)lhs->array;
+ mp_float_t *rarray = (mp_float_t *)rhs->array;
+
+ ulab_rescale_float_strides(lstrides);
+ ulab_rescale_float_strides(rstrides);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ // (a + bi) / (c + di) =
+ // (ac + bd) / (c^2 + d^2) + i (bc - ad) / (c^2 + d^2)
+ // denominator
+ mp_float_t denom = rarray[0] * rarray[0] + rarray[1] * rarray[1];
+
+ // real part
+ *resarray++ = (larray[0] * rarray[0] + larray[1] * rarray[1]) / denom;
+ // imaginary part
+ *resarray++ = (larray[1] * rarray[0] - larray[0] * rarray[1]) / denom;
+ larray += lstrides[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ larray -= lstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ larray += lstrides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ larray -= lstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ larray += lstrides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ larray -= lstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ larray += lstrides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ rarray += rstrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ } else {
+ uint8_t *larray = (uint8_t *)lhs->array;
+ uint8_t *rarray = (uint8_t *)rhs->array;
+ if(lhs->dtype == NDARRAY_COMPLEX) {
+ // real part
+ carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
+ // imaginary part
+ resarray = (mp_float_t *)results->array;
+ resarray++;
+ larray = (uint8_t *)lhs->array;
+ larray += sizeof(mp_float_t);
+ rarray = (uint8_t *)rhs->array;
+ carray_binary_left_divide_(results, resarray, larray, rarray, lstrides, rstrides, rhs->dtype);
+ } else {
+ if(lhs->dtype == NDARRAY_UINT8) {
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint8_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_INT8) {
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int8_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_UINT16) {
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, uint16_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_INT16) {
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, int16_t, larray, lstrides, rarray, rstrides);
+ } else if(lhs->dtype == NDARRAY_FLOAT) {
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE(results, resarray, mp_float_t, larray, lstrides, rarray, rstrides);
+ }
+ }
+ }
+
+ return MP_OBJ_FROM_PTR(results);
+}
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/carray/carray.h b/circuitpython/extmod/ulab/code/numpy/carray/carray.h
new file mode 100644
index 0000000..8ca5de2
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/carray/carray.h
@@ -0,0 +1,237 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021-2022 Zoltán Vörös
+*/
+
+#ifndef _CARRAY_
+#define _CARRAY_
+
+MP_DECLARE_CONST_FUN_OBJ_1(carray_real_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(carray_imag_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(carray_conjugate_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(carray_sort_complex_obj);
+
+
+mp_obj_t carray_imag(mp_obj_t );
+mp_obj_t carray_real(mp_obj_t );
+
+mp_obj_t carray_abs(ndarray_obj_t *, ndarray_obj_t *);
+mp_obj_t carray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
+mp_obj_t carray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
+mp_obj_t carray_binary_subtract(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
+mp_obj_t carray_binary_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *);
+mp_obj_t carray_binary_equal_not_equal(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t );
+
+#define BINARY_LOOP_COMPLEX1(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t l = 0;\
+ do {\
+ *(resarray) = *((mp_float_t *)(larray)) OPERATOR *((type_right *)(rarray));\
+ (resarray) += 2;\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
+
+#define BINARY_LOOP_COMPLEX2(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t k = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX1((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
+
+#define BINARY_LOOP_COMPLEX3(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t j = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX2((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
+
+#define BINARY_LOOP_COMPLEX4(results, resarray, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t i = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX3((results), (resarray), type_right, (larray), (lstrides), (rarray), (rstrides), OPERATOR);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
+
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t l = 0;\
+ do {\
+ *(resarray)++ = *((type_left *)(larray)) - (rarray)[0];\
+ *(resarray)++ = -(rarray)[1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
+
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t k = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS-1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
+
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t j = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
+
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t i = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
+
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t l = 0;\
+ do {\
+ mp_float_t *c = (mp_float_t *)(rarray);\
+ mp_float_t denom = c[0] * c[0] + c[1] * c[1];\
+ mp_float_t a = *((type_left *)(larray)) / denom;\
+ *(resarray)++ = a * c[0];\
+ *(resarray)++ = -a * c[1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
+
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t k = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
+
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t j = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
+
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4(results, resarray, type_left, larray, lstrides, rarray, rstrides)\
+ size_t i = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3((results), (resarray), type_left, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
+
+
+#define BINARY_LOOP_COMPLEX_EQUAL1(results, array, type_right, larray, lstrides, rarray, rstrides)\
+ size_t l = 0;\
+ do {\
+ if((*(larray) == *((type_right *)(rarray))) && ((larray)[1] == MICROPY_FLOAT_CONST(0.0))) {\
+ *(array) ^= 0x01;\
+ }\
+ (array)++;\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\
+
+#define BINARY_LOOP_COMPLEX_EQUAL2(results, array, type_right, larray, lstrides, rarray, rstrides)\
+ size_t k = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_EQUAL1((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (results)->shape[ULAB_MAX_DIMS - 2]);\
+
+#define BINARY_LOOP_COMPLEX_EQUAL3(results, array, type_right, larray, lstrides, rarray, rstrides)\
+ size_t j = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_EQUAL2((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (results)->shape[ULAB_MAX_DIMS - 3]);\
+
+#define BINARY_LOOP_COMPLEX_EQUAL4(results, array, type_right, larray, lstrides, rarray, rstrides)\
+ size_t i = 0;\
+ do {\
+ BINARY_LOOP_COMPLEX_EQUAL3((results), (array), type_right, (larray), (lstrides), (rarray), (rstrides));\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < (results)->shape[ULAB_MAX_DIMS - 4]);\
+
+#if ULAB_MAX_DIMS == 1
+#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX1
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT1
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE1
+#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL1
+#endif /* ULAB_MAX_DIMS == 1 */
+
+#if ULAB_MAX_DIMS == 2
+#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX2
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT2
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE2
+#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL2
+#endif /* ULAB_MAX_DIMS == 2 */
+
+#if ULAB_MAX_DIMS == 3
+#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX3
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT3
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE3
+#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL3
+#endif /* ULAB_MAX_DIMS == 3 */
+
+#if ULAB_MAX_DIMS == 4
+#define BINARY_LOOP_COMPLEX BINARY_LOOP_COMPLEX4
+#define BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT BINARY_LOOP_COMPLEX_REVERSED_SUBTRACT4
+#define BINARY_LOOP_COMPLEX_RIGHT_DIVIDE BINARY_LOOP_COMPLEX_RIGHT_DIVIDE4
+#define BINARY_LOOP_COMPLEX_EQUAL BINARY_LOOP_COMPLEX_EQUAL4
+#endif /* ULAB_MAX_DIMS == 4 */
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.c b/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.c
new file mode 100644
index 0000000..7b623d3
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.c
@@ -0,0 +1,28 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Zoltán Vörös
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../../ulab.h"
+#include "../../ndarray.h"
+
+#if ULAB_SUPPORTS_COMPLEX
+
+void raise_complex_NotImplementedError(void) {
+ mp_raise_NotImplementedError(translate("not implemented for complex dtype"));
+}
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.h b/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.h
new file mode 100644
index 0000000..3ac79b5
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/carray/carray_tools.h
@@ -0,0 +1,25 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2022 Zoltán Vörös
+*/
+
+#ifndef _CARRAY_TOOLS_
+#define _CARRAY_TOOLS_
+
+void raise_complex_NotImplementedError(void);
+
+#if ULAB_SUPPORTS_COMPLEX
+ #define NOT_IMPLEMENTED_FOR_COMPLEX() raise_complex_NotImplementedError();
+ #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) if((dtype) == NDARRAY_COMPLEX) raise_complex_NotImplementedError();
+#else
+ #define NOT_IMPLEMENTED_FOR_COMPLEX() // do nothing
+ #define COMPLEX_DTYPE_NOT_IMPLEMENTED(dtype) // do nothing
+#endif
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/compare.c b/circuitpython/extmod/ulab/code/numpy/compare.c
new file mode 100644
index 0000000..5a82072
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/compare.c
@@ -0,0 +1,428 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Zoltán Vörös
+ * 2020 Jeff Epler for Adafruit Industries
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ndarray_operators.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "compare.h"
+
+static mp_obj_t compare_function(mp_obj_t x1, mp_obj_t x2, uint8_t op) {
+ ndarray_obj_t *lhs = ndarray_from_mp_obj(x1, 0);
+ ndarray_obj_t *rhs = ndarray_from_mp_obj(x2, 0);
+ #if ULAB_SUPPORTS_COMPLEX
+ if((lhs->dtype == NDARRAY_COMPLEX) || (rhs->dtype == NDARRAY_COMPLEX)) {
+ NOT_IMPLEMENTED_FOR_COMPLEX()
+ }
+ #endif
+ uint8_t ndim = 0;
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ int32_t *lstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ if(!ndarray_can_broadcast(lhs, rhs, &ndim, shape, lstrides, rstrides)) {
+ mp_raise_ValueError(translate("operands could not be broadcast together"));
+ m_del(size_t, shape, ULAB_MAX_DIMS);
+ m_del(int32_t, lstrides, ULAB_MAX_DIMS);
+ m_del(int32_t, rstrides, ULAB_MAX_DIMS);
+ }
+
+ uint8_t *larray = (uint8_t *)lhs->array;
+ uint8_t *rarray = (uint8_t *)rhs->array;
+
+ if(op == COMPARE_EQUAL) {
+ return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_EQUAL);
+ } else if(op == COMPARE_NOT_EQUAL) {
+ return ndarray_binary_equality(lhs, rhs, ndim, shape, lstrides, rstrides, MP_BINARY_OP_NOT_EQUAL);
+ }
+ // These are the upcasting rules
+ // float always becomes float
+ // operation on identical types preserves type
+ // uint8 + int8 => int16
+ // uint8 + int16 => int16
+ // uint8 + uint16 => uint16
+ // int8 + int16 => int16
+ // int8 + uint16 => uint16
+ // uint16 + int16 => float
+ // The parameters of RUN_COMPARE_LOOP are
+ // typecode of result, type_out, type_left, type_right, lhs operand, rhs operand, operator
+ if(lhs->dtype == NDARRAY_UINT8) {
+ if(rhs->dtype == NDARRAY_UINT8) {
+ RUN_COMPARE_LOOP(NDARRAY_UINT8, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ }
+ } else if(lhs->dtype == NDARRAY_INT8) {
+ if(rhs->dtype == NDARRAY_UINT8) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ RUN_COMPARE_LOOP(NDARRAY_INT8, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ }
+ } else if(lhs->dtype == NDARRAY_UINT16) {
+ if(rhs->dtype == NDARRAY_UINT8) {
+ RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ RUN_COMPARE_LOOP(NDARRAY_UINT16, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ }
+ } else if(lhs->dtype == NDARRAY_INT16) {
+ if(rhs->dtype == NDARRAY_UINT8) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ RUN_COMPARE_LOOP(NDARRAY_INT16, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ }
+ } else if(lhs->dtype == NDARRAY_FLOAT) {
+ if(rhs->dtype == NDARRAY_UINT8) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT8) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_UINT16) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_INT16) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ } else if(rhs->dtype == NDARRAY_FLOAT) {
+ RUN_COMPARE_LOOP(NDARRAY_FLOAT, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides, ndim, shape, op);
+ }
+ }
+ return mp_const_none; // we should never reach this point
+}
+
+static mp_obj_t compare_equal_helper(mp_obj_t x1, mp_obj_t x2, uint8_t comptype) {
+ // scalar comparisons should return a single object of mp_obj_t type
+ mp_obj_t result = compare_function(x1, x2, comptype);
+ if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(result, &iter_buf);
+ mp_obj_t item = mp_iternext(iterable);
+ return item;
+ }
+ return result;
+}
+
+#if ULAB_NUMPY_HAS_CLIP
+
+mp_obj_t compare_clip(mp_obj_t x1, mp_obj_t x2, mp_obj_t x3) {
+ // Note: this function could be made faster by implementing a single-loop comparison in
+ // RUN_COMPARE_LOOP. However, that would add around 2 kB of compile size, while we
+ // would not gain a factor of two in speed, since the two comparisons should still be
+ // evaluated. In contrast, calling the function twice adds only 140 bytes to the firmware
+ if(mp_obj_is_int(x1) || mp_obj_is_float(x1)) {
+ mp_float_t v1 = mp_obj_get_float(x1);
+ mp_float_t v2 = mp_obj_get_float(x2);
+ mp_float_t v3 = mp_obj_get_float(x3);
+ if(v1 < v2) {
+ return x2;
+ } else if(v1 > v3) {
+ return x3;
+ } else {
+ return x1;
+ }
+ } else { // assume ndarrays
+ return compare_function(x2, compare_function(x1, x3, COMPARE_MINIMUM), COMPARE_MAXIMUM);
+ }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_3(compare_clip_obj, compare_clip);
+#endif
+
+#if ULAB_NUMPY_HAS_EQUAL
+
+mp_obj_t compare_equal(mp_obj_t x1, mp_obj_t x2) {
+ return compare_equal_helper(x1, x2, COMPARE_EQUAL);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(compare_equal_obj, compare_equal);
+#endif
+
+#if ULAB_NUMPY_HAS_NOTEQUAL
+
+mp_obj_t compare_not_equal(mp_obj_t x1, mp_obj_t x2) {
+ return compare_equal_helper(x1, x2, COMPARE_NOT_EQUAL);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(compare_not_equal_obj, compare_not_equal);
+#endif
+
+#if ULAB_NUMPY_HAS_ISFINITE | ULAB_NUMPY_HAS_ISINF
+static mp_obj_t compare_isinf_isfinite(mp_obj_t _x, uint8_t mask) {
+ // mask should signify, whether the function is called from isinf (mask = 1),
+ // or from isfinite (mask = 0)
+ if(mp_obj_is_int(_x)) {
+ if(mask) {
+ return mp_const_false;
+ } else {
+ return mp_const_true;
+ }
+ } else if(mp_obj_is_float(_x)) {
+ mp_float_t x = mp_obj_get_float(_x);
+ if(isnan(x)) {
+ return mp_const_false;
+ }
+ if(mask) { // called from isinf
+ return isinf(x) ? mp_const_true : mp_const_false;
+ } else { // called from isfinite
+ return isinf(x) ? mp_const_false : mp_const_true;
+ }
+ } else if(mp_obj_is_type(_x, &ulab_ndarray_type)) {
+ ndarray_obj_t *x = MP_OBJ_TO_PTR(_x);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(x->ndim, x->shape, NDARRAY_BOOL);
+ // At this point, results is all False
+ uint8_t *rarray = (uint8_t *)results->array;
+ if(x->dtype != NDARRAY_FLOAT) {
+ // int types can never be infinite...
+ if(!mask) {
+ // ...so flip all values in the array, if the function was called from isfinite
+ memset(rarray, 1, results->len);
+ }
+ return results;
+ }
+ uint8_t *xarray = (uint8_t *)x->array;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t value = *(mp_float_t *)xarray;
+ if(isnan(value)) {
+ *rarray++ = 0;
+ } else {
+ *rarray++ = isinf(value) ? mask : 1 - mask;
+ }
+ xarray += x->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < x->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ xarray -= x->strides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
+ xarray += x->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < x->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ xarray -= x->strides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
+ xarray += x->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < x->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ xarray -= x->strides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
+ xarray += x->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < x->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+
+ return results;
+ } else {
+ mp_raise_TypeError(translate("wrong input type"));
+ }
+ return mp_const_none;
+}
+#endif
+
+#if ULAB_NUMPY_HAS_ISFINITE
+mp_obj_t compare_isfinite(mp_obj_t _x) {
+ return compare_isinf_isfinite(_x, 0);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(compare_isfinite_obj, compare_isfinite);
+#endif
+
+#if ULAB_NUMPY_HAS_ISINF
+mp_obj_t compare_isinf(mp_obj_t _x) {
+ return compare_isinf_isfinite(_x, 1);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(compare_isinf_obj, compare_isinf);
+#endif
+
+#if ULAB_NUMPY_HAS_MAXIMUM
+mp_obj_t compare_maximum(mp_obj_t x1, mp_obj_t x2) {
+ // extra round, so that we can return maximum(3, 4) properly
+ mp_obj_t result = compare_function(x1, x2, COMPARE_MAXIMUM);
+ if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
+ return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0);
+ }
+ return result;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(compare_maximum_obj, compare_maximum);
+#endif
+
+#if ULAB_NUMPY_HAS_MINIMUM
+
+mp_obj_t compare_minimum(mp_obj_t x1, mp_obj_t x2) {
+ // extra round, so that we can return minimum(3, 4) properly
+ mp_obj_t result = compare_function(x1, x2, COMPARE_MINIMUM);
+ if((mp_obj_is_int(x1) || mp_obj_is_float(x1)) && (mp_obj_is_int(x2) || mp_obj_is_float(x2))) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(result);
+ return mp_binary_get_val_array(ndarray->dtype, ndarray->array, 0);
+ }
+ return result;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(compare_minimum_obj, compare_minimum);
+#endif
+
+#if ULAB_NUMPY_HAS_WHERE
+
+mp_obj_t compare_where(mp_obj_t _condition, mp_obj_t _x, mp_obj_t _y) {
+ // this implementation will work with ndarrays, and scalars only
+ ndarray_obj_t *c = ndarray_from_mp_obj(_condition, 0);
+ ndarray_obj_t *x = ndarray_from_mp_obj(_x, 0);
+ ndarray_obj_t *y = ndarray_from_mp_obj(_y, 0);
+
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(c->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(x->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(y->dtype)
+
+ int32_t *cstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
+
+ size_t *oshape = m_new(size_t, ULAB_MAX_DIMS);
+
+ uint8_t ndim;
+
+ // establish the broadcasting conditions first
+ // if any two of the arrays can be broadcast together, then
+ // the three arrays can also be broadcast together
+ if(!ndarray_can_broadcast(c, x, &ndim, oshape, cstrides, ystrides) ||
+ !ndarray_can_broadcast(c, y, &ndim, oshape, cstrides, ystrides) ||
+ !ndarray_can_broadcast(x, y, &ndim, oshape, xstrides, ystrides)) {
+ mp_raise_ValueError(translate("operands could not be broadcast together"));
+ }
+
+ ndim = MAX(MAX(c->ndim, x->ndim), y->ndim);
+
+ for(uint8_t i = 1; i <= ndim; i++) {
+ cstrides[ULAB_MAX_DIMS - i] = c->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : c->strides[ULAB_MAX_DIMS - i];
+ xstrides[ULAB_MAX_DIMS - i] = x->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : x->strides[ULAB_MAX_DIMS - i];
+ ystrides[ULAB_MAX_DIMS - i] = y->shape[ULAB_MAX_DIMS - i] < 2 ? 0 : y->strides[ULAB_MAX_DIMS - i];
+ oshape[ULAB_MAX_DIMS - i] = MAX(MAX(c->shape[ULAB_MAX_DIMS - i], x->shape[ULAB_MAX_DIMS - i]), y->shape[ULAB_MAX_DIMS - i]);
+ }
+
+ uint8_t out_dtype = ndarray_upcast_dtype(x->dtype, y->dtype);
+ ndarray_obj_t *out = ndarray_new_dense_ndarray(ndim, oshape, out_dtype);
+
+ mp_float_t (*cfunc)(void *) = ndarray_get_float_function(c->dtype);
+ mp_float_t (*xfunc)(void *) = ndarray_get_float_function(x->dtype);
+ mp_float_t (*yfunc)(void *) = ndarray_get_float_function(y->dtype);
+ mp_float_t (*ofunc)(void *, mp_float_t ) = ndarray_set_float_function(out->dtype);
+
+ uint8_t *oarray = (uint8_t *)out->array;
+ uint8_t *carray = (uint8_t *)c->array;
+ uint8_t *xarray = (uint8_t *)x->array;
+ uint8_t *yarray = (uint8_t *)y->array;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t value;
+ mp_float_t cvalue = cfunc(carray);
+ if(cvalue != MICROPY_FLOAT_CONST(0.0)) {
+ value = xfunc(xarray);
+ } else {
+ value = yfunc(yarray);
+ }
+ ofunc(oarray, value);
+ oarray += out->itemsize;
+ carray += cstrides[ULAB_MAX_DIMS - 1];
+ xarray += xstrides[ULAB_MAX_DIMS - 1];
+ yarray += ystrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < out->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ carray -= cstrides[ULAB_MAX_DIMS - 1] * c->shape[ULAB_MAX_DIMS-1];
+ carray += cstrides[ULAB_MAX_DIMS - 2];
+ xarray -= xstrides[ULAB_MAX_DIMS - 1] * x->shape[ULAB_MAX_DIMS-1];
+ xarray += xstrides[ULAB_MAX_DIMS - 2];
+ yarray -= ystrides[ULAB_MAX_DIMS - 1] * y->shape[ULAB_MAX_DIMS-1];
+ yarray += ystrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < out->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ carray -= cstrides[ULAB_MAX_DIMS - 2] * c->shape[ULAB_MAX_DIMS-2];
+ carray += cstrides[ULAB_MAX_DIMS - 3];
+ xarray -= xstrides[ULAB_MAX_DIMS - 2] * x->shape[ULAB_MAX_DIMS-2];
+ xarray += xstrides[ULAB_MAX_DIMS - 3];
+ yarray -= ystrides[ULAB_MAX_DIMS - 2] * y->shape[ULAB_MAX_DIMS-2];
+ yarray += ystrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < out->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ carray -= cstrides[ULAB_MAX_DIMS - 3] * c->shape[ULAB_MAX_DIMS-3];
+ carray += cstrides[ULAB_MAX_DIMS - 4];
+ xarray -= xstrides[ULAB_MAX_DIMS - 3] * x->shape[ULAB_MAX_DIMS-3];
+ xarray += xstrides[ULAB_MAX_DIMS - 4];
+ yarray -= ystrides[ULAB_MAX_DIMS - 3] * y->shape[ULAB_MAX_DIMS-3];
+ yarray += ystrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < out->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ return MP_OBJ_FROM_PTR(out);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_3(compare_where_obj, compare_where);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/compare.h b/circuitpython/extmod/ulab/code/numpy/compare.h
new file mode 100644
index 0000000..90ceaf7
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/compare.h
@@ -0,0 +1,150 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Zoltán Vörös
+*/
+
+#ifndef _COMPARE_
+#define _COMPARE_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+enum COMPARE_FUNCTION_TYPE {
+ COMPARE_EQUAL,
+ COMPARE_NOT_EQUAL,
+ COMPARE_MINIMUM,
+ COMPARE_MAXIMUM,
+ COMPARE_CLIP,
+};
+
+MP_DECLARE_CONST_FUN_OBJ_3(compare_clip_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_equal_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_isfinite_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_isinf_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_minimum_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_maximum_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(compare_not_equal_obj);
+MP_DECLARE_CONST_FUN_OBJ_3(compare_where_obj);
+
+#if ULAB_MAX_DIMS == 1
+#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t l = 0;\
+ do {\
+ *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
+ (array) += (results)->strides[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);\
+ return MP_OBJ_FROM_PTR(results);\
+
+#endif // ULAB_MAX_DIMS == 1
+
+#if ULAB_MAX_DIMS == 2
+#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
+ (array) += (results)->strides[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);\
+ return MP_OBJ_FROM_PTR(results);\
+
+#endif // ULAB_MAX_DIMS == 2
+
+#if ULAB_MAX_DIMS == 3
+#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
+ (array) += (results)->strides[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);\
+ return MP_OBJ_FROM_PTR(results);\
+
+#endif // ULAB_MAX_DIMS == 3
+
+#if ULAB_MAX_DIMS == 4
+#define COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, OPERATOR)\
+ size_t i = 0;\
+ do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *((type_out *)(array)) = *((type_left *)(larray)) OPERATOR *((type_right *)(rarray)) ? (type_out)(*((type_left *)(larray))) : (type_out)(*((type_right *)(rarray)));\
+ (array) += (results)->strides[ULAB_MAX_DIMS - 1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);\
+ (larray) -= (lstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
+ (larray) += (lstrides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];\
+ (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);\
+ return MP_OBJ_FROM_PTR(results);\
+
+#endif // ULAB_MAX_DIMS == 4
+
+#define RUN_COMPARE_LOOP(dtype, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, ndim, shape, op) do {\
+ ndarray_obj_t *results = ndarray_new_dense_ndarray((ndim), (shape), (dtype));\
+ uint8_t *array = (uint8_t *)results->array;\
+ if((op) == COMPARE_MINIMUM) {\
+ COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, <);\
+ }\
+ if((op) == COMPARE_MAXIMUM) {\
+ COMPARE_LOOP(results, array, type_out, type_left, type_right, larray, lstrides, rarray, rstrides, >);\
+ }\
+} while(0)
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/create.c b/circuitpython/extmod/ulab/code/numpy/create.c
new file mode 100644
index 0000000..5777070
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/create.c
@@ -0,0 +1,783 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2019-2021 Zoltán Vörös
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "../ulab.h"
+#include "create.h"
+#include "../ulab_tools.h"
+
+#if ULAB_NUMPY_HAS_ONES | ULAB_NUMPY_HAS_ZEROS | ULAB_NUMPY_HAS_FULL | ULAB_NUMPY_HAS_EMPTY
+static mp_obj_t create_zeros_ones_full(mp_obj_t oshape, uint8_t dtype, mp_obj_t value) {
+ if(!mp_obj_is_int(oshape) && !mp_obj_is_type(oshape, &mp_type_tuple) && !mp_obj_is_type(oshape, &mp_type_list)) {
+ mp_raise_TypeError(translate("input argument must be an integer, a tuple, or a list"));
+ }
+ ndarray_obj_t *ndarray = NULL;
+ if(mp_obj_is_int(oshape)) {
+ size_t n = mp_obj_get_int(oshape);
+ ndarray = ndarray_new_linear_array(n, dtype);
+ } else if(mp_obj_is_type(oshape, &mp_type_tuple) || mp_obj_is_type(oshape, &mp_type_list)) {
+ uint8_t len = (uint8_t)mp_obj_get_int(mp_obj_len_maybe(oshape));
+ if(len > ULAB_MAX_DIMS) {
+ mp_raise_TypeError(translate("too many dimensions"));
+ }
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, ULAB_MAX_DIMS * sizeof(size_t));
+ size_t i = 0;
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t item, iterable = mp_getiter(oshape, &iter_buf);
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION){
+ shape[ULAB_MAX_DIMS - len + i] = (size_t)mp_obj_get_int(item);
+ i++;
+ }
+ ndarray = ndarray_new_dense_ndarray(len, shape, dtype);
+ }
+ if(value != mp_const_none) {
+ if(dtype == NDARRAY_BOOL) {
+ dtype = NDARRAY_UINT8;
+ if(mp_obj_is_true(value)) {
+ value = mp_obj_new_int(1);
+ } else {
+ value = mp_obj_new_int(0);
+ }
+ }
+ for(size_t i=0; i < ndarray->len; i++) {
+ #if ULAB_SUPPORTS_COMPLEX
+ if(dtype == NDARRAY_COMPLEX) {
+ ndarray_set_complex_value(ndarray->array, i, value);
+ } else {
+ ndarray_set_value(dtype, ndarray->array, i, value);
+ }
+ #else
+ ndarray_set_value(dtype, ndarray->array, i, value);
+ #endif
+ }
+ }
+ // if zeros calls the function, we don't have to do anything
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+#endif
+
+#if ULAB_NUMPY_HAS_ARANGE | ULAB_NUMPY_HAS_LINSPACE
+static ndarray_obj_t *create_linspace_arange(mp_float_t start, mp_float_t step, mp_float_t stop, size_t len, uint8_t dtype) {
+ mp_float_t value = start;
+
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype);
+ if(ndarray->boolean == NDARRAY_BOOLEAN) {
+ uint8_t *array = (uint8_t *)ndarray->array;
+ for(size_t i=0; i < len; i++, value += step) {
+ *array++ = value == MICROPY_FLOAT_CONST(0.0) ? 0 : 1;
+ }
+ } else if(dtype == NDARRAY_UINT8) {
+ ARANGE_LOOP(uint8_t, ndarray, len, step, stop);
+ } else if(dtype == NDARRAY_INT8) {
+ ARANGE_LOOP(int8_t, ndarray, len, step, stop);
+ } else if(dtype == NDARRAY_UINT16) {
+ ARANGE_LOOP(uint16_t, ndarray, len, step, stop);
+ } else if(dtype == NDARRAY_INT16) {
+ ARANGE_LOOP(int16_t, ndarray, len, step, stop);
+ } else {
+ ARANGE_LOOP(mp_float_t, ndarray, len, step, stop);
+ }
+ return ndarray;
+}
+#endif
+
+#if ULAB_NUMPY_HAS_ARANGE
+//| @overload
+//| def arange(stop: _float, step: _float = 1, *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray: ...
+//| @overload
+//| def arange(start: _float, stop: _float, step: _float = 1, *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """
+//| .. param: start
+//| First value in the array, optional, defaults to 0
+//| .. param: stop
+//| Final value in the array
+//| .. param: step
+//| Difference between consecutive elements, optional, defaults to 1.0
+//| .. param: dtype
+//| Type of values in the array
+//|
+//| Return a new 1-D array with elements ranging from ``start`` to ``stop``, with step size ``step``."""
+//| ...
+//|
+
+mp_obj_t create_arange(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ uint8_t dtype = NDARRAY_FLOAT;
+ mp_float_t start, stop, step;
+ if(n_args == 1) {
+ start = MICROPY_FLOAT_CONST(0.0);
+ stop = mp_obj_get_float(args[0].u_obj);
+ step = MICROPY_FLOAT_CONST(1.0);
+ if(mp_obj_is_int(args[0].u_obj)) dtype = NDARRAY_INT16;
+ } else if(n_args == 2) {
+ start = mp_obj_get_float(args[0].u_obj);
+ stop = mp_obj_get_float(args[1].u_obj);
+ step = MICROPY_FLOAT_CONST(1.0);
+ if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj)) dtype = NDARRAY_INT16;
+ } else if(n_args == 3) {
+ start = mp_obj_get_float(args[0].u_obj);
+ stop = mp_obj_get_float(args[1].u_obj);
+ step = mp_obj_get_float(args[2].u_obj);
+ if(mp_obj_is_int(args[0].u_obj) && mp_obj_is_int(args[1].u_obj) && mp_obj_is_int(args[2].u_obj)) dtype = NDARRAY_INT16;
+ } else {
+ mp_raise_TypeError(translate("wrong number of arguments"));
+ }
+ if((MICROPY_FLOAT_C_FUN(fabs)(stop) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(start) > 32768) || (MICROPY_FLOAT_C_FUN(fabs)(step) > 32768)) {
+ dtype = NDARRAY_FLOAT;
+ }
+ if(args[3].u_obj != mp_const_none) {
+ dtype = (uint8_t)mp_obj_get_int(args[3].u_obj);
+ }
+ ndarray_obj_t *ndarray;
+ if((stop - start)/step < 0) {
+ ndarray = ndarray_new_linear_array(0, dtype);
+ } else {
+ size_t len = (size_t)(MICROPY_FLOAT_C_FUN(ceil)((stop - start) / step));
+ stop = start + (len - 1) * step;
+ ndarray = create_linspace_arange(start, step, stop, len, dtype);
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_arange_obj, 1, create_arange);
+#endif
+
+#if ULAB_NUMPY_HAS_CONCATENATE
+//| def concatenate(arrays: Tuple[ulab.numpy.ndarray], *, axis: int = 0) -> ulab.numpy.ndarray:
+//| """
+//| .. param: arrays
+//| tuple of ndarrays
+//| .. param: axis
+//| axis along which the arrays will be joined
+//|
+//| Join a sequence of arrays along an existing axis."""
+//| ...
+//|
+
+mp_obj_t create_concatenate(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &mp_type_tuple)) {
+ mp_raise_TypeError(translate("first argument must be a tuple of ndarrays"));
+ }
+ int8_t axis = (int8_t)args[1].u_int;
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ mp_obj_tuple_t *ndarrays = MP_OBJ_TO_PTR(args[0].u_obj);
+
+ // first check, whether the arrays are compatible
+ ndarray_obj_t *_ndarray = MP_OBJ_TO_PTR(ndarrays->items[0]);
+ uint8_t dtype = _ndarray->dtype;
+ uint8_t ndim = _ndarray->ndim;
+ if(axis < 0) {
+ axis += ndim;
+ }
+ if((axis < 0) || (axis >= ndim)) {
+ mp_raise_ValueError(translate("wrong axis specified"));
+ }
+ // shift axis
+ axis = ULAB_MAX_DIMS - ndim + axis;
+ for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) {
+ shape[j] = _ndarray->shape[j];
+ }
+
+ for(uint8_t i=1; i < ndarrays->len; i++) {
+ _ndarray = MP_OBJ_TO_PTR(ndarrays->items[i]);
+ // check, whether the arrays are compatible
+ if((dtype != _ndarray->dtype) || (ndim != _ndarray->ndim)) {
+ mp_raise_ValueError(translate("input arrays are not compatible"));
+ }
+ for(uint8_t j=0; j < ULAB_MAX_DIMS; j++) {
+ if(j == axis) {
+ shape[j] += _ndarray->shape[j];
+ } else {
+ if(shape[j] != _ndarray->shape[j]) {
+ mp_raise_ValueError(translate("input arrays are not compatible"));
+ }
+ }
+ }
+ }
+
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(ndim, shape, dtype);
+ uint8_t *tpos = (uint8_t *)target->array;
+ uint8_t *tarray;
+
+ for(uint8_t p=0; p < ndarrays->len; p++) {
+ // reset the pointer along the axis
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(ndarrays->items[p]);
+ uint8_t *sarray = (uint8_t *)source->array;
+ tarray = tpos;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ memcpy(tarray, sarray, source->itemsize);
+ tarray += target->strides[ULAB_MAX_DIMS - 1];
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ tarray -= target->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ tarray += target->strides[ULAB_MAX_DIMS - 2];
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ tarray -= target->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ tarray += target->strides[ULAB_MAX_DIMS - 3];
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ tarray -= target->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ tarray += target->strides[ULAB_MAX_DIMS - 4];
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ if(p < ndarrays->len - 1) {
+ tpos += target->strides[axis] * source->shape[axis];
+ }
+ }
+ return MP_OBJ_FROM_PTR(target);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_concatenate_obj, 1, create_concatenate);
+#endif
+
+#if ULAB_MAX_DIMS > 1
+#if ULAB_NUMPY_HAS_DIAG
+//| def diag(a: ulab.numpy.ndarray, *, k: int = 0) -> ulab.numpy.ndarray:
+//| """
+//| .. param: a
+//| an ndarray
+//| .. param: k
+//| Offset of the diagonal from the main diagonal. Can be positive or negative.
+//|
+//| Return specified diagonals."""
+//| ...
+//|
+
+mp_obj_t create_diag(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("input must be an ndarray"));
+ }
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
+ if(source->ndim == 1) { // return a rank-2 tensor with the prescribed diagonal
+ ndarray_obj_t *target = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, source->len, source->len), source->dtype);
+ uint8_t *sarray = (uint8_t *)source->array;
+ uint8_t *tarray = (uint8_t *)target->array;
+ for(size_t i=0; i < source->len; i++) {
+ memcpy(tarray, sarray, source->itemsize);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ tarray += (source->len + 1) * target->itemsize;
+ }
+ return MP_OBJ_FROM_PTR(target);
+ }
+ if(source->ndim > 2) {
+ mp_raise_TypeError(translate("input must be a tensor of rank 2"));
+ }
+ int32_t k = args[1].u_int;
+ size_t len = 0;
+ uint8_t *sarray = (uint8_t *)source->array;
+ if(k < 0) { // move the pointer "vertically"
+ if(-k < (int32_t)source->shape[ULAB_MAX_DIMS - 2]) {
+ sarray -= k * source->strides[ULAB_MAX_DIMS - 2];
+ len = MIN(source->shape[ULAB_MAX_DIMS - 2] + k, source->shape[ULAB_MAX_DIMS - 1]);
+ }
+ } else { // move the pointer "horizontally"
+ if(k < (int32_t)source->shape[ULAB_MAX_DIMS - 1]) {
+ sarray += k * source->strides[ULAB_MAX_DIMS - 1];
+ len = MIN(source->shape[ULAB_MAX_DIMS - 1] - k, source->shape[ULAB_MAX_DIMS - 2]);
+ }
+ }
+
+ if(len == 0) {
+ mp_raise_ValueError(translate("offset is too large"));
+ }
+
+ ndarray_obj_t *target = ndarray_new_linear_array(len, source->dtype);
+ uint8_t *tarray = (uint8_t *)target->array;
+
+ for(size_t i=0; i < len; i++) {
+ memcpy(tarray, sarray, source->itemsize);
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ tarray += source->itemsize;
+ }
+ return MP_OBJ_FROM_PTR(target);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_diag_obj, 1, create_diag);
+#endif /* ULAB_NUMPY_HAS_DIAG */
+
+#if ULAB_NUMPY_HAS_EMPTY
+// This function is bound in numpy.c to numpy.zeros(), and is simply an alias for that
+
+//| def empty(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """
+//| .. param: shape
+//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array)
+//| .. param: dtype
+//| Type of values in the array
+//|
+//| Return a new array of the given shape with all elements set to 0. An alias for numpy.zeros."""
+//| ...
+//|
+#endif
+
+#if ULAB_NUMPY_HAS_EYE
+//| def eye(size: int, *, M: Optional[int] = None, k: int = 0, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """Return a new square array of size, with the diagonal elements set to 1
+//| and the other elements set to 0. If k is given, the diagonal is shifted by the specified amount."""
+//| ...
+//|
+
+mp_obj_t create_eye(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_INT, { .u_int = 0 } },
+ { MP_QSTR_M, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_k, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = 0 } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ size_t n = args[0].u_int, m;
+ size_t k = args[2].u_int > 0 ? (size_t)args[2].u_int : (size_t)(-args[2].u_int);
+ uint8_t dtype = args[3].u_int;
+ if(args[1].u_rom_obj == mp_const_none) {
+ m = n;
+ } else {
+ m = mp_obj_get_int(args[1].u_rom_obj);
+ }
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, n, m), dtype);
+ if(dtype == NDARRAY_BOOL) {
+ dtype = NDARRAY_UINT8;
+ }
+ mp_obj_t one = mp_obj_new_int(1);
+ size_t i = 0;
+ if((args[2].u_int >= 0)) {
+ while(k < m) {
+ ndarray_set_value(dtype, ndarray->array, i*m+k, one);
+ k++;
+ i++;
+ }
+ } else {
+ while(k < n) {
+ ndarray_set_value(dtype, ndarray->array, k*m+i, one);
+ k++;
+ i++;
+ }
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_eye_obj, 1, create_eye);
+#endif /* ULAB_NUMPY_HAS_EYE */
+#endif /* ULAB_MAX_DIMS > 1 */
+
+#if ULAB_NUMPY_HAS_FULL
+//| def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[_float, _bool], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """
+//| .. param: shape
+//| Shape of the array, either an integer (for a 1-D array) or a tuple of integers (for tensors of higher rank)
+//| .. param: fill_value
+//| scalar, the value with which the array is filled
+//| .. param: dtype
+//| Type of values in the array
+//|
+//| Return a new array of the given shape with all elements set to 0."""
+//| ...
+//|
+
+mp_obj_t create_full(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ uint8_t dtype = args[2].u_int;
+
+ return create_zeros_ones_full(args[0].u_obj, dtype, args[1].u_obj);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_full_obj, 0, create_full);
+#endif
+
+
+#if ULAB_NUMPY_HAS_LINSPACE
+//| def linspace(
+//| start: _float,
+//| stop: _float,
+//| *,
+//| dtype: _DType = ulab.numpy.float,
+//| num: int = 50,
+//| endpoint: _bool = True,
+//| retstep: _bool = False
+//| ) -> ulab.numpy.ndarray:
+//| """
+//| .. param: start
+//| First value in the array
+//| .. param: stop
+//| Final value in the array
+//| .. param int: num
+//| Count of values in the array.
+//| .. param: dtype
+//| Type of values in the array
+//| .. param bool: endpoint
+//| Whether the ``stop`` value is included. Note that even when
+//| endpoint=True, the exact ``stop`` value may not be included due to the
+//| inaccuracy of floating point arithmetic.
+//| .. param bool: retstep,
+//| If True, return (`samples`, `step`), where `step` is the spacing between samples.
+//|
+//| Return a new 1-D array with ``num`` elements ranging from ``start`` to ``stop`` linearly."""
+//| ...
+//|
+
+mp_obj_t create_linspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } },
+ { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } },
+ { MP_QSTR_retstep, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_false } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(args[2].u_int < 2) {
+ mp_raise_ValueError(translate("number of points must be at least 2"));
+ }
+ size_t len = (size_t)args[2].u_int;
+ mp_float_t start, step, stop;
+
+ ndarray_obj_t *ndarray = NULL;
+
+ #if ULAB_SUPPORTS_COMPLEX
+ mp_float_t step_real, step_imag;
+ bool complex_out = false;
+
+ if(mp_obj_is_type(args[0].u_obj, &mp_type_complex) || mp_obj_is_type(args[1].u_obj, &mp_type_complex)) {
+ complex_out = true;
+ ndarray = ndarray_new_linear_array(len, NDARRAY_COMPLEX);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ mp_float_t start_real, start_imag;
+ mp_float_t stop_real, stop_imag;
+
+ mp_obj_get_complex(args[0].u_obj, &start_real, &start_imag);
+ mp_obj_get_complex(args[1].u_obj, &stop_real, &stop_imag);
+ if(args[3].u_obj == mp_const_true) {
+ step_real = (stop_real - start_real) / (len - 1);
+ step_imag = (stop_imag - start_imag) / (len - 1);
+ } else {
+ step_real = (stop_real - start_real) / len;
+ step_imag = (stop_imag - start_imag) / len;
+ }
+
+ for(size_t i = 0; i < len; i++) {
+ *array++ = start_real;
+ *array++ = start_imag;
+ start_real += step_real;
+ start_imag += step_imag;
+ }
+ } else {
+ #endif
+ start = mp_obj_get_float(args[0].u_obj);
+ stop = mp_obj_get_float(args[1].u_obj);
+
+ uint8_t typecode = args[5].u_int;
+
+ if(args[3].u_obj == mp_const_true) {
+ step = (stop - start) / (len - 1);
+ } else {
+ step = (stop - start) / len;
+ stop = start + step * (len - 1);
+ }
+
+ ndarray = create_linspace_arange(start, step, stop, len, typecode);
+ #if ULAB_SUPPORTS_COMPLEX
+ }
+ #endif
+
+ if(args[4].u_obj == mp_const_false) {
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else {
+ mp_obj_t tuple[2];
+ tuple[0] = ndarray;
+ #if ULAB_SUPPORTS_COMPLEX
+ if(complex_out) {
+ tuple[1] = mp_obj_new_complex(step_real, step_imag);
+ } else {
+ tuple[1] = mp_obj_new_float(step);
+ }
+ #else /* ULAB_SUPPORTS_COMPLEX */
+ tuple[1] = mp_obj_new_float(step);
+ #endif
+
+ return mp_obj_new_tuple(2, tuple);
+ }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_linspace_obj, 2, create_linspace);
+#endif
+
+#if ULAB_NUMPY_HAS_LOGSPACE
+//| def logspace(
+//| start: _float,
+//| stop: _float,
+//| *,
+//| dtype: _DType = ulab.numpy.float,
+//| num: int = 50,
+//| endpoint: _bool = True,
+//| base: _float = 10.0
+//| ) -> ulab.numpy.ndarray:
+//| """
+//| .. param: start
+//| First value in the array
+//| .. param: stop
+//| Final value in the array
+//| .. param int: num
+//| Count of values in the array. Defaults to 50.
+//| .. param: base
+//| The base of the log space. The step size between the elements in
+//| ``ln(samples) / ln(base)`` (or ``log_base(samples)``) is uniform. Defaults to 10.0.
+//| .. param: dtype
+//| Type of values in the array
+//| .. param bool: endpoint
+//| Whether the ``stop`` value is included. Note that even when
+//| endpoint=True, the exact ``stop`` value may not be included due to the
+//| inaccuracy of floating point arithmetic. Defaults to True.
+//|
+//| Return a new 1-D array with ``num`` evenly spaced elements on a log scale.
+//| The sequence starts at ``base ** start``, and ends with ``base ** stop``."""
+//| ...
+//|
+
+const mp_obj_float_t create_float_const_ten = {{&mp_type_float}, MICROPY_FLOAT_CONST(10.0)};
+
+mp_obj_t create_logspace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_num, MP_ARG_INT, { .u_int = 50 } },
+ { MP_QSTR_base, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_PTR(&create_float_const_ten) } },
+ { MP_QSTR_endpoint, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_true } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(args[2].u_int < 2) {
+ mp_raise_ValueError(translate("number of points must be at least 2"));
+ }
+ size_t len = (size_t)args[2].u_int;
+ mp_float_t start, step, quotient;
+ start = mp_obj_get_float(args[0].u_obj);
+ uint8_t dtype = args[5].u_int;
+ mp_float_t base = mp_obj_get_float(args[3].u_obj);
+ if(args[4].u_obj == mp_const_true) step = (mp_obj_get_float(args[1].u_obj) - start)/(len - 1);
+ else step = (mp_obj_get_float(args[1].u_obj) - start) / len;
+ quotient = MICROPY_FLOAT_C_FUN(pow)(base, step);
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype);
+
+ mp_float_t value = MICROPY_FLOAT_C_FUN(pow)(base, start);
+ if(ndarray->dtype == NDARRAY_UINT8) {
+ uint8_t *array = (uint8_t *)ndarray->array;
+ if(ndarray->boolean) {
+ memset(array, 1, len);
+ } else {
+ for(size_t i=0; i < len; i++, value *= quotient) *array++ = (uint8_t)value;
+ }
+ } else if(ndarray->dtype == NDARRAY_INT8) {
+ int8_t *array = (int8_t *)ndarray->array;
+ for(size_t i=0; i < len; i++, value *= quotient) *array++ = (int8_t)value;
+ } else if(ndarray->dtype == NDARRAY_UINT16) {
+ uint16_t *array = (uint16_t *)ndarray->array;
+ for(size_t i=0; i < len; i++, value *= quotient) *array++ = (uint16_t)value;
+ } else if(ndarray->dtype == NDARRAY_INT16) {
+ int16_t *array = (int16_t *)ndarray->array;
+ for(size_t i=0; i < len; i++, value *= quotient) *array++ = (int16_t)value;
+ } else {
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ for(size_t i=0; i < len; i++, value *= quotient) *array++ = value;
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_logspace_obj, 2, create_logspace);
+#endif
+
+#if ULAB_NUMPY_HAS_ONES
+//| def ones(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """
+//| .. param: shape
+//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array)
+//| .. param: dtype
+//| Type of values in the array
+//|
+//| Return a new array of the given shape with all elements set to 1."""
+//| ...
+//|
+
+mp_obj_t create_ones(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ uint8_t dtype = args[1].u_int;
+ mp_obj_t one = mp_obj_new_int(1);
+ return create_zeros_ones_full(args[0].u_obj, dtype, one);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_ones_obj, 0, create_ones);
+#endif
+
+#if ULAB_NUMPY_HAS_ZEROS
+//| def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: _DType = ulab.numpy.float) -> ulab.numpy.ndarray:
+//| """
+//| .. param: shape
+//| Shape of the array, either an integer (for a 1-D array) or a tuple of 2 integers (for a 2-D array)
+//| .. param: dtype
+//| Type of values in the array
+//|
+//| Return a new array of the given shape with all elements set to 0."""
+//| ...
+//|
+
+mp_obj_t create_zeros(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_obj = MP_OBJ_NULL } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_INT, { .u_int = NDARRAY_FLOAT } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ uint8_t dtype = args[1].u_int;
+ return create_zeros_ones_full(args[0].u_obj, dtype, mp_const_none);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_zeros_obj, 0, create_zeros);
+#endif
+
+#if ULAB_NUMPY_HAS_FROMBUFFER
+mp_obj_t create_frombuffer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } },
+ { MP_QSTR_count, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(-1) } },
+ { MP_QSTR_offset, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(0) } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ uint8_t dtype = mp_obj_get_int(args[1].u_obj);
+ size_t offset = mp_obj_get_int(args[3].u_obj);
+
+ mp_buffer_info_t bufinfo;
+ if(mp_get_buffer(args[0].u_obj, &bufinfo, MP_BUFFER_READ)) {
+ size_t sz = ulab_binary_get_size(dtype);
+
+ if(bufinfo.len < offset) {
+ mp_raise_ValueError(translate("offset must be non-negative and no greater than buffer length"));
+ }
+ size_t len = (bufinfo.len - offset) / sz;
+ if((len * sz) != (bufinfo.len - offset)) {
+ mp_raise_ValueError(translate("buffer size must be a multiple of element size"));
+ }
+ if(mp_obj_get_int(args[2].u_obj) > 0) {
+ size_t count = mp_obj_get_int(args[2].u_obj);
+ if(len < count) {
+ mp_raise_ValueError(translate("buffer is smaller than requested size"));
+ } else {
+ len = count;
+ }
+ }
+ ndarray_obj_t *ndarray = m_new_obj(ndarray_obj_t);
+ ndarray->base.type = &ulab_ndarray_type;
+ ndarray->dtype = dtype == NDARRAY_BOOL ? NDARRAY_UINT8 : dtype;
+ ndarray->boolean = dtype == NDARRAY_BOOL ? NDARRAY_BOOLEAN : NDARRAY_NUMERIC;
+ ndarray->ndim = 1;
+ ndarray->len = len;
+ ndarray->itemsize = sz;
+ ndarray->shape[ULAB_MAX_DIMS - 1] = len;
+ ndarray->strides[ULAB_MAX_DIMS - 1] = sz;
+
+ uint8_t *buffer = bufinfo.buf;
+ ndarray->array = buffer + offset;
+ return MP_OBJ_FROM_PTR(ndarray);
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(create_frombuffer_obj, 1, create_frombuffer);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/create.h b/circuitpython/extmod/ulab/code/numpy/create.h
new file mode 100644
index 0000000..18f636c
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/create.h
@@ -0,0 +1,79 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _CREATE_
+#define _CREATE_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+#if ULAB_NUMPY_HAS_ARANGE
+mp_obj_t create_arange(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_arange_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_CONCATENATE
+mp_obj_t create_concatenate(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_concatenate_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_DIAG
+mp_obj_t create_diag(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_diag_obj);
+#endif
+
+#if ULAB_MAX_DIMS > 1
+#if ULAB_NUMPY_HAS_EYE
+mp_obj_t create_eye(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_eye_obj);
+#endif
+#endif
+
+#if ULAB_NUMPY_HAS_FULL
+mp_obj_t create_full(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_full_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_LINSPACE
+mp_obj_t create_linspace(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_linspace_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_LOGSPACE
+mp_obj_t create_logspace(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_logspace_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_ONES
+mp_obj_t create_ones(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_ones_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_ZEROS
+mp_obj_t create_zeros(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_zeros_obj);
+#endif
+
+#if ULAB_NUMPY_HAS_FROMBUFFER
+mp_obj_t create_frombuffer(size_t , const mp_obj_t *, mp_map_t *);
+MP_DECLARE_CONST_FUN_OBJ_KW(create_frombuffer_obj);
+#endif
+
+#define ARANGE_LOOP(type_, ndarray, len, step, stop) \
+({\
+ type_ *array = (type_ *)(ndarray)->array;\
+ for (size_t i = 0; i < (len) - 1; i++, (value) += (step)) {\
+ *array++ = (type_)(value);\
+ }\
+ *array = (type_)(stop);\
+})
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/fft/fft.c b/circuitpython/extmod/ulab/code/numpy/fft/fft.c
new file mode 100644
index 0000000..27cb79c
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/fft/fft.c
@@ -0,0 +1,102 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/runtime.h"
+#include "py/builtin.h"
+#include "py/binary.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+
+#include "../carray/carray_tools.h"
+#include "fft.h"
+
+//| """Frequency-domain functions"""
+//|
+//| import ulab.numpy
+
+
+//| def fft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
+//| """
+//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
+//| :param ulab.numpy.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
+//| :return tuple (r, c): The real and complex parts of the FFT
+//|
+//| Perform a Fast Fourier Transform from the time domain into the frequency domain
+//|
+//| See also ~ulab.extras.spectrum, which computes the magnitude of the fft,
+//| rather than separately returning its real and imaginary parts."""
+//| ...
+//|
+#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
+static mp_obj_t fft_fft(mp_obj_t arg) {
+ return fft_fft_ifft_spectrogram(arg, FFT_FFT);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(fft_fft_obj, fft_fft);
+#else
+static mp_obj_t fft_fft(size_t n_args, const mp_obj_t *args) {
+ if(n_args == 2) {
+ return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_FFT);
+ } else {
+ return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_FFT);
+ }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj, 1, 2, fft_fft);
+#endif
+
+//| def ifft(r: ulab.numpy.ndarray, c: Optional[ulab.numpy.ndarray] = None) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
+//| """
+//| :param ulab.numpy.ndarray r: A 1-dimension array of values whose size is a power of 2
+//| :param ulab.numpy.ndarray c: An optional 1-dimension array of values whose size is a power of 2, giving the complex part of the value
+//| :return tuple (r, c): The real and complex parts of the inverse FFT
+//|
+//| Perform an Inverse Fast Fourier Transform from the frequeny domain into the time domain"""
+//| ...
+//|
+
+#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
+static mp_obj_t fft_ifft(mp_obj_t arg) {
+ return fft_fft_ifft_spectrogram(arg, FFT_IFFT);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(fft_ifft_obj, fft_ifft);
+#else
+static mp_obj_t fft_ifft(size_t n_args, const mp_obj_t *args) {
+ NOT_IMPLEMENTED_FOR_COMPLEX()
+ if(n_args == 2) {
+ return fft_fft_ifft_spectrogram(n_args, args[0], args[1], FFT_IFFT);
+ } else {
+ return fft_fft_ifft_spectrogram(n_args, args[0], mp_const_none, FFT_IFFT);
+ }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj, 1, 2, fft_ifft);
+#endif
+
+STATIC const mp_rom_map_elem_t ulab_fft_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_fft) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_fft), (mp_obj_t)&fft_fft_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_ifft), (mp_obj_t)&fft_ifft_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_fft_globals, ulab_fft_globals_table);
+
+const mp_obj_module_t ulab_fft_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_ulab_fft_globals,
+};
+MP_REGISTER_MODULE(MP_QSTR_ulab_dot_fft, ulab_fft_module, MODULE_ULAB_ENABLED && CIRCUITPY_ULAB);
diff --git a/circuitpython/extmod/ulab/code/numpy/fft/fft.h b/circuitpython/extmod/ulab/code/numpy/fft/fft.h
new file mode 100644
index 0000000..1e50a8d
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/fft/fft.h
@@ -0,0 +1,30 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _FFT_
+#define _FFT_
+
+#include "../../ulab.h"
+#include "../../ulab_tools.h"
+#include "../../ndarray.h"
+#include "fft_tools.h"
+
+extern const mp_obj_module_t ulab_fft_module;
+
+#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
+MP_DECLARE_CONST_FUN_OBJ_3(fft_fft_obj);
+MP_DECLARE_CONST_FUN_OBJ_3(fft_ifft_obj);
+#else
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_fft_obj);
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(fft_ifft_obj);
+#endif
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.c b/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.c
new file mode 100644
index 0000000..8a55927
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.c
@@ -0,0 +1,287 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#include <math.h>
+#include <string.h>
+#include "py/runtime.h"
+
+#include "../../ndarray.h"
+#include "../../ulab_tools.h"
+#include "../carray/carray_tools.h"
+#include "fft_tools.h"
+
+#ifndef MP_PI
+#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
+#endif
+#ifndef MP_E
+#define MP_E MICROPY_FLOAT_CONST(2.71828182845904523536)
+#endif
+
+/* Kernel implementation for the case, when ulab has no complex support
+
+ * The following function takes two arrays, namely, the real and imaginary
+ * parts of a complex array, and calculates the Fourier transform in place.
+ *
+ * The function is basically a modification of four1 from Numerical Recipes,
+ * has no dependencies beyond micropython itself (for the definition of mp_float_t),
+ * and can be used independent of ulab.
+ */
+
+#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
+/* Kernel implementation for the complex case. Data are contained in data as
+
+ data[0], data[1], data[2], data[3], .... , data[2n - 2], data[2n-1]
+ real[0], imag[0], real[1], imag[1], .... , real[n-1], imag[n-1]
+
+ In general
+ real[i] = data[2i]
+ imag[i] = data[2i+1]
+
+*/
+void fft_kernel_complex(mp_float_t *data, size_t n, int isign) {
+ size_t j, m, mmax, istep;
+ mp_float_t tempr, tempi;
+ mp_float_t wtemp, wr, wpr, wpi, wi, theta;
+
+ j = 0;
+ for(size_t i = 0; i < n; i++) {
+ if (j > i) {
+ SWAP(mp_float_t, data[2*i], data[2*j]);
+ SWAP(mp_float_t, data[2*i+1], data[2*j+1]);
+ }
+ m = n >> 1;
+ while (j >= m && m > 0) {
+ j -= m;
+ m >>= 1;
+ }
+ j += m;
+ }
+
+ mmax = 1;
+ while (n > mmax) {
+ istep = mmax << 1;
+ theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
+ wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
+ wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
+ wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
+ wr = MICROPY_FLOAT_CONST(1.0);
+ wi = MICROPY_FLOAT_CONST(0.0);
+ for(m = 0; m < mmax; m++) {
+ for(size_t i = m; i < n; i += istep) {
+ j = i + mmax;
+ tempr = wr * data[2*j] - wi * data[2*j+1];
+ tempi = wr * data[2*j+1] + wi * data[2*j];
+ data[2*j] = data[2*i] - tempr;
+ data[2*j+1] = data[2*i+1] - tempi;
+ data[2*i] += tempr;
+ data[2*i+1] += tempi;
+ }
+ wtemp = wr;
+ wr = wr*wpr - wi*wpi + wr;
+ wi = wi*wpr + wtemp*wpi + wi;
+ }
+ mmax = istep;
+ }
+}
+
+/*
+ * The following function is a helper interface to the python side.
+ * It has been factored out from fft.c, so that the same argument parsing
+ * routine can be called from scipy.signal.spectrogram.
+ */
+mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t data_in, uint8_t type) {
+ if(!mp_obj_is_type(data_in, &ulab_ndarray_type)) {
+ mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
+ }
+ ndarray_obj_t *in = MP_OBJ_TO_PTR(data_in);
+ #if ULAB_MAX_DIMS > 1
+ if(in->ndim != 1) {
+ mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
+ }
+ #endif
+ size_t len = in->len;
+ // Check if input is of length of power of 2
+ if((len & (len-1)) != 0) {
+ mp_raise_ValueError(translate("input array length must be power of 2"));
+ }
+
+ ndarray_obj_t *out = ndarray_new_linear_array(len, NDARRAY_COMPLEX);
+ mp_float_t *data = (mp_float_t *)out->array;
+ uint8_t *array = (uint8_t *)in->array;
+
+ if(in->dtype == NDARRAY_COMPLEX) {
+ uint8_t sz = 2 * sizeof(mp_float_t);
+ uint8_t *data_ = (uint8_t *)out->array;
+ for(size_t i = 0; i < len; i++) {
+ memcpy(data_, array, sz);
+ array += in->strides[ULAB_MAX_DIMS - 1];
+ }
+ } else {
+ mp_float_t (*func)(void *) = ndarray_get_float_function(in->dtype);
+ for(size_t i = 0; i < len; i++) {
+ // real part; the imaginary part is 0, no need to assign
+ *data = func(array);
+ data += 2;
+ array += in->strides[ULAB_MAX_DIMS - 1];
+ }
+ }
+ data -= 2 * len;
+
+ if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
+ fft_kernel_complex(data, len, 1);
+ if(type == FFT_SPECTROGRAM) {
+ ndarray_obj_t *spectrum = ndarray_new_linear_array(len, NDARRAY_FLOAT);
+ mp_float_t *sarray = (mp_float_t *)spectrum->array;
+ for(size_t i = 0; i < len; i++) {
+ *sarray++ = MICROPY_FLOAT_C_FUN(sqrt)(data[0] * data[0] + data[1] * data[1]);
+ data += 2;
+ }
+ m_del(mp_float_t, data, 2 * len);
+ return MP_OBJ_FROM_PTR(spectrum);
+ }
+ } else { // inverse transform
+ fft_kernel_complex(data, len, -1);
+ // TODO: numpy accepts the norm keyword argument
+ for(size_t i = 0; i < len; i++) {
+ *data++ /= len;
+ }
+ }
+ return MP_OBJ_FROM_PTR(out);
+}
+#else /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
+void fft_kernel(mp_float_t *real, mp_float_t *imag, size_t n, int isign) {
+ size_t j, m, mmax, istep;
+ mp_float_t tempr, tempi;
+ mp_float_t wtemp, wr, wpr, wpi, wi, theta;
+
+ j = 0;
+ for(size_t i = 0; i < n; i++) {
+ if (j > i) {
+ SWAP(mp_float_t, real[i], real[j]);
+ SWAP(mp_float_t, imag[i], imag[j]);
+ }
+ m = n >> 1;
+ while (j >= m && m > 0) {
+ j -= m;
+ m >>= 1;
+ }
+ j += m;
+ }
+
+ mmax = 1;
+ while (n > mmax) {
+ istep = mmax << 1;
+ theta = MICROPY_FLOAT_CONST(-2.0)*isign*MP_PI/istep;
+ wtemp = MICROPY_FLOAT_C_FUN(sin)(MICROPY_FLOAT_CONST(0.5) * theta);
+ wpr = MICROPY_FLOAT_CONST(-2.0) * wtemp * wtemp;
+ wpi = MICROPY_FLOAT_C_FUN(sin)(theta);
+ wr = MICROPY_FLOAT_CONST(1.0);
+ wi = MICROPY_FLOAT_CONST(0.0);
+ for(m = 0; m < mmax; m++) {
+ for(size_t i = m; i < n; i += istep) {
+ j = i + mmax;
+ tempr = wr * real[j] - wi * imag[j];
+ tempi = wr * imag[j] + wi * real[j];
+ real[j] = real[i] - tempr;
+ imag[j] = imag[i] - tempi;
+ real[i] += tempr;
+ imag[i] += tempi;
+ }
+ wtemp = wr;
+ wr = wr*wpr - wi*wpi + wr;
+ wi = wi*wpr + wtemp*wpi + wi;
+ }
+ mmax = istep;
+ }
+}
+
+mp_obj_t fft_fft_ifft_spectrogram(size_t n_args, mp_obj_t arg_re, mp_obj_t arg_im, uint8_t type) {
+ if(!mp_obj_is_type(arg_re, &ulab_ndarray_type)) {
+ mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
+ }
+ if(n_args == 2) {
+ if(!mp_obj_is_type(arg_im, &ulab_ndarray_type)) {
+ mp_raise_NotImplementedError(translate("FFT is defined for ndarrays only"));
+ }
+ }
+ ndarray_obj_t *re = MP_OBJ_TO_PTR(arg_re);
+ #if ULAB_MAX_DIMS > 1
+ if(re->ndim != 1) {
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(re->dtype)
+ mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
+ }
+ #endif
+ size_t len = re->len;
+ // Check if input is of length of power of 2
+ if((len & (len-1)) != 0) {
+ mp_raise_ValueError(translate("input array length must be power of 2"));
+ }
+
+ ndarray_obj_t *out_re = ndarray_new_linear_array(len, NDARRAY_FLOAT);
+ mp_float_t *data_re = (mp_float_t *)out_re->array;
+
+ uint8_t *array = (uint8_t *)re->array;
+ mp_float_t (*func)(void *) = ndarray_get_float_function(re->dtype);
+
+ for(size_t i=0; i < len; i++) {
+ *data_re++ = func(array);
+ array += re->strides[ULAB_MAX_DIMS - 1];
+ }
+ data_re -= len;
+ ndarray_obj_t *out_im = ndarray_new_linear_array(len, NDARRAY_FLOAT);
+ mp_float_t *data_im = (mp_float_t *)out_im->array;
+
+ if(n_args == 2) {
+ ndarray_obj_t *im = MP_OBJ_TO_PTR(arg_im);
+ #if ULAB_MAX_DIMS > 1
+ if(im->ndim != 1) {
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(im->dtype)
+ mp_raise_TypeError(translate("FFT is implemented for linear arrays only"));
+ }
+ #endif
+ if (re->len != im->len) {
+ mp_raise_ValueError(translate("real and imaginary parts must be of equal length"));
+ }
+ array = (uint8_t *)im->array;
+ func = ndarray_get_float_function(im->dtype);
+ for(size_t i=0; i < len; i++) {
+ *data_im++ = func(array);
+ array += im->strides[ULAB_MAX_DIMS - 1];
+ }
+ data_im -= len;
+ }
+
+ if((type == FFT_FFT) || (type == FFT_SPECTROGRAM)) {
+ fft_kernel(data_re, data_im, len, 1);
+ if(type == FFT_SPECTROGRAM) {
+ for(size_t i=0; i < len; i++) {
+ *data_re = MICROPY_FLOAT_C_FUN(sqrt)(*data_re * *data_re + *data_im * *data_im);
+ data_re++;
+ data_im++;
+ }
+ }
+ } else { // inverse transform
+ fft_kernel(data_re, data_im, len, -1);
+ // TODO: numpy accepts the norm keyword argument
+ for(size_t i=0; i < len; i++) {
+ *data_re++ /= len;
+ *data_im++ /= len;
+ }
+ }
+ if(type == FFT_SPECTROGRAM) {
+ return MP_OBJ_TO_PTR(out_re);
+ } else {
+ mp_obj_t tuple[2];
+ tuple[0] = out_re;
+ tuple[1] = out_im;
+ return mp_obj_new_tuple(2, tuple);
+ }
+}
+#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
diff --git a/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.h b/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.h
new file mode 100644
index 0000000..9444232
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/fft/fft_tools.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _FFT_TOOLS_
+#define _FFT_TOOLS_
+
+enum FFT_TYPE {
+ FFT_FFT,
+ FFT_IFFT,
+ FFT_SPECTROGRAM,
+};
+
+#if ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE
+void fft_kernel(mp_float_t *, size_t , int );
+mp_obj_t fft_fft_ifft_spectrogram(mp_obj_t , uint8_t );
+#else
+void fft_kernel(mp_float_t *, mp_float_t *, size_t , int );
+mp_obj_t fft_fft_ifft_spectrogram(size_t , mp_obj_t , mp_obj_t , uint8_t );
+#endif /* ULAB_SUPPORTS_COMPLEX & ULAB_FFT_IS_NUMPY_COMPATIBLE */
+
+#endif /* _FFT_TOOLS_ */
diff --git a/circuitpython/extmod/ulab/code/numpy/filter.c b/circuitpython/extmod/ulab/code/numpy/filter.c
new file mode 100644
index 0000000..057cd6d
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/filter.c
@@ -0,0 +1,132 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020-2021 Zoltán Vörös
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../scipy/signal/signal.h"
+#include "carray/carray_tools.h"
+#include "filter.h"
+
+#if ULAB_NUMPY_HAS_CONVOLVE
+
+mp_obj_t filter_convolve(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_a, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_v, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type) || !mp_obj_is_type(args[1].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("convolve arguments must be ndarrays"));
+ }
+
+ ndarray_obj_t *a = MP_OBJ_TO_PTR(args[0].u_obj);
+ ndarray_obj_t *c = MP_OBJ_TO_PTR(args[1].u_obj);
+ // deal with linear arrays only
+ #if ULAB_MAX_DIMS > 1
+ if((a->ndim != 1) || (c->ndim != 1)) {
+ mp_raise_TypeError(translate("convolve arguments must be linear arrays"));
+ }
+ #endif
+ size_t len_a = a->len;
+ size_t len_c = c->len;
+ if(len_a == 0 || len_c == 0) {
+ mp_raise_TypeError(translate("convolve arguments must not be empty"));
+ }
+
+ int len = len_a + len_c - 1; // convolve mode "full"
+ int32_t off = len_c - 1;
+ uint8_t dtype = NDARRAY_FLOAT;
+
+ #if ULAB_SUPPORTS_COMPLEX
+ if((a->dtype == NDARRAY_COMPLEX) || (c->dtype == NDARRAY_COMPLEX)) {
+ dtype = NDARRAY_COMPLEX;
+ }
+ #endif
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(len, dtype);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+
+ uint8_t *aarray = (uint8_t *)a->array;
+ uint8_t *carray = (uint8_t *)c->array;
+
+ int32_t as = a->strides[ULAB_MAX_DIMS - 1] / a->itemsize;
+ int32_t cs = c->strides[ULAB_MAX_DIMS - 1] / c->itemsize;
+
+
+ #if ULAB_SUPPORTS_COMPLEX
+ if(dtype == NDARRAY_COMPLEX) {
+ mp_float_t a_real, a_imag;
+ mp_float_t c_real, c_imag = MICROPY_FLOAT_CONST(0.0);
+ for(int32_t k = -off; k < len-off; k++) {
+ mp_float_t accum_real = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t accum_imag = MICROPY_FLOAT_CONST(0.0);
+
+ int32_t top_n = MIN(len_c, len_a - k);
+ int32_t bot_n = MAX(-k, 0);
+
+ for(int32_t n = bot_n; n < top_n; n++) {
+ int32_t idx_c = (len_c - n - 1) * cs;
+ int32_t idx_a = (n + k) * as;
+ if(a->dtype != NDARRAY_COMPLEX) {
+ a_real = ndarray_get_float_index(aarray, a->dtype, idx_a);
+ a_imag = MICROPY_FLOAT_CONST(0.0);
+ } else {
+ a_real = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a);
+ a_imag = ndarray_get_float_index(aarray, NDARRAY_FLOAT, 2 * idx_a + 1);
+ }
+
+ if(c->dtype != NDARRAY_COMPLEX) {
+ c_real = ndarray_get_float_index(carray, c->dtype, idx_c);
+ c_imag = MICROPY_FLOAT_CONST(0.0);
+ } else {
+ c_real = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c);
+ c_imag = ndarray_get_float_index(carray, NDARRAY_FLOAT, 2 * idx_c + 1);
+ }
+ accum_real += a_real * c_real - a_imag * c_imag;
+ accum_imag += a_real * c_imag + a_imag * c_real;
+ }
+ *array++ = accum_real;
+ *array++ = accum_imag;
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+ }
+ #endif
+
+ for(int32_t k = -off; k < len-off; k++) {
+ mp_float_t accum = MICROPY_FLOAT_CONST(0.0);
+ int32_t top_n = MIN(len_c, len_a - k);
+ int32_t bot_n = MAX(-k, 0);
+ for(int32_t n = bot_n; n < top_n; n++) {
+ int32_t idx_c = (len_c - n - 1) * cs;
+ int32_t idx_a = (n + k) * as;
+ mp_float_t ai = ndarray_get_float_index(aarray, a->dtype, idx_a);
+ mp_float_t ci = ndarray_get_float_index(carray, c->dtype, idx_c);
+ accum += ai * ci;
+ }
+ *array++ = accum;
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(filter_convolve_obj, 2, filter_convolve);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/filter.h b/circuitpython/extmod/ulab/code/numpy/filter.h
new file mode 100644
index 0000000..d6d0f17
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/filter.h
@@ -0,0 +1,20 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2020-2021 Zoltán Vörös
+*/
+
+#ifndef _FILTER_
+#define _FILTER_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+MP_DECLARE_CONST_FUN_OBJ_KW(filter_convolve_obj);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/linalg/linalg.c b/circuitpython/extmod/ulab/code/numpy/linalg/linalg.c
new file mode 100644
index 0000000..11dc7de
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/linalg/linalg.c
@@ -0,0 +1,541 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Roberto Colistete Jr.
+ * 2020 Taku Fukada
+ *
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../../ulab.h"
+#include "../../ulab_tools.h"
+#include "../carray/carray_tools.h"
+#include "linalg.h"
+
+#if ULAB_NUMPY_HAS_LINALG_MODULE
+//|
+//| import ulab.numpy
+//|
+//| """Linear algebra functions"""
+//|
+
+#if ULAB_MAX_DIMS > 1
+//| def cholesky(A: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
+//| """
+//| :param ~ulab.numpy.ndarray A: a positive definite, symmetric square matrix
+//| :return ~ulab.numpy.ndarray L: a square root matrix in the lower triangular form
+//| :raises ValueError: If the input does not fulfill the necessary conditions
+//|
+//| The returned matrix satisfies the equation m=LL*"""
+//| ...
+//|
+
+static mp_obj_t linalg_cholesky(mp_obj_t oin) {
+ ndarray_obj_t *ndarray = tools_object_is_square(oin);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ ndarray_obj_t *L = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, ndarray->shape[ULAB_MAX_DIMS - 1], ndarray->shape[ULAB_MAX_DIMS - 1]), NDARRAY_FLOAT);
+ mp_float_t *Larray = (mp_float_t *)L->array;
+
+ size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
+ uint8_t *array = (uint8_t *)ndarray->array;
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+
+ for(size_t m=0; m < N; m++) { // rows
+ for(size_t n=0; n < N; n++) { // columns
+ *Larray++ = func(array);
+ array += ndarray->strides[ULAB_MAX_DIMS - 1];
+ }
+ array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
+ array += ndarray->strides[ULAB_MAX_DIMS - 2];
+ }
+ Larray -= N*N;
+ // make sure the matrix is symmetric
+ for(size_t m=0; m < N; m++) { // rows
+ for(size_t n=m+1; n < N; n++) { // columns
+ // compare entry (m, n) to (n, m)
+ if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(Larray[m * N + n] - Larray[n * N + m])) {
+ mp_raise_ValueError(translate("input matrix is asymmetric"));
+ }
+ }
+ }
+
+ // this is actually not needed, but Cholesky in numpy returns the lower triangular matrix
+ for(size_t i=0; i < N; i++) { // rows
+ for(size_t j=i+1; j < N; j++) { // columns
+ Larray[i*N + j] = MICROPY_FLOAT_CONST(0.0);
+ }
+ }
+ mp_float_t sum = 0.0;
+ for(size_t i=0; i < N; i++) { // rows
+ for(size_t j=0; j <= i; j++) { // columns
+ sum = Larray[i * N + j];
+ for(size_t k=0; k < j; k++) {
+ sum -= Larray[i * N + k] * Larray[j * N + k];
+ }
+ if(i == j) {
+ if(sum <= MICROPY_FLOAT_CONST(0.0)) {
+ mp_raise_ValueError(translate("matrix is not positive definite"));
+ } else {
+ Larray[i * N + i] = MICROPY_FLOAT_C_FUN(sqrt)(sum);
+ }
+ } else {
+ Larray[i * N + j] = sum / Larray[j * N + j];
+ }
+ }
+ }
+ return MP_OBJ_FROM_PTR(L);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(linalg_cholesky_obj, linalg_cholesky);
+
+//| def det(m: ulab.numpy.ndarray) -> float:
+//| """
+//| :param: m, a square matrix
+//| :return float: The determinant of the matrix
+//|
+//| Computes the eigenvalues and eigenvectors of a square matrix"""
+//| ...
+//|
+
+static mp_obj_t linalg_det(mp_obj_t oin) {
+ ndarray_obj_t *ndarray = tools_object_is_square(oin);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ uint8_t *array = (uint8_t *)ndarray->array;
+ size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
+ mp_float_t *tmp = m_new(mp_float_t, N * N);
+ for(size_t m=0; m < N; m++) { // rows
+ for(size_t n=0; n < N; n++) { // columns
+ *tmp++ = ndarray_get_float_value(array, ndarray->dtype);
+ array += ndarray->strides[ULAB_MAX_DIMS - 1];
+ }
+ array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
+ array += ndarray->strides[ULAB_MAX_DIMS - 2];
+ }
+
+ // re-wind the pointer
+ tmp -= N*N;
+
+ mp_float_t c;
+ mp_float_t det_sign = 1.0;
+
+ for(size_t m=0; m < N-1; m++){
+ if(MICROPY_FLOAT_C_FUN(fabs)(tmp[m * (N+1)]) < LINALG_EPSILON) {
+ size_t m1 = m + 1;
+ for(; m1 < N; m1++) {
+ if(!(MICROPY_FLOAT_C_FUN(fabs)(tmp[m1*N+m]) < LINALG_EPSILON)) {
+ //look for a line to swap
+ for(size_t m2=0; m2 < N; m2++) {
+ mp_float_t swapVal = tmp[m*N+m2];
+ tmp[m*N+m2] = tmp[m1*N+m2];
+ tmp[m1*N+m2] = swapVal;
+ }
+ det_sign = -det_sign;
+ break;
+ }
+ }
+ if (m1 >= N) {
+ m_del(mp_float_t, tmp, N * N);
+ return mp_obj_new_float(0.0);
+ }
+ }
+ for(size_t n=0; n < N; n++) {
+ if(m != n) {
+ c = tmp[N * n + m] / tmp[m * (N+1)];
+ for(size_t k=0; k < N; k++){
+ tmp[N * n + k] -= c * tmp[N * m + k];
+ }
+ }
+ }
+ }
+ mp_float_t det = det_sign;
+
+ for(size_t m=0; m < N; m++){
+ det *= tmp[m * (N+1)];
+ }
+ m_del(mp_float_t, tmp, N * N);
+ return mp_obj_new_float(det);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(linalg_det_obj, linalg_det);
+
+#endif
+
+#if ULAB_MAX_DIMS > 1
+//| def eig(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
+//| """
+//| :param m: a square matrix
+//| :return tuple (eigenvectors, eigenvalues):
+//|
+//| Computes the eigenvalues and eigenvectors of a square matrix"""
+//| ...
+//|
+
+static mp_obj_t linalg_eig(mp_obj_t oin) {
+ ndarray_obj_t *in = tools_object_is_square(oin);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(in->dtype)
+ uint8_t *iarray = (uint8_t *)in->array;
+ size_t S = in->shape[ULAB_MAX_DIMS - 1];
+ mp_float_t *array = m_new(mp_float_t, S*S);
+ for(size_t i=0; i < S; i++) { // rows
+ for(size_t j=0; j < S; j++) { // columns
+ *array++ = ndarray_get_float_value(iarray, in->dtype);
+ iarray += in->strides[ULAB_MAX_DIMS - 1];
+ }
+ iarray -= in->strides[ULAB_MAX_DIMS - 1] * S;
+ iarray += in->strides[ULAB_MAX_DIMS - 2];
+ }
+ array -= S * S;
+ // make sure the matrix is symmetric
+ for(size_t m=0; m < S; m++) {
+ for(size_t n=m+1; n < S; n++) {
+ // compare entry (m, n) to (n, m)
+ // TODO: this must probably be scaled!
+ if(LINALG_EPSILON < MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n] - array[n * S + m])) {
+ mp_raise_ValueError(translate("input matrix is asymmetric"));
+ }
+ }
+ }
+
+ // if we got this far, then the matrix will be symmetric
+
+ ndarray_obj_t *eigenvectors = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, S, S), NDARRAY_FLOAT);
+ mp_float_t *eigvectors = (mp_float_t *)eigenvectors->array;
+
+ size_t iterations = linalg_jacobi_rotations(array, eigvectors, S);
+
+ if(iterations == 0) {
+ // the computation did not converge; numpy raises LinAlgError
+ m_del(mp_float_t, array, in->len);
+ mp_raise_ValueError(translate("iterations did not converge"));
+ }
+ ndarray_obj_t *eigenvalues = ndarray_new_linear_array(S, NDARRAY_FLOAT);
+ mp_float_t *eigvalues = (mp_float_t *)eigenvalues->array;
+ for(size_t i=0; i < S; i++) {
+ eigvalues[i] = array[i * (S + 1)];
+ }
+ m_del(mp_float_t, array, in->len);
+
+ mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
+ tuple->items[0] = MP_OBJ_FROM_PTR(eigenvalues);
+ tuple->items[1] = MP_OBJ_FROM_PTR(eigenvectors);
+ return tuple;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(linalg_eig_obj, linalg_eig);
+
+//| def inv(m: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
+//| """
+//| :param ~ulab.numpy.ndarray m: a square matrix
+//| :return: The inverse of the matrix, if it exists
+//| :raises ValueError: if the matrix is not invertible
+//|
+//| Computes the inverse of a square matrix"""
+//| ...
+//|
+static mp_obj_t linalg_inv(mp_obj_t o_in) {
+ ndarray_obj_t *ndarray = tools_object_is_square(o_in);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ uint8_t *array = (uint8_t *)ndarray->array;
+ size_t N = ndarray->shape[ULAB_MAX_DIMS - 1];
+ ndarray_obj_t *inverted = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, N, N), NDARRAY_FLOAT);
+ mp_float_t *iarray = (mp_float_t *)inverted->array;
+
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+
+ for(size_t i=0; i < N; i++) { // rows
+ for(size_t j=0; j < N; j++) { // columns
+ *iarray++ = func(array);
+ array += ndarray->strides[ULAB_MAX_DIMS - 1];
+ }
+ array -= ndarray->strides[ULAB_MAX_DIMS - 1] * N;
+ array += ndarray->strides[ULAB_MAX_DIMS - 2];
+ }
+ // re-wind the pointer
+ iarray -= N*N;
+
+ if(!linalg_invert_matrix(iarray, N)) {
+ mp_raise_ValueError(translate("input matrix is singular"));
+ }
+ return MP_OBJ_FROM_PTR(inverted);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(linalg_inv_obj, linalg_inv);
+#endif
+
+//| def norm(x: ulab.numpy.ndarray) -> float:
+//| """
+//| :param ~ulab.numpy.ndarray x: a vector or a matrix
+//|
+//| Computes the 2-norm of a vector or a matrix, i.e., ``sqrt(sum(x*x))``, however, without the RAM overhead."""
+//| ...
+//|
+
+static mp_obj_t linalg_norm(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } ,
+ { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_obj_t x = args[0].u_obj;
+ mp_obj_t axis = args[1].u_obj;
+
+ mp_float_t dot = 0.0, value;
+ size_t count = 1;
+
+ if(mp_obj_is_type(x, &mp_type_tuple) || mp_obj_is_type(x, &mp_type_list) || mp_obj_is_type(x, &mp_type_range)) {
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t item, iterable = mp_getiter(x, &iter_buf);
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ value = mp_obj_get_float(item);
+ // we could simply take the sum of value ** 2,
+ // but this method is numerically stable
+ dot = dot + (value * value - dot) / count++;
+ }
+ return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1)));
+ } else if(mp_obj_is_type(x, &ulab_ndarray_type)) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(x);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ uint8_t *array = (uint8_t *)ndarray->array;
+ // always get a float, so that we don't have to resolve the dtype later
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+ shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
+ mp_float_t *rarray = (mp_float_t *)results->array;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ if(axis != mp_const_none) {
+ count = 1;
+ dot = 0.0;
+ }
+ do {
+ value = func(array);
+ dot = dot + (value * value - dot) / count++;
+ array += _shape_strides.strides[0];
+ l++;
+ } while(l < _shape_strides.shape[0]);
+ *rarray = MICROPY_FLOAT_C_FUN(sqrt)(dot * (count - 1));
+ #if ULAB_MAX_DIMS > 1
+ rarray += _shape_strides.increment;
+ array -= _shape_strides.strides[0] * _shape_strides.shape[0];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 1];
+ k++;
+ } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 2];
+ j++;
+ } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 3];
+ i++;
+ } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ if(results->ndim == 0) {
+ return mp_obj_new_float(*rarray);
+ }
+ return results;
+ }
+ return mp_const_none; // we should never reach this point
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(linalg_norm_obj, 1, linalg_norm);
+
+#if ULAB_MAX_DIMS > 1
+//| def qr(m: ulab.numpy.ndarray) -> Tuple[ulab.numpy.ndarray, ulab.numpy.ndarray]:
+//| """
+//| :param m: a matrix
+//| :return tuple (Q, R):
+//|
+//| Factor the matrix a as QR, where Q is orthonormal and R is upper-triangular.
+//| """
+//| ...
+//|
+
+static mp_obj_t linalg_qr(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_mode, MP_ARG_OBJ, { .u_rom_obj = MP_ROM_QSTR(MP_QSTR_reduced) } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("operation is defined for ndarrays only"));
+ }
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
+ if(source->ndim != 2) {
+ mp_raise_ValueError(translate("operation is defined for 2D arrays only"));
+ }
+
+ size_t m = source->shape[ULAB_MAX_DIMS - 2]; // rows
+ size_t n = source->shape[ULAB_MAX_DIMS - 1]; // columns
+
+ ndarray_obj_t *Q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m), NDARRAY_FLOAT);
+ ndarray_obj_t *R = ndarray_new_dense_ndarray(2, source->shape, NDARRAY_FLOAT);
+
+ mp_float_t *qarray = (mp_float_t *)Q->array;
+ mp_float_t *rarray = (mp_float_t *)R->array;
+
+ // simply copy the entries of source to a float array
+ mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
+ uint8_t *sarray = (uint8_t *)source->array;
+
+ for(size_t i = 0; i < m; i++) {
+ for(size_t j = 0; j < n; j++) {
+ *rarray++ = func(sarray);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ }
+ sarray -= n * source->strides[ULAB_MAX_DIMS - 1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ }
+ rarray -= m * n;
+
+ // start with the unit matrix
+ for(size_t i = 0; i < m; i++) {
+ qarray[i * (m + 1)] = 1.0;
+ }
+
+ for(size_t j = 0; j < n; j++) { // columns
+ for(size_t i = m - 1; i > j; i--) { // rows
+ mp_float_t c, s;
+ // Givens matrix: note that numpy uses a strange form of the rotation
+ // [[c s],
+ // [s -c]]
+ if(MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j]) < LINALG_EPSILON) { // r[i, j]
+ c = (rarray[(i - 1) * n + j] >= 0.0) ? 1.0 : -1.0; // r[i-1, j]
+ s = 0.0;
+ } else if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) < LINALG_EPSILON) { // r[i-1, j]
+ c = 0.0;
+ s = (rarray[i * n + j] >= 0.0) ? -1.0 : 1.0; // r[i, j]
+ } else {
+ mp_float_t t, u;
+ if(MICROPY_FLOAT_C_FUN(fabs)(rarray[(i - 1) * n + j]) > MICROPY_FLOAT_C_FUN(fabs)(rarray[i * n + j])) { // r[i-1, j], r[i, j]
+ t = rarray[i * n + j] / rarray[(i - 1) * n + j]; // r[i, j]/r[i-1, j]
+ u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
+ c = -1.0 / u;
+ s = c * t;
+ } else {
+ t = rarray[(i - 1) * n + j] / rarray[i * n + j]; // r[i-1, j]/r[i, j]
+ u = MICROPY_FLOAT_C_FUN(sqrt)(1 + t * t);
+ s = -1.0 / u;
+ c = s * t;
+ }
+ }
+
+ mp_float_t r1, r2;
+ // update R: multiply with the rotation matrix from the left
+ for(size_t k = 0; k < n; k++) {
+ r1 = rarray[(i - 1) * n + k]; // r[i-1, k]
+ r2 = rarray[i * n + k]; // r[i, k]
+ rarray[(i - 1) * n + k] = c * r1 + s * r2; // r[i-1, k]
+ rarray[i * n + k] = s * r1 - c * r2; // r[i, k]
+ }
+
+ // update Q: multiply with the transpose of the rotation matrix from the right
+ for(size_t k = 0; k < m; k++) {
+ r1 = qarray[k * m + (i - 1)];
+ r2 = qarray[k * m + i];
+ qarray[k * m + (i - 1)] = c * r1 + s * r2;
+ qarray[k * m + i] = s * r1 - c * r2;
+ }
+ }
+ }
+
+ mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
+ GET_STR_DATA_LEN(args[1].u_obj, mode, len);
+ if(memcmp(mode, "complete", 8) == 0) {
+ tuple->items[0] = MP_OBJ_FROM_PTR(Q);
+ tuple->items[1] = MP_OBJ_FROM_PTR(R);
+ } else if(memcmp(mode, "reduced", 7) == 0) {
+ size_t k = MAX(m, n) - MIN(m, n);
+ ndarray_obj_t *q = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m, m - k), NDARRAY_FLOAT);
+ ndarray_obj_t *r = ndarray_new_dense_ndarray(2, ndarray_shape_vector(0, 0, m - k, n), NDARRAY_FLOAT);
+ mp_float_t *qa = (mp_float_t *)q->array;
+ mp_float_t *ra = (mp_float_t *)r->array;
+ for(size_t i = 0; i < m; i++) {
+ memcpy(qa, qarray, (m - k) * q->itemsize);
+ qa += (m - k);
+ qarray += m;
+ }
+ for(size_t i = 0; i < m - k; i++) {
+ memcpy(ra, rarray, n * r->itemsize);
+ ra += n;
+ rarray += n;
+ }
+ tuple->items[0] = MP_OBJ_FROM_PTR(q);
+ tuple->items[1] = MP_OBJ_FROM_PTR(r);
+ } else {
+ mp_raise_ValueError(translate("mode must be complete, or reduced"));
+ }
+ return tuple;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(linalg_qr_obj, 1, linalg_qr);
+#endif
+
+STATIC const mp_rom_map_elem_t ulab_linalg_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_linalg) },
+ #if ULAB_MAX_DIMS > 1
+ #if ULAB_LINALG_HAS_CHOLESKY
+ { MP_ROM_QSTR(MP_QSTR_cholesky), (mp_obj_t)&linalg_cholesky_obj },
+ #endif
+ #if ULAB_LINALG_HAS_DET
+ { MP_ROM_QSTR(MP_QSTR_det), (mp_obj_t)&linalg_det_obj },
+ #endif
+ #if ULAB_LINALG_HAS_EIG
+ { MP_ROM_QSTR(MP_QSTR_eig), (mp_obj_t)&linalg_eig_obj },
+ #endif
+ #if ULAB_LINALG_HAS_INV
+ { MP_ROM_QSTR(MP_QSTR_inv), (mp_obj_t)&linalg_inv_obj },
+ #endif
+ #if ULAB_LINALG_HAS_QR
+ { MP_ROM_QSTR(MP_QSTR_qr), (mp_obj_t)&linalg_qr_obj },
+ #endif
+ #endif
+ #if ULAB_LINALG_HAS_NORM
+ { MP_ROM_QSTR(MP_QSTR_norm), (mp_obj_t)&linalg_norm_obj },
+ #endif
+};
+
+STATIC MP_DEFINE_CONST_DICT(mp_module_ulab_linalg_globals, ulab_linalg_globals_table);
+
+const mp_obj_module_t ulab_linalg_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_ulab_linalg_globals,
+};
+MP_REGISTER_MODULE(MP_QSTR_ulab_dot_linalg, ulab_linalg_module, MODULE_ULAB_ENABLED && CIRCUITPY_ULAB);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/linalg/linalg.h b/circuitpython/extmod/ulab/code/numpy/linalg/linalg.h
new file mode 100644
index 0000000..35fc403
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/linalg/linalg.h
@@ -0,0 +1,27 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _LINALG_
+#define _LINALG_
+
+#include "../../ulab.h"
+#include "../../ndarray.h"
+#include "linalg_tools.h"
+
+extern const mp_obj_module_t ulab_linalg_module;
+
+MP_DECLARE_CONST_FUN_OBJ_1(linalg_cholesky_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(linalg_det_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(linalg_eig_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(linalg_inv_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(linalg_norm_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(linalg_qr_obj);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.c b/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.c
new file mode 100644
index 0000000..5e03a50
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.c
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2010 Zoltán Vörös
+*/
+
+#include <math.h>
+#include <string.h>
+#include "py/runtime.h"
+
+#include "linalg_tools.h"
+
+/*
+ * The following function inverts a matrix, whose entries are given in the input array
+ * The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
+ * and can be used independent of ulab.
+ */
+
+bool linalg_invert_matrix(mp_float_t *data, size_t N) {
+ // returns true, of the inversion was successful,
+ // false, if the matrix is singular
+
+ // initially, this is the unit matrix: the contents of this matrix is what
+ // will be returned after all the transformations
+ mp_float_t *unit = m_new(mp_float_t, N*N);
+ mp_float_t elem = 1.0;
+ // initialise the unit matrix
+ memset(unit, 0, sizeof(mp_float_t)*N*N);
+ for(size_t m=0; m < N; m++) {
+ memcpy(&unit[m * (N+1)], &elem, sizeof(mp_float_t));
+ }
+ for(size_t m=0; m < N; m++){
+ // this could be faster with ((c < epsilon) && (c > -epsilon))
+ if(MICROPY_FLOAT_C_FUN(fabs)(data[m * (N+1)]) < LINALG_EPSILON) {
+ //look for a line to swap
+ size_t m1 = m + 1;
+ for(; m1 < N; m1++) {
+ if(!(MICROPY_FLOAT_C_FUN(fabs)(data[m1*N + m]) < LINALG_EPSILON)) {
+ for(size_t m2=0; m2 < N; m2++) {
+ mp_float_t swapVal = data[m*N+m2];
+ data[m*N+m2] = data[m1*N+m2];
+ data[m1*N+m2] = swapVal;
+ swapVal = unit[m*N+m2];
+ unit[m*N+m2] = unit[m1*N+m2];
+ unit[m1*N+m2] = swapVal;
+ }
+ break;
+ }
+ }
+ if (m1 >= N) {
+ m_del(mp_float_t, unit, N*N);
+ return false;
+ }
+ }
+ for(size_t n=0; n < N; n++) {
+ if(m != n){
+ elem = data[N * n + m] / data[m * (N+1)];
+ for(size_t k=0; k < N; k++) {
+ data[N * n + k] -= elem * data[N * m + k];
+ unit[N * n + k] -= elem * unit[N * m + k];
+ }
+ }
+ }
+ }
+ for(size_t m=0; m < N; m++) {
+ elem = data[m * (N+1)];
+ for(size_t n=0; n < N; n++) {
+ data[N * m + n] /= elem;
+ unit[N * m + n] /= elem;
+ }
+ }
+ memcpy(data, unit, sizeof(mp_float_t)*N*N);
+ m_del(mp_float_t, unit, N * N);
+ return true;
+}
+
+/*
+ * The following function calculates the eigenvalues and eigenvectors of a symmetric
+ * real matrix, whose entries are given in the input array.
+ * The function has no dependencies beyond micropython itself (for the definition of mp_float_t),
+ * and can be used independent of ulab.
+ */
+
+size_t linalg_jacobi_rotations(mp_float_t *array, mp_float_t *eigvectors, size_t S) {
+ // eigvectors should be a 0-array; start out with the unit matrix
+ for(size_t m=0; m < S; m++) {
+ eigvectors[m * (S+1)] = 1.0;
+ }
+ mp_float_t largest, w, t, c, s, tau, aMk, aNk, vm, vn;
+ size_t M, N;
+ size_t iterations = JACOBI_MAX * S * S;
+ do {
+ iterations--;
+ // find the pivot here
+ M = 0;
+ N = 0;
+ largest = 0.0;
+ for(size_t m=0; m < S-1; m++) { // -1: no need to inspect last row
+ for(size_t n=m+1; n < S; n++) {
+ w = MICROPY_FLOAT_C_FUN(fabs)(array[m * S + n]);
+ if((largest < w) && (LINALG_EPSILON < w)) {
+ M = m;
+ N = n;
+ largest = w;
+ }
+ }
+ }
+ if(M + N == 0) { // all entries are smaller than epsilon, there is not much we can do...
+ break;
+ }
+ // at this point, we have the pivot, and it is the entry (M, N)
+ // now we have to find the rotation angle
+ w = (array[N * S + N] - array[M * S + M]) / (MICROPY_FLOAT_CONST(2.0)*array[M * S + N]);
+ // The following if/else chooses the smaller absolute value for the tangent
+ // of the rotation angle. Going with the smaller should be numerically stabler.
+ if(w > 0) {
+ t = MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) - w;
+ } else {
+ t = MICROPY_FLOAT_CONST(-1.0)*(MICROPY_FLOAT_C_FUN(sqrt)(w*w + MICROPY_FLOAT_CONST(1.0)) + w);
+ }
+ s = t / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the sine of the rotation angle
+ c = MICROPY_FLOAT_CONST(1.0) / MICROPY_FLOAT_C_FUN(sqrt)(t*t + MICROPY_FLOAT_CONST(1.0)); // the cosine of the rotation angle
+ tau = (MICROPY_FLOAT_CONST(1.0)-c)/s; // this is equal to the tangent of the half of the rotation angle
+
+ // at this point, we have the rotation angles, so we can transform the matrix
+ // first the two diagonal elements
+ // a(M, M) = a(M, M) - t*a(M, N)
+ array[M * S + M] = array[M * S + M] - t * array[M * S + N];
+ // a(N, N) = a(N, N) + t*a(M, N)
+ array[N * S + N] = array[N * S + N] + t * array[M * S + N];
+ // after the rotation, the a(M, N), and a(N, M) entries should become zero
+ array[M * S + N] = array[N * S + M] = MICROPY_FLOAT_CONST(0.0);
+ // then all other elements in the column
+ for(size_t k=0; k < S; k++) {
+ if((k == M) || (k == N)) {
+ continue;
+ }
+ aMk = array[M * S + k];
+ aNk = array[N * S + k];
+ // a(M, k) = a(M, k) - s*(a(N, k) + tau*a(M, k))
+ array[M * S + k] -= s * (aNk + tau * aMk);
+ // a(N, k) = a(N, k) + s*(a(M, k) - tau*a(N, k))
+ array[N * S + k] += s * (aMk - tau * aNk);
+ // a(k, M) = a(M, k)
+ array[k * S + M] = array[M * S + k];
+ // a(k, N) = a(N, k)
+ array[k * S + N] = array[N * S + k];
+ }
+ // now we have to update the eigenvectors
+ // the rotation matrix, R, multiplies from the right
+ // R is the unit matrix, except for the
+ // R(M,M) = R(N, N) = c
+ // R(N, M) = s
+ // (M, N) = -s
+ // entries. This means that only the Mth, and Nth columns will change
+ for(size_t m=0; m < S; m++) {
+ vm = eigvectors[m * S + M];
+ vn = eigvectors[m * S + N];
+ // the new value of eigvectors(m, M)
+ eigvectors[m * S + M] = c * vm - s * vn;
+ // the new value of eigvectors(m, N)
+ eigvectors[m * S + N] = s * vm + c * vn;
+ }
+ } while(iterations > 0);
+
+ return iterations;
+}
diff --git a/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.h b/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.h
new file mode 100644
index 0000000..942da00
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/linalg/linalg_tools.h
@@ -0,0 +1,28 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _TOOLS_TOOLS_
+#define _TOOLS_TOOLS_
+
+#ifndef LINALG_EPSILON
+#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
+#define LINALG_EPSILON MICROPY_FLOAT_CONST(1.2e-7)
+#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
+#define LINALG_EPSILON MICROPY_FLOAT_CONST(2.3e-16)
+#endif
+#endif /* LINALG_EPSILON */
+
+#define JACOBI_MAX 20
+
+bool linalg_invert_matrix(mp_float_t *, size_t );
+size_t linalg_jacobi_rotations(mp_float_t *, mp_float_t *, size_t );
+
+#endif /* _TOOLS_TOOLS_ */
+
diff --git a/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.c b/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.c
new file mode 100644
index 0000000..8704836
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.c
@@ -0,0 +1,66 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021 Zoltán Vörös
+ *
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+
+#include "ndarray_iter.h"
+
+#ifdef NDARRAY_HAS_FLATITER
+mp_obj_t ndarray_flatiter_make_new(mp_obj_t self_in) {
+ ndarray_flatiter_t *flatiter = m_new_obj(ndarray_flatiter_t);
+ flatiter->base.type = &ndarray_flatiter_type;
+ flatiter->iternext = ndarray_flatiter_next;
+ flatiter->ndarray = MP_OBJ_TO_PTR(self_in);
+ flatiter->cur = 0;
+ return flatiter;
+}
+
+mp_obj_t ndarray_flatiter_next(mp_obj_t self_in) {
+ ndarray_flatiter_t *self = MP_OBJ_TO_PTR(self_in);
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(self->ndarray);
+ uint8_t *array = (uint8_t *)ndarray->array;
+
+ if(self->cur < ndarray->len) {
+ uint32_t remainder = self->cur;
+ uint8_t i = ULAB_MAX_DIMS - 1;
+ do {
+ size_t div = (remainder / ndarray->shape[i]);
+ array += remainder * ndarray->strides[i];
+ remainder -= div * ndarray->shape[i];
+ i--;
+ } while(i > ULAB_MAX_DIMS - ndarray->ndim);
+ self->cur++;
+ return ndarray_get_item(ndarray, array);
+ }
+ return MP_OBJ_STOP_ITERATION;
+}
+
+mp_obj_t ndarray_new_flatiterator(mp_obj_t flatiter_in, mp_obj_iter_buf_t *iter_buf) {
+ assert(sizeof(ndarray_flatiter_t) <= sizeof(mp_obj_iter_buf_t));
+ ndarray_flatiter_t *iter = (ndarray_flatiter_t *)iter_buf;
+ ndarray_flatiter_t *flatiter = MP_OBJ_TO_PTR(flatiter_in);
+ iter->base.type = &mp_type_polymorph_iter;
+ iter->iternext = ndarray_flatiter_next;
+ iter->ndarray = flatiter->ndarray;
+ iter->cur = 0;
+ return MP_OBJ_FROM_PTR(iter);
+}
+
+mp_obj_t ndarray_get_flatiterator(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) {
+ return ndarray_new_flatiterator(o_in, iter_buf);
+}
+#endif /* NDARRAY_HAS_FLATITER */
diff --git a/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.h b/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.h
new file mode 100644
index 0000000..b3fc48d
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/ndarray/ndarray_iter.h
@@ -0,0 +1,36 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2020-2021 Zoltán Vörös
+*/
+
+#ifndef _NDARRAY_ITER_
+#define _NDARRAY_ITER_
+
+#include "py/runtime.h"
+#include "py/binary.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+
+#include "../../ulab.h"
+#include "../../ndarray.h"
+
+// TODO: take simply mp_obj_ndarray_it_t from ndarray.c
+typedef struct _mp_obj_ndarray_flatiter_t {
+ mp_obj_base_t base;
+ mp_fun_1_t iternext;
+ mp_obj_t ndarray;
+ size_t cur;
+} ndarray_flatiter_t;
+
+mp_obj_t ndarray_get_flatiterator(mp_obj_t , mp_obj_iter_buf_t *);
+mp_obj_t ndarray_flatiter_make_new(mp_obj_t );
+mp_obj_t ndarray_flatiter_next(mp_obj_t );
+
+#endif \ No newline at end of file
diff --git a/circuitpython/extmod/ulab/code/numpy/numerical.c b/circuitpython/extmod/ulab/code/numpy/numerical.c
new file mode 100644
index 0000000..d6983c0
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/numerical.c
@@ -0,0 +1,1402 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "py/obj.h"
+#include "py/objint.h"
+#include "py/runtime.h"
+#include "py/builtin.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "./carray/carray_tools.h"
+#include "numerical.h"
+
+enum NUMERICAL_FUNCTION_TYPE {
+ NUMERICAL_ALL,
+ NUMERICAL_ANY,
+ NUMERICAL_ARGMAX,
+ NUMERICAL_ARGMIN,
+ NUMERICAL_MAX,
+ NUMERICAL_MEAN,
+ NUMERICAL_MIN,
+ NUMERICAL_STD,
+ NUMERICAL_SUM,
+};
+
+//| """Numerical and Statistical functions
+//|
+//| Most of these functions take an "axis" argument, which indicates whether to
+//| operate over the flattened array (None), or a particular axis (integer)."""
+//|
+//| from typing import Dict
+//|
+//| _ArrayLike = Union[ndarray, List[_float], Tuple[_float], range]
+//|
+//| _DType = int
+//| """`ulab.numpy.int8`, `ulab.numpy.uint8`, `ulab.numpy.int16`, `ulab.numpy.uint16`, `ulab.numpy.float` or `ulab.numpy.bool`"""
+//|
+//| from builtins import float as _float
+//| from builtins import bool as _bool
+//|
+//| int8: _DType
+//| """Type code for signed integers in the range -128 .. 127 inclusive, like the 'b' typecode of `array.array`"""
+//|
+//| int16: _DType
+//| """Type code for signed integers in the range -32768 .. 32767 inclusive, like the 'h' typecode of `array.array`"""
+//|
+//| float: _DType
+//| """Type code for floating point values, like the 'f' typecode of `array.array`"""
+//|
+//| uint8: _DType
+//| """Type code for unsigned integers in the range 0 .. 255 inclusive, like the 'H' typecode of `array.array`"""
+//|
+//| uint16: _DType
+//| """Type code for unsigned integers in the range 0 .. 65535 inclusive, like the 'h' typecode of `array.array`"""
+//|
+//| bool: _DType
+//| """Type code for boolean values"""
+//|
+
+static void numerical_reduce_axes(ndarray_obj_t *ndarray, int8_t axis, size_t *shape, int32_t *strides) {
+ // removes the values corresponding to a single axis from the shape and strides array
+ uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + axis;
+ if((ndarray->ndim == 1) && (axis == 0)) {
+ index = 0;
+ shape[ULAB_MAX_DIMS - 1] = 1;
+ return;
+ }
+ for(uint8_t i = ULAB_MAX_DIMS - 1; i > 0; i--) {
+ if(i > index) {
+ shape[i] = ndarray->shape[i];
+ strides[i] = ndarray->strides[i];
+ } else {
+ shape[i] = ndarray->shape[i-1];
+ strides[i] = ndarray->strides[i-1];
+ }
+ }
+}
+
+#if ULAB_NUMPY_HAS_ALL | ULAB_NUMPY_HAS_ANY
+static mp_obj_t numerical_all_any(mp_obj_t oin, mp_obj_t axis, uint8_t optype) {
+ bool anytype = optype == NUMERICAL_ALL ? 1 : 0;
+ if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
+ uint8_t *array = (uint8_t *)ndarray->array;
+ if(ndarray->len == 0) { // return immediately with empty arrays
+ if(optype == NUMERICAL_ALL) {
+ return mp_const_true;
+ } else {
+ return mp_const_false;
+ }
+ }
+ // always get a float, so that we don't have to resolve the dtype later
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+ ndarray_obj_t *results = NULL;
+ uint8_t *rarray = NULL;
+ shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
+ if(axis != mp_const_none) {
+ results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_BOOL);
+ rarray = results->array;
+ if(optype == NUMERICAL_ALL) {
+ memset(rarray, 1, results->len);
+ }
+ }
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ if(axis == mp_const_none) {
+ do {
+ #if ULAB_SUPPORTS_COMPLEX
+ if(ndarray->dtype == NDARRAY_COMPLEX) {
+ mp_float_t real = *((mp_float_t *)array);
+ mp_float_t imag = *((mp_float_t *)(array + sizeof(mp_float_t)));
+ if(((real != MICROPY_FLOAT_CONST(0.0)) | (imag != MICROPY_FLOAT_CONST(0.0))) & !anytype) {
+ // optype = NUMERICAL_ANY
+ return mp_const_true;
+ } else if(((real == MICROPY_FLOAT_CONST(0.0)) & (imag == MICROPY_FLOAT_CONST(0.0))) & anytype) {
+ // optype == NUMERICAL_ALL
+ return mp_const_false;
+ }
+ } else {
+ #endif
+ mp_float_t value = func(array);
+ if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) {
+ // optype = NUMERICAL_ANY
+ return mp_const_true;
+ } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) {
+ // optype == NUMERICAL_ALL
+ return mp_const_false;
+ }
+ #if ULAB_SUPPORTS_COMPLEX
+ }
+ #endif
+ array += _shape_strides.strides[0];
+ l++;
+ } while(l < _shape_strides.shape[0]);
+ } else { // a scalar axis keyword was supplied
+ do {
+ #if ULAB_SUPPORTS_COMPLEX
+ if(ndarray->dtype == NDARRAY_COMPLEX) {
+ mp_float_t real = *((mp_float_t *)array);
+ mp_float_t imag = *((mp_float_t *)(array + sizeof(mp_float_t)));
+ if(((real != MICROPY_FLOAT_CONST(0.0)) | (imag != MICROPY_FLOAT_CONST(0.0))) & !anytype) {
+ // optype = NUMERICAL_ANY
+ *rarray = 1;
+ // since we are breaking out of the loop, move the pointer forward
+ array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l);
+ break;
+ } else if(((real == MICROPY_FLOAT_CONST(0.0)) & (imag == MICROPY_FLOAT_CONST(0.0))) & anytype) {
+ // optype == NUMERICAL_ALL
+ *rarray = 0;
+ // since we are breaking out of the loop, move the pointer forward
+ array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l);
+ break;
+ }
+ } else {
+ #endif
+ mp_float_t value = func(array);
+ if((value != MICROPY_FLOAT_CONST(0.0)) & !anytype) {
+ // optype == NUMERICAL_ANY
+ *rarray = 1;
+ // since we are breaking out of the loop, move the pointer forward
+ array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l);
+ break;
+ } else if((value == MICROPY_FLOAT_CONST(0.0)) & anytype) {
+ // optype == NUMERICAL_ALL
+ *rarray = 0;
+ // since we are breaking out of the loop, move the pointer forward
+ array += _shape_strides.strides[0] * (_shape_strides.shape[0] - l);
+ break;
+ }
+ #if ULAB_SUPPORTS_COMPLEX
+ }
+ #endif
+ array += _shape_strides.strides[0];
+ l++;
+ } while(l < _shape_strides.shape[0]);
+ }
+ #if ULAB_MAX_DIMS > 1
+ rarray += _shape_strides.increment;
+ array -= _shape_strides.strides[0] * _shape_strides.shape[0];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 1];
+ k++;
+ } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 1]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 2];
+ j++;
+ } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 3];
+ i++;
+ } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ if(axis == mp_const_none) {
+ // the innermost loop fell through, so return the result here
+ if(!anytype) {
+ return mp_const_false;
+ } else {
+ return mp_const_true;
+ }
+ }
+ return results;
+ } else if(mp_obj_is_int(oin) || mp_obj_is_float(oin)) {
+ return mp_obj_is_true(oin) ? mp_const_true : mp_const_false;
+ } else {
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ if(!mp_obj_is_true(item) & !anytype) {
+ return mp_const_false;
+ } else if(mp_obj_is_true(item) & anytype) {
+ return mp_const_true;
+ }
+ }
+ }
+ return anytype ? mp_const_true : mp_const_false;
+}
+#endif
+
+#if ULAB_NUMPY_HAS_SUM | ULAB_NUMPY_HAS_MEAN | ULAB_NUMPY_HAS_STD
+static mp_obj_t numerical_sum_mean_std_iterable(mp_obj_t oin, uint8_t optype, size_t ddof) {
+ mp_float_t value = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t M = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t m = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t S = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t s = MICROPY_FLOAT_CONST(0.0);
+ size_t count = 0;
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t item, iterable = mp_getiter(oin, &iter_buf);
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ value = mp_obj_get_float(item);
+ m = M + (value - M) / (count + 1);
+ s = S + (value - M) * (value - m);
+ M = m;
+ S = s;
+ count++;
+ }
+ if(optype == NUMERICAL_SUM) {
+ return mp_obj_new_float(m * count);
+ } else if(optype == NUMERICAL_MEAN) {
+ return count > 0 ? mp_obj_new_float(m) : mp_obj_new_float(MICROPY_FLOAT_CONST(0.0));
+ } else { // this should be the case of the standard deviation
+ return count > ddof ? mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(s / (count - ddof))) : mp_obj_new_float(MICROPY_FLOAT_CONST(0.0));
+ }
+}
+
+static mp_obj_t numerical_sum_mean_std_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype, size_t ddof) {
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ uint8_t *array = (uint8_t *)ndarray->array;
+ shape_strides _shape_strides = tools_reduce_axes(ndarray, axis);
+
+ if(axis == mp_const_none) {
+ // work with the flattened array
+ if((optype == NUMERICAL_STD) && (ddof > ndarray->len)) {
+ // if there are too many degrees of freedom, there is no point in calculating anything
+ return mp_obj_new_float(MICROPY_FLOAT_CONST(0.0));
+ }
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+ mp_float_t M = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t m = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t S = MICROPY_FLOAT_CONST(0.0);
+ mp_float_t s = MICROPY_FLOAT_CONST(0.0);
+ size_t count = 0;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ count++;
+ mp_float_t value = func(array);
+ m = M + (value - M) / (mp_float_t)count;
+ if(optype == NUMERICAL_STD) {
+ s = S + (value - M) * (value - m);
+ S = s;
+ }
+ M = m;
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < _shape_strides.shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 1] * _shape_strides.shape[ULAB_MAX_DIMS - 1];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < _shape_strides.shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 2] * _shape_strides.shape[ULAB_MAX_DIMS - 2];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < _shape_strides.shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= _shape_strides.strides[ULAB_MAX_DIMS - 3] * _shape_strides.shape[ULAB_MAX_DIMS - 3];
+ array += _shape_strides.strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < _shape_strides.shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ if(optype == NUMERICAL_SUM) {
+ // numpy returns an integer for integer input types
+ if(ndarray->dtype == NDARRAY_FLOAT) {
+ return mp_obj_new_float(M * ndarray->len);
+ } else {
+ return mp_obj_new_int((int32_t)MICROPY_FLOAT_C_FUN(round)(M * ndarray->len));
+ }
+ } else if(optype == NUMERICAL_MEAN) {
+ return mp_obj_new_float(M);
+ } else { // this must be the case of the standard deviation
+ // we have already made certain that ddof < ndarray->len holds
+ return mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(S / (ndarray->len - ddof)));
+ }
+ } else {
+ ndarray_obj_t *results = NULL;
+ uint8_t *rarray = NULL;
+ mp_float_t *farray = NULL;
+ if(optype == NUMERICAL_SUM) {
+ results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, ndarray->dtype);
+ rarray = (uint8_t *)results->array;
+ // TODO: numpy promotes the output to the highest integer type
+ if(ndarray->dtype == NDARRAY_UINT8) {
+ RUN_SUM(uint8_t, array, results, rarray, _shape_strides);
+ } else if(ndarray->dtype == NDARRAY_INT8) {
+ RUN_SUM(int8_t, array, results, rarray, _shape_strides);
+ } else if(ndarray->dtype == NDARRAY_UINT16) {
+ RUN_SUM(uint16_t, array, results, rarray, _shape_strides);
+ } else if(ndarray->dtype == NDARRAY_INT16) {
+ RUN_SUM(int16_t, array, results, rarray, _shape_strides);
+ } else {
+ // for floats, the sum might be inaccurate with the naive summation
+ // call mean, and multiply with the number of samples
+ farray = (mp_float_t *)results->array;
+ RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, MICROPY_FLOAT_CONST(0.0), 0);
+ mp_float_t norm = (mp_float_t)_shape_strides.shape[0];
+ // re-wind the array here
+ farray = (mp_float_t *)results->array;
+ for(size_t i=0; i < results->len; i++) {
+ *farray++ *= norm;
+ }
+ }
+ } else {
+ bool isStd = optype == NUMERICAL_STD ? 1 : 0;
+ results = ndarray_new_dense_ndarray(_shape_strides.ndim, _shape_strides.shape, NDARRAY_FLOAT);
+ farray = (mp_float_t *)results->array;
+ // we can return the 0 array here, if the degrees of freedom is larger than the length of the axis
+ if((optype == NUMERICAL_STD) && (_shape_strides.shape[0] <= ddof)) {
+ return MP_OBJ_FROM_PTR(results);
+ }
+ mp_float_t div = optype == NUMERICAL_STD ? (mp_float_t)(_shape_strides.shape[0] - ddof) : MICROPY_FLOAT_CONST(0.0);
+ if(ndarray->dtype == NDARRAY_UINT8) {
+ RUN_MEAN_STD(uint8_t, array, farray, _shape_strides, div, isStd);
+ } else if(ndarray->dtype == NDARRAY_INT8) {
+ RUN_MEAN_STD(int8_t, array, farray, _shape_strides, div, isStd);
+ } else if(ndarray->dtype == NDARRAY_UINT16) {
+ RUN_MEAN_STD(uint16_t, array, farray, _shape_strides, div, isStd);
+ } else if(ndarray->dtype == NDARRAY_INT16) {
+ RUN_MEAN_STD(int16_t, array, farray, _shape_strides, div, isStd);
+ } else {
+ RUN_MEAN_STD(mp_float_t, array, farray, _shape_strides, div, isStd);
+ }
+ }
+ if(results->ndim == 0) { // return a scalar here
+ return mp_binary_get_val_array(results->dtype, results->array, 0);
+ }
+ return MP_OBJ_FROM_PTR(results);
+ }
+ return mp_const_none;
+}
+#endif
+
+#if ULAB_NUMPY_HAS_ARGMINMAX
+static mp_obj_t numerical_argmin_argmax_iterable(mp_obj_t oin, uint8_t optype) {
+ if(MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(oin)) == 0) {
+ mp_raise_ValueError(translate("attempt to get argmin/argmax of an empty sequence"));
+ }
+ size_t idx = 0, best_idx = 0;
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(oin, &iter_buf);
+ mp_obj_t item;
+ uint8_t op = 0; // argmin, min
+ if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) op = 1;
+ item = mp_iternext(iterable);
+ mp_obj_t best_obj = item;
+ mp_float_t value, best_value = mp_obj_get_float(item);
+ value = best_value;
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ idx++;
+ value = mp_obj_get_float(item);
+ if((op == 0) && (value < best_value)) {
+ best_obj = item;
+ best_idx = idx;
+ best_value = value;
+ } else if((op == 1) && (value > best_value)) {
+ best_obj = item;
+ best_idx = idx;
+ best_value = value;
+ }
+ }
+ if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
+ return MP_OBJ_NEW_SMALL_INT(best_idx);
+ } else {
+ return best_obj;
+ }
+}
+
+static mp_obj_t numerical_argmin_argmax_ndarray(ndarray_obj_t *ndarray, mp_obj_t axis, uint8_t optype) {
+ // TODO: treat the flattened array
+ if(ndarray->len == 0) {
+ mp_raise_ValueError(translate("attempt to get (arg)min/(arg)max of empty sequence"));
+ }
+
+ if(axis == mp_const_none) {
+ // work with the flattened array
+ mp_float_t (*func)(void *) = ndarray_get_float_function(ndarray->dtype);
+ uint8_t *array = (uint8_t *)ndarray->array;
+ mp_float_t best_value = func(array);
+ mp_float_t value;
+ size_t index = 0, best_index = 0;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ value = func(array);
+ if((optype == NUMERICAL_ARGMAX) || (optype == NUMERICAL_MAX)) {
+ if(best_value < value) {
+ best_value = value;
+ best_index = index;
+ }
+ } else {
+ if(best_value > value) {
+ best_value = value;
+ best_index = index;
+ }
+ }
+ array += ndarray->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ index++;
+ } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
+ array += ndarray->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
+ array += ndarray->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
+ array += ndarray->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+
+ if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
+ return mp_obj_new_int(best_index);
+ } else {
+ if(ndarray->dtype == NDARRAY_FLOAT) {
+ return mp_obj_new_float(best_value);
+ } else {
+ return MP_OBJ_NEW_SMALL_INT((int32_t)best_value);
+ }
+ }
+ } else {
+ int8_t ax = tools_get_axis(axis, ndarray->ndim);
+
+ uint8_t *array = (uint8_t *)ndarray->array;
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+ uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax;
+
+ ndarray_obj_t *results = NULL;
+
+ if((optype == NUMERICAL_ARGMIN) || (optype == NUMERICAL_ARGMAX)) {
+ results = ndarray_new_dense_ndarray(MAX(1, ndarray->ndim-1), shape, NDARRAY_INT16);
+ } else {
+ results = ndarray_new_dense_ndarray(MAX(1, ndarray->ndim-1), shape, ndarray->dtype);
+ }
+
+ uint8_t *rarray = (uint8_t *)results->array;
+
+ if(ndarray->dtype == NDARRAY_UINT8) {
+ RUN_ARGMIN(ndarray, uint8_t, array, results, rarray, shape, strides, index, optype);
+ } else if(ndarray->dtype == NDARRAY_INT8) {
+ RUN_ARGMIN(ndarray, int8_t, array, results, rarray, shape, strides, index, optype);
+ } else if(ndarray->dtype == NDARRAY_UINT16) {
+ RUN_ARGMIN(ndarray, uint16_t, array, results, rarray, shape, strides, index, optype);
+ } else if(ndarray->dtype == NDARRAY_INT16) {
+ RUN_ARGMIN(ndarray, int16_t, array, results, rarray, shape, strides, index, optype);
+ } else {
+ RUN_ARGMIN(ndarray, mp_float_t, array, results, rarray, shape, strides, index, optype);
+ }
+ if(results->len == 1) {
+ return mp_binary_get_val_array(results->dtype, results->array, 0);
+ }
+ return MP_OBJ_FROM_PTR(results);
+ }
+ return mp_const_none;
+}
+#endif
+
+static mp_obj_t numerical_function(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, uint8_t optype) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none} } ,
+ { MP_QSTR_axis, MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_obj_t oin = args[0].u_obj;
+ mp_obj_t axis = args[1].u_obj;
+ if((axis != mp_const_none) && (!mp_obj_is_int(axis))) {
+ mp_raise_TypeError(translate("axis must be None, or an integer"));
+ }
+
+ if((optype == NUMERICAL_ALL) || (optype == NUMERICAL_ANY)) {
+ return numerical_all_any(oin, axis, optype);
+ }
+ if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) ||
+ mp_obj_is_type(oin, &mp_type_range)) {
+ switch(optype) {
+ case NUMERICAL_MIN:
+ case NUMERICAL_ARGMIN:
+ case NUMERICAL_MAX:
+ case NUMERICAL_ARGMAX:
+ return numerical_argmin_argmax_iterable(oin, optype);
+ case NUMERICAL_SUM:
+ case NUMERICAL_MEAN:
+ return numerical_sum_mean_std_iterable(oin, optype, 0);
+ default: // we should never reach this point, but whatever
+ return mp_const_none;
+ }
+ } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
+ switch(optype) {
+ case NUMERICAL_MIN:
+ case NUMERICAL_MAX:
+ case NUMERICAL_ARGMIN:
+ case NUMERICAL_ARGMAX:
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ return numerical_argmin_argmax_ndarray(ndarray, axis, optype);
+ case NUMERICAL_SUM:
+ case NUMERICAL_MEAN:
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ return numerical_sum_mean_std_ndarray(ndarray, axis, optype, 0);
+ default:
+ mp_raise_NotImplementedError(translate("operation is not implemented on ndarrays"));
+ }
+ } else {
+ mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
+ }
+ return mp_const_none;
+}
+
+#if ULAB_NUMPY_HAS_SORT | NDARRAY_HAS_SORT
+static mp_obj_t numerical_sort_helper(mp_obj_t oin, mp_obj_t axis, uint8_t inplace) {
+ if(!mp_obj_is_type(oin, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("sort argument must be an ndarray"));
+ }
+
+ ndarray_obj_t *ndarray;
+ if(inplace == 1) {
+ ndarray = MP_OBJ_TO_PTR(oin);
+ } else {
+ ndarray = ndarray_copy_view(MP_OBJ_TO_PTR(oin));
+ }
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+
+ int8_t ax = 0;
+ if(axis == mp_const_none) {
+ // flatten the array
+ #if ULAB_MAX_DIMS > 1
+ for(uint8_t i=0; i < ULAB_MAX_DIMS - 1; i++) {
+ ndarray->shape[i] = 0;
+ ndarray->strides[i] = 0;
+ }
+ ndarray->shape[ULAB_MAX_DIMS - 1] = ndarray->len;
+ ndarray->strides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
+ ndarray->ndim = 1;
+ #endif
+ } else {
+ ax = tools_get_axis(axis, ndarray->ndim);
+ }
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+ ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ // we work with the typed array, so re-scale the stride
+ int32_t increment = ndarray->strides[ax] / ndarray->itemsize;
+
+ uint8_t *array = (uint8_t *)ndarray->array;
+ if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) {
+ HEAPSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax]);
+ } else if((ndarray->dtype == NDARRAY_INT16) || (ndarray->dtype == NDARRAY_INT16)) {
+ HEAPSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax]);
+ } else {
+ HEAPSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax]);
+ }
+ if(inplace == 1) {
+ return mp_const_none;
+ } else {
+ return MP_OBJ_FROM_PTR(ndarray);
+ }
+}
+#endif /* ULAB_NUMERICAL_HAS_SORT | NDARRAY_HAS_SORT */
+
+#if ULAB_NUMPY_HAS_ALL
+mp_obj_t numerical_all(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ALL);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_all_obj, 1, numerical_all);
+#endif
+
+#if ULAB_NUMPY_HAS_ANY
+mp_obj_t numerical_any(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ANY);
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_any_obj, 1, numerical_any);
+#endif
+
+#if ULAB_NUMPY_HAS_ARGMINMAX
+//| def argmax(array: _ArrayLike, *, axis: Optional[int] = None) -> int:
+//| """Return the index of the maximum element of the 1D array"""
+//| ...
+//|
+
+mp_obj_t numerical_argmax(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMAX);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmax_obj, 1, numerical_argmax);
+
+//| def argmin(array: _ArrayLike, *, axis: Optional[int] = None) -> int:
+//| """Return the index of the minimum element of the 1D array"""
+//| ...
+//|
+
+static mp_obj_t numerical_argmin(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_ARGMIN);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argmin_obj, 1, numerical_argmin);
+#endif
+
+#if ULAB_NUMPY_HAS_ARGSORT
+//| def argsort(array: ulab.numpy.ndarray, *, axis: int = -1) -> ulab.numpy.ndarray:
+//| """Returns an array which gives indices into the input array from least to greatest."""
+//| ...
+//|
+
+mp_obj_t numerical_argsort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("argsort argument must be an ndarray"));
+ }
+
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ if(args[1].u_obj == mp_const_none) {
+ // bail out, though dense arrays could still be sorted
+ mp_raise_NotImplementedError(translate("argsort is not implemented for flattened arrays"));
+ }
+ // Since we are returning an NDARRAY_UINT16 array, bail out,
+ // if the axis is longer than what we can hold
+ for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) {
+ if(ndarray->shape[i] > 65535) {
+ mp_raise_ValueError(translate("axis too long"));
+ }
+ }
+ int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim);
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+
+ // We could return an NDARRAY_UINT8 array, if all lengths are shorter than 256
+ ndarray_obj_t *indices = ndarray_new_ndarray(ndarray->ndim, ndarray->shape, NULL, NDARRAY_UINT16);
+ int32_t *istrides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(istrides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(indices, ax, shape, istrides);
+ for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) {
+ istrides[i] /= sizeof(uint16_t);
+ }
+
+ ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ // we work with the typed array, so re-scale the stride
+ int32_t increment = ndarray->strides[ax] / ndarray->itemsize;
+ uint16_t iincrement = indices->strides[ax] / sizeof(uint16_t);
+
+ uint8_t *array = (uint8_t *)ndarray->array;
+ uint16_t *iarray = (uint16_t *)indices->array;
+
+ // fill in the index values
+ #if ULAB_MAX_DIMS > 3
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t k = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t l = 0;
+ do {
+ #endif
+ uint16_t m = 0;
+ do {
+ *iarray = m++;
+ iarray += iincrement;
+ } while(m < indices->shape[ax]);
+ #if ULAB_MAX_DIMS > 1
+ iarray -= iincrement * indices->shape[ax];
+ iarray += istrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < shape[ULAB_MAX_DIMS - 1]);
+ iarray -= istrides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
+ iarray += istrides[ULAB_MAX_DIMS - 2];
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ k++;
+ } while(k < shape[ULAB_MAX_DIMS - 2]);
+ iarray -= istrides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
+ iarray += istrides[ULAB_MAX_DIMS - 3];
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ j++;
+ } while(j < shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ // reset the array
+ iarray = indices->array;
+
+ if((ndarray->dtype == NDARRAY_UINT8) || (ndarray->dtype == NDARRAY_INT8)) {
+ HEAP_ARGSORT(ndarray, uint8_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement);
+ } else if((ndarray->dtype == NDARRAY_UINT16) || (ndarray->dtype == NDARRAY_INT16)) {
+ HEAP_ARGSORT(ndarray, uint16_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement);
+ } else {
+ HEAP_ARGSORT(ndarray, mp_float_t, array, shape, strides, ax, increment, ndarray->shape[ax], iarray, istrides, iincrement);
+ }
+ return MP_OBJ_FROM_PTR(indices);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_argsort_obj, 1, numerical_argsort);
+#endif
+
+#if ULAB_NUMPY_HAS_CROSS
+//| def cross(a: ulab.numpy.ndarray, b: ulab.numpy.ndarray) -> ulab.numpy.ndarray:
+//| """Return the cross product of two vectors of length 3"""
+//| ...
+//|
+
+static mp_obj_t numerical_cross(mp_obj_t _a, mp_obj_t _b) {
+ if (!mp_obj_is_type(_a, &ulab_ndarray_type) || !mp_obj_is_type(_b, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("arguments must be ndarrays"));
+ }
+ ndarray_obj_t *a = MP_OBJ_TO_PTR(_a);
+ ndarray_obj_t *b = MP_OBJ_TO_PTR(_b);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(a->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(b->dtype)
+ if((a->ndim != 1) || (b->ndim != 1) || (a->len != b->len) || (a->len != 3)) {
+ mp_raise_ValueError(translate("cross is defined for 1D arrays of length 3"));
+ }
+
+ mp_float_t *results = m_new(mp_float_t, 3);
+ results[0] = ndarray_get_float_index(a->array, a->dtype, 1) * ndarray_get_float_index(b->array, b->dtype, 2);
+ results[0] -= ndarray_get_float_index(a->array, a->dtype, 2) * ndarray_get_float_index(b->array, b->dtype, 1);
+ results[1] = -ndarray_get_float_index(a->array, a->dtype, 0) * ndarray_get_float_index(b->array, b->dtype, 2);
+ results[1] += ndarray_get_float_index(a->array, a->dtype, 2) * ndarray_get_float_index(b->array, b->dtype, 0);
+ results[2] = ndarray_get_float_index(a->array, a->dtype, 0) * ndarray_get_float_index(b->array, b->dtype, 1);
+ results[2] -= ndarray_get_float_index(a->array, a->dtype, 1) * ndarray_get_float_index(b->array, b->dtype, 0);
+
+ /* The upcasting happens here with the rules
+
+ - if one of the operarands is a float, the result is always float
+ - operation on identical types preserves type
+
+ uint8 + int8 => int16
+ uint8 + int16 => int16
+ uint8 + uint16 => uint16
+ int8 + int16 => int16
+ int8 + uint16 => uint16
+ uint16 + int16 => float
+
+ */
+
+ uint8_t dtype = NDARRAY_FLOAT;
+ if(a->dtype == b->dtype) {
+ dtype = a->dtype;
+ } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_INT8)) || ((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_UINT8))) {
+ dtype = NDARRAY_INT16;
+ } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_INT16)) || ((a->dtype == NDARRAY_INT16) && (b->dtype == NDARRAY_UINT8))) {
+ dtype = NDARRAY_INT16;
+ } else if(((a->dtype == NDARRAY_UINT8) && (b->dtype == NDARRAY_UINT16)) || ((a->dtype == NDARRAY_UINT16) && (b->dtype == NDARRAY_UINT8))) {
+ dtype = NDARRAY_UINT16;
+ } else if(((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_INT16)) || ((a->dtype == NDARRAY_INT16) && (b->dtype == NDARRAY_INT8))) {
+ dtype = NDARRAY_INT16;
+ } else if(((a->dtype == NDARRAY_INT8) && (b->dtype == NDARRAY_UINT16)) || ((a->dtype == NDARRAY_UINT16) && (b->dtype == NDARRAY_INT8))) {
+ dtype = NDARRAY_UINT16;
+ }
+
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(3, dtype);
+ if(dtype == NDARRAY_UINT8) {
+ uint8_t *array = (uint8_t *)ndarray->array;
+ for(uint8_t i=0; i < 3; i++) array[i] = (uint8_t)results[i];
+ } else if(dtype == NDARRAY_INT8) {
+ int8_t *array = (int8_t *)ndarray->array;
+ for(uint8_t i=0; i < 3; i++) array[i] = (int8_t)results[i];
+ } else if(dtype == NDARRAY_UINT16) {
+ uint16_t *array = (uint16_t *)ndarray->array;
+ for(uint8_t i=0; i < 3; i++) array[i] = (uint16_t)results[i];
+ } else if(dtype == NDARRAY_INT16) {
+ int16_t *array = (int16_t *)ndarray->array;
+ for(uint8_t i=0; i < 3; i++) array[i] = (int16_t)results[i];
+ } else {
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ for(uint8_t i=0; i < 3; i++) array[i] = results[i];
+ }
+ m_del(mp_float_t, results, 3);
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(numerical_cross_obj, numerical_cross);
+
+#endif /* ULAB_NUMERICAL_HAS_CROSS */
+
+#if ULAB_NUMPY_HAS_DIFF
+//| def diff(array: ulab.numpy.ndarray, *, n: int = 1, axis: int = -1) -> ulab.numpy.ndarray:
+//| """Return the numerical derivative of successive elements of the array, as
+//| an array. axis=None is not supported."""
+//| ...
+//|
+
+mp_obj_t numerical_diff(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_n, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1 } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1 } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("diff argument must be an ndarray"));
+ }
+
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ int8_t ax = args[2].u_int;
+ if(ax < 0) ax += ndarray->ndim;
+
+ if((ax < 0) || (ax > ndarray->ndim - 1)) {
+ mp_raise_ValueError(translate("index out of range"));
+ }
+
+ if((args[1].u_int < 0) || (args[1].u_int > 9)) {
+ mp_raise_ValueError(translate("differentiation order out of range"));
+ }
+ uint8_t N = (uint8_t)args[1].u_int;
+ uint8_t index = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ if(N > ndarray->shape[index]) {
+ mp_raise_ValueError(translate("differentiation order out of range"));
+ }
+
+ int8_t *stencil = m_new(int8_t, N+1);
+ stencil[0] = 1;
+ for(uint8_t i=1; i < N+1; i++) {
+ stencil[i] = -stencil[i-1]*(N-i+1)/i;
+ }
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ for(uint8_t i=0; i < ULAB_MAX_DIMS; i++) {
+ shape[i] = ndarray->shape[i];
+ if(i == index) {
+ shape[i] -= N;
+ }
+ }
+ uint8_t *array = (uint8_t *)ndarray->array;
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, shape, ndarray->dtype);
+ uint8_t *rarray = (uint8_t *)results->array;
+
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+
+ if(ndarray->dtype == NDARRAY_UINT8) {
+ RUN_DIFF(ndarray, uint8_t, array, results, rarray, shape, strides, index, stencil, N);
+ } else if(ndarray->dtype == NDARRAY_INT8) {
+ RUN_DIFF(ndarray, int8_t, array, results, rarray, shape, strides, index, stencil, N);
+ } else if(ndarray->dtype == NDARRAY_UINT16) {
+ RUN_DIFF(ndarray, uint16_t, array, results, rarray, shape, strides, index, stencil, N);
+ } else if(ndarray->dtype == NDARRAY_INT16) {
+ RUN_DIFF(ndarray, int16_t, array, results, rarray, shape, strides, index, stencil, N);
+ } else {
+ RUN_DIFF(ndarray, mp_float_t, array, results, rarray, shape, strides, index, stencil, N);
+ }
+ m_del(int8_t, stencil, N+1);
+ m_del(size_t, shape, ULAB_MAX_DIMS);
+ m_del(int32_t, strides, ULAB_MAX_DIMS);
+ return MP_OBJ_FROM_PTR(results);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_diff_obj, 1, numerical_diff);
+#endif
+
+#if ULAB_NUMPY_HAS_FLIP
+//| def flip(array: ulab.numpy.ndarray, *, axis: Optional[int] = None) -> ulab.numpy.ndarray:
+//| """Returns a new array that reverses the order of the elements along the
+//| given axis, or along all axes if axis is None."""
+//| ...
+//|
+
+mp_obj_t numerical_flip(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("flip argument must be an ndarray"));
+ }
+
+ ndarray_obj_t *results = NULL;
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
+ if(args[1].u_obj == mp_const_none) { // flip the flattened array
+ results = ndarray_new_linear_array(ndarray->len, ndarray->dtype);
+ ndarray_copy_array(ndarray, results, 0);
+ uint8_t *rarray = (uint8_t *)results->array;
+ rarray += (results->len - 1) * results->itemsize;
+ results->array = rarray;
+ results->strides[ULAB_MAX_DIMS - 1] = -results->strides[ULAB_MAX_DIMS - 1];
+ } else if(mp_obj_is_int(args[1].u_obj)){
+ int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim);
+
+ ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ int32_t offset = (ndarray->shape[ax] - 1) * ndarray->strides[ax];
+ results = ndarray_new_view(ndarray, ndarray->ndim, ndarray->shape, ndarray->strides, offset);
+ results->strides[ax] = -results->strides[ax];
+ } else {
+ mp_raise_TypeError(translate("wrong axis index"));
+ }
+ return results;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_flip_obj, 1, numerical_flip);
+#endif
+
+#if ULAB_NUMPY_HAS_MINMAX
+//| def max(array: _ArrayLike, *, axis: Optional[int] = None) -> _float:
+//| """Return the maximum element of the 1D array"""
+//| ...
+//|
+
+mp_obj_t numerical_max(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MAX);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_max_obj, 1, numerical_max);
+#endif
+
+#if ULAB_NUMPY_HAS_MEAN
+//| def mean(array: _ArrayLike, *, axis: Optional[int] = None) -> _float:
+//| """Return the mean element of the 1D array, as a number if axis is None, otherwise as an array."""
+//| ...
+//|
+
+mp_obj_t numerical_mean(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MEAN);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_mean_obj, 1, numerical_mean);
+#endif
+
+#if ULAB_NUMPY_HAS_MEDIAN
+//| def median(array: ulab.numpy.ndarray, *, axis: int = -1) -> ulab.numpy.ndarray:
+//| """Find the median value in an array along the given axis, or along all axes if axis is None."""
+//| ...
+//|
+
+mp_obj_t numerical_median(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("median argument must be an ndarray"));
+ }
+
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
+ if(ndarray->len == 0) {
+ return mp_obj_new_float(MICROPY_FLOAT_C_FUN(nan)(""));
+ }
+
+ ndarray = numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0);
+
+ if((args[1].u_obj == mp_const_none) || (ndarray->ndim == 1)) {
+ // at this point, the array holding the sorted values should be flat
+ uint8_t *array = (uint8_t *)ndarray->array;
+ size_t len = ndarray->len;
+ array += (len >> 1) * ndarray->itemsize;
+ mp_float_t median = ndarray_get_float_value(array, ndarray->dtype);
+ if(!(len & 0x01)) { // len is an even number
+ array -= ndarray->itemsize;
+ median += ndarray_get_float_value(array, ndarray->dtype);
+ median *= MICROPY_FLOAT_CONST(0.5);
+ }
+ return mp_obj_new_float(median);
+ } else {
+ int8_t ax = tools_get_axis(args[1].u_obj, ndarray->ndim);
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(uint32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+ ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim-1, shape, NDARRAY_FLOAT);
+ mp_float_t *rarray = (mp_float_t *)results->array;
+
+ uint8_t *array = (uint8_t *)ndarray->array;
+
+ size_t len = ndarray->shape[ax];
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ size_t k = 0;
+ do {
+ array += ndarray->strides[ax] * (len >> 1);
+ mp_float_t median = ndarray_get_float_value(array, ndarray->dtype);
+ if(!(len & 0x01)) { // len is an even number
+ array -= ndarray->strides[ax];
+ median += ndarray_get_float_value(array, ndarray->dtype);
+ median *= MICROPY_FLOAT_CONST(0.5);
+ array += ndarray->strides[ax];
+ }
+ array -= ndarray->strides[ax] * (len >> 1);
+ array += strides[ULAB_MAX_DIMS - 1];
+ *rarray = median;
+ rarray++;
+ k++;
+ } while(k < shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 2
+ array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
+ array += strides[ULAB_MAX_DIMS - 2];
+ j++;
+ } while(j < shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2];
+ array += strides[ULAB_MAX_DIMS - 3];
+ i++;
+ } while(i < shape[ULAB_MAX_DIMS - 3]);
+ #endif
+
+ return MP_OBJ_FROM_PTR(results);
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_median_obj, 1, numerical_median);
+#endif
+
+#if ULAB_NUMPY_HAS_MINMAX
+//| def min(array: _ArrayLike, *, axis: Optional[int] = None) -> _float:
+//| """Return the minimum element of the 1D array"""
+//| ...
+//|
+
+mp_obj_t numerical_min(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_MIN);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_min_obj, 1, numerical_min);
+#endif
+
+#if ULAB_NUMPY_HAS_ROLL
+//| def roll(array: ulab.numpy.ndarray, distance: int, *, axis: Optional[int] = None) -> None:
+//| """Shift the content of a vector by the positions given as the second
+//| argument. If the ``axis`` keyword is supplied, the shift is applied to
+//| the given axis. The array is modified in place."""
+//| ...
+//|
+
+mp_obj_t numerical_roll(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("roll argument must be an ndarray"));
+ }
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0].u_obj);
+ uint8_t *array = ndarray->array;
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndarray->ndim, ndarray->shape, ndarray->dtype);
+
+ int32_t shift = mp_obj_get_int(args[1].u_obj);
+ int32_t _shift = shift < 0 ? -shift : shift;
+
+ size_t counter;
+ uint8_t *rarray = (uint8_t *)results->array;
+
+ if(args[2].u_obj == mp_const_none) { // roll the flattened array
+ _shift = _shift % results->len;
+ if(shift > 0) { // shift to the right
+ rarray += _shift * results->itemsize;
+ counter = results->len - _shift;
+ } else { // shift to the left
+ rarray += (results->len - _shift) * results->itemsize;
+ counter = _shift;
+ }
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ memcpy(rarray, array, ndarray->itemsize);
+ rarray += results->itemsize;
+ array += ndarray->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ if(--counter == 0) {
+ rarray = results->array;
+ }
+ } while(l < ndarray->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ array -= ndarray->strides[ULAB_MAX_DIMS - 1] * ndarray->shape[ULAB_MAX_DIMS-1];
+ array += ndarray->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < ndarray->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= ndarray->strides[ULAB_MAX_DIMS - 2] * ndarray->shape[ULAB_MAX_DIMS-2];
+ array += ndarray->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < ndarray->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= ndarray->strides[ULAB_MAX_DIMS - 3] * ndarray->shape[ULAB_MAX_DIMS-3];
+ array += ndarray->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < ndarray->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ } else if(mp_obj_is_int(args[2].u_obj)){
+ int8_t ax = tools_get_axis(args[2].u_obj, ndarray->ndim);
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(shape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(strides, 0, sizeof(int32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(ndarray, ax, shape, strides);
+
+ size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
+ memset(rshape, 0, sizeof(size_t)*ULAB_MAX_DIMS);
+ int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ memset(rstrides, 0, sizeof(int32_t)*ULAB_MAX_DIMS);
+ numerical_reduce_axes(results, ax, rshape, rstrides);
+
+ ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ uint8_t *_rarray;
+ _shift = _shift % results->shape[ax];
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ _rarray = rarray;
+ if(shift < 0) {
+ rarray += (results->shape[ax] - _shift) * results->strides[ax];
+ counter = _shift;
+ } else {
+ rarray += _shift * results->strides[ax];
+ counter = results->shape[ax] - _shift;
+ }
+ do {
+ memcpy(rarray, array, ndarray->itemsize);
+ array += ndarray->strides[ax];
+ rarray += results->strides[ax];
+ if(--counter == 0) {
+ rarray = _rarray;
+ }
+ l++;
+ } while(l < ndarray->shape[ax]);
+ #if ULAB_MAX_DIMS > 1
+ rarray = _rarray;
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ array -= ndarray->strides[ax] * ndarray->shape[ax];
+ array += strides[ULAB_MAX_DIMS - 1];
+ k++;
+ } while(k < shape[ULAB_MAX_DIMS - 1]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS-1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS-1];
+ array += strides[ULAB_MAX_DIMS - 2];
+ j++;
+ } while(j < shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS-2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS-2];
+ array += strides[ULAB_MAX_DIMS - 3];
+ i++;
+ } while(i < shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ } else {
+ mp_raise_TypeError(translate("wrong axis index"));
+ }
+ return results;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_roll_obj, 2, numerical_roll);
+#endif
+
+#if ULAB_NUMPY_HAS_SORT
+//| def sort(array: ulab.numpy.ndarray, *, axis: int = -1) -> ulab.numpy.ndarray:
+//| """Sort the array along the given axis, or along all axes if axis is None.
+//| The array is modified in place."""
+//| ...
+//|
+
+mp_obj_t numerical_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 0);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_obj, 1, numerical_sort);
+#endif
+
+#if NDARRAY_HAS_SORT
+// method of an ndarray
+static mp_obj_t numerical_sort_inplace(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_int = -1 } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ return numerical_sort_helper(args[0].u_obj, args[1].u_obj, 1);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj, 1, numerical_sort_inplace);
+#endif /* NDARRAY_HAS_SORT */
+
+#if ULAB_NUMPY_HAS_STD
+//| def std(array: _ArrayLike, *, axis: Optional[int] = None, ddof: int = 0) -> _float:
+//| """Return the standard deviation of the array, as a number if axis is None, otherwise as an array."""
+//| ...
+//|
+
+mp_obj_t numerical_std(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none } } ,
+ { MP_QSTR_axis, MP_ARG_OBJ, {.u_rom_obj = mp_const_none } },
+ { MP_QSTR_ddof, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_obj_t oin = args[0].u_obj;
+ mp_obj_t axis = args[1].u_obj;
+ size_t ddof = args[2].u_int;
+ if((axis != mp_const_none) && (mp_obj_get_int(axis) != 0) && (mp_obj_get_int(axis) != 1)) {
+ // this seems to pass with False, and True...
+ mp_raise_ValueError(translate("axis must be None, or an integer"));
+ }
+ if(mp_obj_is_type(oin, &mp_type_tuple) || mp_obj_is_type(oin, &mp_type_list) || mp_obj_is_type(oin, &mp_type_range)) {
+ return numerical_sum_mean_std_iterable(oin, NUMERICAL_STD, ddof);
+ } else if(mp_obj_is_type(oin, &ulab_ndarray_type)) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(oin);
+ return numerical_sum_mean_std_ndarray(ndarray, axis, NUMERICAL_STD, ddof);
+ } else {
+ mp_raise_TypeError(translate("input must be tuple, list, range, or ndarray"));
+ }
+ return mp_const_none;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_std_obj, 1, numerical_std);
+#endif
+
+#if ULAB_NUMPY_HAS_SUM
+//| def sum(array: _ArrayLike, *, axis: Optional[int] = None) -> Union[_float, int, ulab.numpy.ndarray]:
+//| """Return the sum of the array, as a number if axis is None, otherwise as an array."""
+//| ...
+//|
+
+mp_obj_t numerical_sum(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ return numerical_function(n_args, pos_args, kw_args, NUMERICAL_SUM);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(numerical_sum_obj, 1, numerical_sum);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/numerical.h b/circuitpython/extmod/ulab/code/numpy/numerical.h
new file mode 100644
index 0000000..8d2971c
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/numerical.h
@@ -0,0 +1,652 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _NUMERICAL_
+#define _NUMERICAL_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+// TODO: implement cumsum
+
+#define RUN_ARGMIN1(ndarray, type, array, results, rarray, index, op)\
+({\
+ uint16_t best_index = 0;\
+ type best_value = *((type *)(array));\
+ if(((op) == NUMERICAL_MAX) || ((op) == NUMERICAL_ARGMAX)) {\
+ for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
+ if(*((type *)(array)) > best_value) {\
+ best_index = i;\
+ best_value = *((type *)(array));\
+ }\
+ (array) += (ndarray)->strides[(index)];\
+ }\
+ } else {\
+ for(uint16_t i=0; i < (ndarray)->shape[(index)]; i++) {\
+ if(*((type *)(array)) < best_value) {\
+ best_index = i;\
+ best_value = *((type *)(array));\
+ }\
+ (array) += (ndarray)->strides[(index)];\
+ }\
+ }\
+ if(((op) == NUMERICAL_ARGMAX) || ((op) == NUMERICAL_ARGMIN)) {\
+ memcpy((rarray), &best_index, (results)->itemsize);\
+ } else {\
+ memcpy((rarray), &best_value, (results)->itemsize);\
+ }\
+ (rarray) += (results)->itemsize;\
+})
+
+#define RUN_SUM1(type, array, results, rarray, ss)\
+({\
+ type sum = 0;\
+ for(size_t i=0; i < (ss).shape[0]; i++) {\
+ sum += *((type *)(array));\
+ (array) += (ss).strides[0];\
+ }\
+ memcpy((rarray), &sum, (results)->itemsize);\
+ (rarray) += (results)->itemsize;\
+})
+
+// The mean could be calculated by simply dividing the sum by
+// the number of elements, but that method is numerically unstable
+#define RUN_MEAN1(type, array, rarray, ss)\
+({\
+ mp_float_t M = 0.0;\
+ for(size_t i=0; i < (ss).shape[0]; i++) {\
+ mp_float_t value = (mp_float_t)(*(type *)(array));\
+ M = M + (value - M) / (mp_float_t)(i+1);\
+ (array) += (ss).strides[0];\
+ }\
+ *(rarray)++ = M;\
+})
+
+// Instead of the straightforward implementation of the definition,
+// we take the numerically stable Welford algorithm here
+// https://www.johndcook.com/blog/2008/09/26/comparing-three-methods-of-computing-standard-deviation/
+#define RUN_STD1(type, array, rarray, ss, div)\
+({\
+ mp_float_t M = 0.0, m = 0.0, S = 0.0;\
+ for(size_t i=0; i < (ss).shape[0]; i++) {\
+ mp_float_t value = (mp_float_t)(*(type *)(array));\
+ m = M + (value - M) / (mp_float_t)(i+1);\
+ S = S + (value - M) * (value - m);\
+ M = m;\
+ (array) += (ss).strides[0];\
+ }\
+ *(rarray)++ = MICROPY_FLOAT_C_FUN(sqrt)(S / (div));\
+})
+
+#define RUN_MEAN_STD1(type, array, rarray, ss, div, isStd)\
+({\
+ mp_float_t M = 0.0, m = 0.0, S = 0.0;\
+ for(size_t i=0; i < (ss).shape[0]; i++) {\
+ mp_float_t value = (mp_float_t)(*(type *)(array));\
+ m = M + (value - M) / (mp_float_t)(i+1);\
+ if(isStd) {\
+ S += (value - M) * (value - m);\
+ }\
+ M = m;\
+ (array) += (ss).strides[0];\
+ }\
+ *(rarray)++ = isStd ? MICROPY_FLOAT_C_FUN(sqrt)(S / (div)) : M;\
+})
+
+#define RUN_DIFF1(ndarray, type, array, results, rarray, index, stencil, N)\
+({\
+ for(size_t i=0; i < (results)->shape[ULAB_MAX_DIMS - 1]; i++) {\
+ type sum = 0;\
+ uint8_t *source = (array);\
+ for(uint8_t d=0; d < (N)+1; d++) {\
+ sum -= (stencil)[d] * *((type *)source);\
+ source += (ndarray)->strides[(index)];\
+ }\
+ (array) += (ndarray)->strides[ULAB_MAX_DIMS - 1];\
+ *(type *)(rarray) = sum;\
+ (rarray) += (results)->itemsize;\
+ }\
+})
+
+#define HEAPSORT1(type, array, increment, N)\
+({\
+ type *_array = (type *)array;\
+ type tmp;\
+ size_t c, q = (N), p, r = (N) >> 1;\
+ for (;;) {\
+ if (r > 0) {\
+ tmp = _array[(--r)*(increment)];\
+ } else {\
+ q--;\
+ if(q == 0) {\
+ break;\
+ }\
+ tmp = _array[q*(increment)];\
+ _array[q*(increment)] = _array[0];\
+ }\
+ p = r;\
+ c = r + r + 1;\
+ while (c < q) {\
+ if((c + 1 < q) && (_array[(c+1)*(increment)] > _array[c*(increment)])) {\
+ c++;\
+ }\
+ if(_array[c*(increment)] > tmp) {\
+ _array[p*(increment)] = _array[c*(increment)];\
+ p = c;\
+ c = p + p + 1;\
+ } else {\
+ break;\
+ }\
+ }\
+ _array[p*(increment)] = tmp;\
+ }\
+})
+
+#define HEAP_ARGSORT1(type, array, increment, N, iarray, iincrement)\
+({\
+ type *_array = (type *)array;\
+ type tmp;\
+ uint16_t itmp, c, q = (N), p, r = (N) >> 1;\
+ for (;;) {\
+ if (r > 0) {\
+ r--;\
+ itmp = (iarray)[r*(iincrement)];\
+ tmp = _array[itmp*(increment)];\
+ } else {\
+ q--;\
+ if(q == 0) {\
+ break;\
+ }\
+ itmp = (iarray)[q*(iincrement)];\
+ tmp = _array[itmp*(increment)];\
+ (iarray)[q*(iincrement)] = (iarray)[0];\
+ }\
+ p = r;\
+ c = r + r + 1;\
+ while (c < q) {\
+ if((c + 1 < q) && (_array[(iarray)[(c+1)*(iincrement)]*(increment)] > _array[(iarray)[c*(iincrement)]*(increment)])) {\
+ c++;\
+ }\
+ if(_array[(iarray)[c*(iincrement)]*(increment)] > tmp) {\
+ (iarray)[p*(iincrement)] = (iarray)[c*(iincrement)];\
+ p = c;\
+ c = p + p + 1;\
+ } else {\
+ break;\
+ }\
+ }\
+ (iarray)[p*(iincrement)] = itmp;\
+ }\
+})
+
+#if ULAB_MAX_DIMS == 1
+#define RUN_SUM(type, array, results, rarray, ss) do {\
+ RUN_SUM1(type, (array), (results), (rarray), (ss));\
+} while(0)
+
+#define RUN_MEAN(type, array, rarray, ss) do {\
+ RUN_MEAN1(type, (array), (rarray), (ss));\
+} while(0)
+
+#define RUN_STD(type, array, rarray, ss, div) do {\
+ RUN_STD1(type, (array), (results), (rarray), (ss), (div));\
+} while(0)
+
+#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
+ RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
+} while(0)
+
+#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
+ RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
+} while(0)
+
+#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
+ RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
+} while(0)
+
+#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
+ HEAPSORT1(type, (array), (increment), (N));\
+} while(0)
+
+#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
+ HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
+} while(0)
+
+#endif
+
+#if ULAB_MAX_DIMS == 2
+#define RUN_SUM(type, array, results, rarray, ss) do {\
+ size_t l = 0;\
+ do {\
+ RUN_SUM1(type, (array), (results), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#define RUN_MEAN(type, array, rarray, ss) do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN1(type, (array), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#define RUN_STD(type, array, rarray, ss, div) do {\
+ size_t l = 0;\
+ do {\
+ RUN_STD1(type, (array), (rarray), (ss), (div));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+
+#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
+ size_t l = 0;\
+ do {\
+ RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
+ (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
+ size_t l = 0;\
+ do {\
+ RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
+ (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
+ l++;\
+ } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
+ size_t l = 0;\
+ do {\
+ HEAPSORT1(type, (array), (increment), (N));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
+ size_t l = 0;\
+ do {\
+ HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+} while(0)
+
+#endif
+
+#if ULAB_MAX_DIMS == 3
+#define RUN_SUM(type, array, results, rarray, ss) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_SUM1(type, (array), (results), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define RUN_MEAN(type, array, rarray, ss) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN1(type, (array), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define RUN_STD(type, array, rarray, ss, div) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_STD1(type, (array), (rarray), (ss), (div));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
+ (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
+ (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 2]);\
+ (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS-2];\
+ (array) += (ndarray)->strides[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ HEAPSORT1(type, (array), (increment), (N));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+} while(0)
+
+#endif
+
+#if ULAB_MAX_DIMS == 4
+#define RUN_SUM(type, array, results, rarray, ss) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_SUM1(type, (array), (results), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define RUN_MEAN(type, array, rarray, ss) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN1(type, (array), (rarray), (ss));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define RUN_STD(type, array, rarray, ss, div) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_STD1(type, (array), (rarray), (ss), (div));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define RUN_MEAN_STD(type, array, rarray, ss, div, isStd) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_MEAN_STD1(type, (array), (rarray), (ss), (div), (isStd));\
+ (array) -= (ss).strides[0] * (ss).shape[0];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (ss).shape[ULAB_MAX_DIMS - 1]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 1] * (ss).shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (ss).shape[ULAB_MAX_DIMS - 2]);\
+ (array) -= (ss).strides[ULAB_MAX_DIMS - 2] * (ss).shape[ULAB_MAX_DIMS - 2];\
+ (array) += (ss).strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (ss).shape[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define RUN_ARGMIN(ndarray, type, array, results, rarray, shape, strides, index, op) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_ARGMIN1((ndarray), type, (array), (results), (rarray), (index), (op));\
+ (array) -= (ndarray)->strides[(index)] * (ndarray)->shape[(index)];\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
+ (array) += (strides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (shape)[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define RUN_DIFF(ndarray, type, array, results, rarray, shape, strides, index, stencil, N) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ RUN_DIFF1((ndarray), type, (array), (results), (rarray), (index), (stencil), (N));\
+ (array) -= (ndarray)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (array) += (ndarray)->strides[ULAB_MAX_DIMS - 2];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 2];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 2]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
+ (array) += (strides)[ULAB_MAX_DIMS - 3];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 3];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 3]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 3] * (shape)[ULAB_MAX_DIMS-3];\
+ (array) += (strides)[ULAB_MAX_DIMS - 4];\
+ (rarray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\
+ (rarray) += (results)->strides[ULAB_MAX_DIMS - 4];\
+ j++;\
+ } while(j < (shape)[ULAB_MAX_DIMS - 4]);\
+} while(0)
+
+#define HEAPSORT(ndarray, type, array, shape, strides, index, increment, N) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ HEAPSORT1(type, (array), (increment), (N));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+ (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
+ (array) += (strides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (shape)[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#define HEAP_ARGSORT(ndarray, type, array, shape, strides, index, increment, N, iarray, istrides, iincrement) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ HEAP_ARGSORT1(type, (array), (increment), (N), (iarray), (iincrement));\
+ (array) += (strides)[ULAB_MAX_DIMS - 1];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (shape)[ULAB_MAX_DIMS - 1]);\
+ (iarray) -= (istrides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 2];\
+ (array) -= (strides)[ULAB_MAX_DIMS - 1] * (shape)[ULAB_MAX_DIMS-1];\
+ (array) += (strides)[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (shape)[ULAB_MAX_DIMS - 2]);\
+ (iarray) -= (istrides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
+ (iarray) += (istrides)[ULAB_MAX_DIMS - 3];\
+ (array) -= (strides)[ULAB_MAX_DIMS - 2] * (shape)[ULAB_MAX_DIMS-2];\
+ (array) += (strides)[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (shape)[ULAB_MAX_DIMS - 3]);\
+} while(0)
+
+#endif
+
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_all_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_any_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmax_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argmin_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_argsort_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(numerical_cross_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_diff_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_flip_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_max_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_mean_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_median_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_min_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_roll_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_std_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sum_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(numerical_sort_inplace_obj);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/numpy.c b/circuitpython/extmod/ulab/code/numpy/numpy.c
new file mode 100644
index 0000000..ebd171d
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/numpy.c
@@ -0,0 +1,383 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Jeff Epler for Adafruit Industries
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020-2022 Zoltán Vörös
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <string.h>
+#include "py/runtime.h"
+
+#include "numpy.h"
+#include "approx.h"
+#include "carray/carray.h"
+#include "compare.h"
+#include "create.h"
+#include "fft/fft.h"
+#include "filter.h"
+#include "linalg/linalg.h"
+#include "numerical.h"
+#include "stats.h"
+#include "transform.h"
+#include "poly.h"
+#include "vector.h"
+
+//| """Compatibility layer for numpy"""
+//|
+
+//| class ndarray: ...
+
+//| def get_printoptions() -> Dict[str, int]:
+//| """Get printing options"""
+//| ...
+//|
+//| def set_printoptions(threshold: Optional[int] = None, edgeitems: Optional[int] = None) -> None:
+//| """Set printing options"""
+//| ...
+//|
+//| def ndinfo(array: ulab.numpy.ndarray) -> None:
+//| ...
+//|
+//| def array(
+//| values: Union[ndarray, Iterable[Union[_float, _bool, Iterable[Any]]]],
+//| *,
+//| dtype: _DType = ulab.numpy.float
+//| ) -> ulab.numpy.ndarray:
+//| """alternate constructor function for `ulab.numpy.ndarray`. Mirrors numpy.array"""
+//| ...
+
+// math constants
+#if ULAB_NUMPY_HAS_E
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
+#define ulab_const_float_e MP_ROM_PTR((mp_obj_t)(((0x402df854 & ~3) | 2) + 0x80800000))
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+#define ulab_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))}
+#else
+mp_obj_float_t ulab_const_float_e_obj = {{&mp_type_float}, MP_E};
+#define ulab_const_float_e MP_ROM_PTR(&ulab_const_float_e_obj)
+#endif
+#endif
+
+#if ULAB_NUMPY_HAS_INF
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
+#define numpy_const_float_inf MP_ROM_PTR((mp_obj_t)(0x7f800002 + 0x80800000))
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+#define numpy_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))}
+#else
+mp_obj_float_t numpy_const_float_inf_obj = {{&mp_type_float}, (mp_float_t)INFINITY};
+#define numpy_const_float_inf MP_ROM_PTR(&numpy_const_float_inf_obj)
+#endif
+#endif
+
+#if ULAB_NUMPY_HAS_NAN
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
+#define numpy_const_float_nan MP_ROM_PTR((mp_obj_t)(0x7fc00002 + 0x80800000))
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+#define numpy_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))}
+#else
+mp_obj_float_t numpy_const_float_nan_obj = {{&mp_type_float}, (mp_float_t)NAN};
+#define numpy_const_float_nan MP_ROM_PTR(&numpy_const_float_nan_obj)
+#endif
+#endif
+
+#if ULAB_NUMPY_HAS_PI
+#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
+#define ulab_const_float_pi MP_ROM_PTR((mp_obj_t)(((0x40490fdb & ~3) | 2) + 0x80800000))
+#elif MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D
+#define ulab_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))}
+#else
+mp_obj_float_t ulab_const_float_pi_obj = {{&mp_type_float}, MP_PI};
+#define ulab_const_float_pi MP_ROM_PTR(&ulab_const_float_pi_obj)
+#endif
+#endif
+
+static const mp_rom_map_elem_t ulab_numpy_globals_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_numpy) },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_ndarray), (mp_obj_t)&ulab_ndarray_type },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_array), MP_ROM_PTR(&ndarray_array_constructor_obj) },
+ #if ULAB_NUMPY_HAS_FROMBUFFER
+ { MP_ROM_QSTR(MP_QSTR_frombuffer), MP_ROM_PTR(&create_frombuffer_obj) },
+ #endif
+ // math constants
+ #if ULAB_NUMPY_HAS_E
+ { MP_ROM_QSTR(MP_QSTR_e), ulab_const_float_e },
+ #endif
+ #if ULAB_NUMPY_HAS_INF
+ { MP_ROM_QSTR(MP_QSTR_inf), numpy_const_float_inf },
+ #endif
+ #if ULAB_NUMPY_HAS_NAN
+ { MP_ROM_QSTR(MP_QSTR_nan), numpy_const_float_nan },
+ #endif
+ #if ULAB_NUMPY_HAS_PI
+ { MP_ROM_QSTR(MP_QSTR_pi), ulab_const_float_pi },
+ #endif
+ // class constants, always included
+ { MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_INT(NDARRAY_BOOL) },
+ { MP_ROM_QSTR(MP_QSTR_uint8), MP_ROM_INT(NDARRAY_UINT8) },
+ { MP_ROM_QSTR(MP_QSTR_int8), MP_ROM_INT(NDARRAY_INT8) },
+ { MP_ROM_QSTR(MP_QSTR_uint16), MP_ROM_INT(NDARRAY_UINT16) },
+ { MP_ROM_QSTR(MP_QSTR_int16), MP_ROM_INT(NDARRAY_INT16) },
+ { MP_ROM_QSTR(MP_QSTR_float), MP_ROM_INT(NDARRAY_FLOAT) },
+ #if ULAB_SUPPORTS_COMPLEX
+ { MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_INT(NDARRAY_COMPLEX) },
+ #endif
+ // modules of numpy
+ #if ULAB_NUMPY_HAS_FFT_MODULE
+ { MP_ROM_QSTR(MP_QSTR_fft), MP_ROM_PTR(&ulab_fft_module) },
+ #endif
+ #if ULAB_NUMPY_HAS_LINALG_MODULE
+ { MP_ROM_QSTR(MP_QSTR_linalg), MP_ROM_PTR(&ulab_linalg_module) },
+ #endif
+ #if ULAB_HAS_PRINTOPTIONS
+ { MP_ROM_QSTR(MP_QSTR_set_printoptions), (mp_obj_t)&ndarray_set_printoptions_obj },
+ { MP_ROM_QSTR(MP_QSTR_get_printoptions), (mp_obj_t)&ndarray_get_printoptions_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_NDINFO
+ { MP_ROM_QSTR(MP_QSTR_ndinfo), (mp_obj_t)&ndarray_info_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ARANGE
+ { MP_ROM_QSTR(MP_QSTR_arange), (mp_obj_t)&create_arange_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_COMPRESS
+ { MP_ROM_QSTR(MP_QSTR_compress), (mp_obj_t)&transform_compress_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_CONCATENATE
+ { MP_ROM_QSTR(MP_QSTR_concatenate), (mp_obj_t)&create_concatenate_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_DIAG
+ #if ULAB_MAX_DIMS > 1
+ { MP_ROM_QSTR(MP_QSTR_diag), (mp_obj_t)&create_diag_obj },
+ #endif
+ #endif
+ #if ULAB_NUMPY_HAS_EMPTY
+ { MP_ROM_QSTR(MP_QSTR_empty), (mp_obj_t)&create_zeros_obj },
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ #if ULAB_NUMPY_HAS_EYE
+ { MP_ROM_QSTR(MP_QSTR_eye), (mp_obj_t)&create_eye_obj },
+ #endif
+ #endif /* ULAB_MAX_DIMS */
+ // functions of the approx sub-module
+ #if ULAB_NUMPY_HAS_INTERP
+ { MP_OBJ_NEW_QSTR(MP_QSTR_interp), (mp_obj_t)&approx_interp_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_TRAPZ
+ { MP_OBJ_NEW_QSTR(MP_QSTR_trapz), (mp_obj_t)&approx_trapz_obj },
+ #endif
+ // functions of the create sub-module
+ #if ULAB_NUMPY_HAS_FULL
+ { MP_ROM_QSTR(MP_QSTR_full), (mp_obj_t)&create_full_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_LINSPACE
+ { MP_ROM_QSTR(MP_QSTR_linspace), (mp_obj_t)&create_linspace_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_LOGSPACE
+ { MP_ROM_QSTR(MP_QSTR_logspace), (mp_obj_t)&create_logspace_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ONES
+ { MP_ROM_QSTR(MP_QSTR_ones), (mp_obj_t)&create_ones_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ZEROS
+ { MP_ROM_QSTR(MP_QSTR_zeros), (mp_obj_t)&create_zeros_obj },
+ #endif
+ // functions of the compare sub-module
+ #if ULAB_NUMPY_HAS_CLIP
+ { MP_OBJ_NEW_QSTR(MP_QSTR_clip), (mp_obj_t)&compare_clip_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_EQUAL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_equal), (mp_obj_t)&compare_equal_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_NOTEQUAL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_not_equal), (mp_obj_t)&compare_not_equal_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ISFINITE
+ { MP_OBJ_NEW_QSTR(MP_QSTR_isfinite), (mp_obj_t)&compare_isfinite_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ISINF
+ { MP_OBJ_NEW_QSTR(MP_QSTR_isinf), (mp_obj_t)&compare_isinf_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MAXIMUM
+ { MP_OBJ_NEW_QSTR(MP_QSTR_maximum), (mp_obj_t)&compare_maximum_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MINIMUM
+ { MP_OBJ_NEW_QSTR(MP_QSTR_minimum), (mp_obj_t)&compare_minimum_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_WHERE
+ { MP_OBJ_NEW_QSTR(MP_QSTR_where), (mp_obj_t)&compare_where_obj },
+ #endif
+ // functions of the filter sub-module
+ #if ULAB_NUMPY_HAS_CONVOLVE
+ { MP_OBJ_NEW_QSTR(MP_QSTR_convolve), (mp_obj_t)&filter_convolve_obj },
+ #endif
+ // functions of the numerical sub-module
+ #if ULAB_NUMPY_HAS_ALL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_all), (mp_obj_t)&numerical_all_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ANY
+ { MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&numerical_any_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ARGMINMAX
+ { MP_OBJ_NEW_QSTR(MP_QSTR_argmax), (mp_obj_t)&numerical_argmax_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_argmin), (mp_obj_t)&numerical_argmin_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ARGSORT
+ { MP_OBJ_NEW_QSTR(MP_QSTR_argsort), (mp_obj_t)&numerical_argsort_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_CROSS
+ { MP_OBJ_NEW_QSTR(MP_QSTR_cross), (mp_obj_t)&numerical_cross_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_DIFF
+ { MP_OBJ_NEW_QSTR(MP_QSTR_diff), (mp_obj_t)&numerical_diff_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_DOT
+ #if ULAB_MAX_DIMS > 1
+ { MP_OBJ_NEW_QSTR(MP_QSTR_dot), (mp_obj_t)&transform_dot_obj },
+ #endif
+ #endif
+ #if ULAB_NUMPY_HAS_TRACE
+ #if ULAB_MAX_DIMS > 1
+ { MP_ROM_QSTR(MP_QSTR_trace), (mp_obj_t)&stats_trace_obj },
+ #endif
+ #endif
+ #if ULAB_NUMPY_HAS_FLIP
+ { MP_OBJ_NEW_QSTR(MP_QSTR_flip), (mp_obj_t)&numerical_flip_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MINMAX
+ { MP_OBJ_NEW_QSTR(MP_QSTR_max), (mp_obj_t)&numerical_max_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MEAN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_mean), (mp_obj_t)&numerical_mean_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MEDIAN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_median), (mp_obj_t)&numerical_median_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_MINMAX
+ { MP_OBJ_NEW_QSTR(MP_QSTR_min), (mp_obj_t)&numerical_min_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ROLL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_roll), (mp_obj_t)&numerical_roll_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SORT
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sort), (mp_obj_t)&numerical_sort_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_STD
+ { MP_OBJ_NEW_QSTR(MP_QSTR_std), (mp_obj_t)&numerical_std_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SUM
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sum), (mp_obj_t)&numerical_sum_obj },
+ #endif
+ // functions of the poly sub-module
+ #if ULAB_NUMPY_HAS_POLYFIT
+ { MP_OBJ_NEW_QSTR(MP_QSTR_polyfit), (mp_obj_t)&poly_polyfit_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_POLYVAL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_polyval), (mp_obj_t)&poly_polyval_obj },
+ #endif
+ // functions of the vector sub-module
+ #if ULAB_NUMPY_HAS_ACOS
+ { MP_OBJ_NEW_QSTR(MP_QSTR_acos), (mp_obj_t)&vector_acos_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ACOSH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_acosh), (mp_obj_t)&vector_acosh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ARCTAN2
+ { MP_OBJ_NEW_QSTR(MP_QSTR_arctan2), (mp_obj_t)&vector_arctan2_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_AROUND
+ { MP_OBJ_NEW_QSTR(MP_QSTR_around), (mp_obj_t)&vector_around_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ASIN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_asin), (mp_obj_t)&vector_asin_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ASINH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_asinh), (mp_obj_t)&vector_asinh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ATAN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_atan), (mp_obj_t)&vector_atan_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_ATANH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_atanh), (mp_obj_t)&vector_atanh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_CEIL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_ceil), (mp_obj_t)&vector_ceil_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_COS
+ { MP_OBJ_NEW_QSTR(MP_QSTR_cos), (mp_obj_t)&vector_cos_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_COSH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_cosh), (mp_obj_t)&vector_cosh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_DEGREES
+ { MP_OBJ_NEW_QSTR(MP_QSTR_degrees), (mp_obj_t)&vector_degrees_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_EXP
+ { MP_OBJ_NEW_QSTR(MP_QSTR_exp), (mp_obj_t)&vector_exp_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_EXPM1
+ { MP_OBJ_NEW_QSTR(MP_QSTR_expm1), (mp_obj_t)&vector_expm1_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_FLOOR
+ { MP_OBJ_NEW_QSTR(MP_QSTR_floor), (mp_obj_t)&vector_floor_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_LOG
+ { MP_OBJ_NEW_QSTR(MP_QSTR_log), (mp_obj_t)&vector_log_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_LOG10
+ { MP_OBJ_NEW_QSTR(MP_QSTR_log10), (mp_obj_t)&vector_log10_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_LOG2
+ { MP_OBJ_NEW_QSTR(MP_QSTR_log2), (mp_obj_t)&vector_log2_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_RADIANS
+ { MP_OBJ_NEW_QSTR(MP_QSTR_radians), (mp_obj_t)&vector_radians_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SIN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sin), (mp_obj_t)&vector_sin_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SINH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sinh), (mp_obj_t)&vector_sinh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SQRT
+ { MP_OBJ_NEW_QSTR(MP_QSTR_sqrt), (mp_obj_t)&vector_sqrt_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_TAN
+ { MP_OBJ_NEW_QSTR(MP_QSTR_tan), (mp_obj_t)&vector_tan_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_TANH
+ { MP_OBJ_NEW_QSTR(MP_QSTR_tanh), (mp_obj_t)&vector_tanh_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_VECTORIZE
+ { MP_OBJ_NEW_QSTR(MP_QSTR_vectorize), (mp_obj_t)&vector_vectorize_obj },
+ #endif
+ #if ULAB_SUPPORTS_COMPLEX
+ #if ULAB_NUMPY_HAS_REAL
+ { MP_OBJ_NEW_QSTR(MP_QSTR_real), (mp_obj_t)&carray_real_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_IMAG
+ { MP_OBJ_NEW_QSTR(MP_QSTR_imag), (mp_obj_t)&carray_imag_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_CONJUGATE
+ { MP_ROM_QSTR(MP_QSTR_conjugate), (mp_obj_t)&carray_conjugate_obj },
+ #endif
+ #if ULAB_NUMPY_HAS_SORT_COMPLEX
+ { MP_ROM_QSTR(MP_QSTR_sort_complex), (mp_obj_t)&carray_sort_complex_obj },
+ #endif
+ #endif
+};
+
+static MP_DEFINE_CONST_DICT(mp_module_ulab_numpy_globals, ulab_numpy_globals_table);
+
+const mp_obj_module_t ulab_numpy_module = {
+ .base = { &mp_type_module },
+ .globals = (mp_obj_dict_t*)&mp_module_ulab_numpy_globals,
+};
+
+MP_REGISTER_MODULE(MP_QSTR_ulab_dot_numpy, ulab_numpy_module, MODULE_ULAB_ENABLED && CIRCUITPY_ULAB);
diff --git a/circuitpython/extmod/ulab/code/numpy/numpy.h b/circuitpython/extmod/ulab/code/numpy/numpy.h
new file mode 100644
index 0000000..f1348f3
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/numpy.h
@@ -0,0 +1,21 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020-2021 Zoltán Vörös
+ *
+*/
+
+#ifndef _NUMPY_
+#define _NUMPY_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+extern const mp_obj_module_t ulab_numpy_module;
+
+#endif /* _NUMPY_ */
diff --git a/circuitpython/extmod/ulab/code/numpy/poly.c b/circuitpython/extmod/ulab/code/numpy/poly.c
new file mode 100644
index 0000000..97ee5c7
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/poly.c
@@ -0,0 +1,250 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Jeff Epler for Adafruit Industries
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Taku Fukada
+*/
+
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/objarray.h"
+
+#include "../ulab.h"
+#include "linalg/linalg_tools.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "poly.h"
+
+#if ULAB_NUMPY_HAS_POLYFIT
+
+mp_obj_t poly_polyfit(size_t n_args, const mp_obj_t *args) {
+ if(!ndarray_object_is_array_like(args[0])) {
+ mp_raise_ValueError(translate("input data must be an iterable"));
+ }
+ #if ULAB_SUPPORTS_COMPLEX
+ if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[0]);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ }
+ #endif
+ size_t lenx = 0, leny = 0;
+ uint8_t deg = 0;
+ mp_float_t *x, *XT, *y, *prod;
+
+ if(n_args == 2) { // only the y values are supplied
+ // TODO: this is actually not enough: the first argument can very well be a matrix,
+ // in which case we are between the rock and a hard place
+ leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
+ deg = (uint8_t)mp_obj_get_int(args[1]);
+ if(leny < deg) {
+ mp_raise_ValueError(translate("more degrees of freedom than data points"));
+ }
+ lenx = leny;
+ x = m_new(mp_float_t, lenx); // assume uniformly spaced data points
+ for(size_t i=0; i < lenx; i++) {
+ x[i] = i;
+ }
+ y = m_new(mp_float_t, leny);
+ fill_array_iterable(y, args[0]);
+ } else /* n_args == 3 */ {
+ if(!ndarray_object_is_array_like(args[1])) {
+ mp_raise_ValueError(translate("input data must be an iterable"));
+ }
+ lenx = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
+ leny = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[1]));
+ if(lenx != leny) {
+ mp_raise_ValueError(translate("input vectors must be of equal length"));
+ }
+ deg = (uint8_t)mp_obj_get_int(args[2]);
+ if(leny < deg) {
+ mp_raise_ValueError(translate("more degrees of freedom than data points"));
+ }
+ x = m_new(mp_float_t, lenx);
+ fill_array_iterable(x, args[0]);
+ y = m_new(mp_float_t, leny);
+ fill_array_iterable(y, args[1]);
+ }
+
+ // one could probably express X as a function of XT,
+ // and thereby save RAM, because X is used only in the product
+ XT = m_new(mp_float_t, (deg+1)*leny); // XT is a matrix of shape (deg+1, len) (rows, columns)
+ for(size_t i=0; i < leny; i++) { // column index
+ XT[i+0*lenx] = 1.0; // top row
+ for(uint8_t j=1; j < deg+1; j++) { // row index
+ XT[i+j*leny] = XT[i+(j-1)*leny]*x[i];
+ }
+ }
+
+ prod = m_new(mp_float_t, (deg+1)*(deg+1)); // the product matrix is of shape (deg+1, deg+1)
+ mp_float_t sum;
+ for(uint8_t i=0; i < deg+1; i++) { // column index
+ for(uint8_t j=0; j < deg+1; j++) { // row index
+ sum = 0.0;
+ for(size_t k=0; k < lenx; k++) {
+ // (j, k) * (k, i)
+ // Note that the second matrix is simply the transpose of the first:
+ // X(k, i) = XT(i, k) = XT[k*lenx+i]
+ sum += XT[j*lenx+k]*XT[i*lenx+k]; // X[k*(deg+1)+i];
+ }
+ prod[j*(deg+1)+i] = sum;
+ }
+ }
+ if(!linalg_invert_matrix(prod, deg+1)) {
+ // Although X was a Vandermonde matrix, whose inverse is guaranteed to exist,
+ // we bail out here, if prod couldn't be inverted: if the values in x are not all
+ // distinct, prod is singular
+ m_del(mp_float_t, XT, (deg+1)*lenx);
+ m_del(mp_float_t, x, lenx);
+ m_del(mp_float_t, y, lenx);
+ m_del(mp_float_t, prod, (deg+1)*(deg+1));
+ mp_raise_ValueError(translate("could not invert Vandermonde matrix"));
+ }
+ // at this point, we have the inverse of X^T * X
+ // y is a column vector; x is free now, we can use it for storing intermediate values
+ for(uint8_t i=0; i < deg+1; i++) { // row index
+ sum = 0.0;
+ for(size_t j=0; j < lenx; j++) { // column index
+ sum += XT[i*lenx+j]*y[j];
+ }
+ x[i] = sum;
+ }
+ // XT is no longer needed
+ m_del(mp_float_t, XT, (deg+1)*leny);
+
+ ndarray_obj_t *beta = ndarray_new_linear_array(deg+1, NDARRAY_FLOAT);
+ mp_float_t *betav = (mp_float_t *)beta->array;
+ // x[0..(deg+1)] contains now the product X^T * y; we can get rid of y
+ m_del(float, y, leny);
+
+ // now, we calculate beta, i.e., we apply prod = (X^T * X)^(-1) on x = X^T * y; x is a column vector now
+ for(uint8_t i=0; i < deg+1; i++) {
+ sum = 0.0;
+ for(uint8_t j=0; j < deg+1; j++) {
+ sum += prod[i*(deg+1)+j]*x[j];
+ }
+ betav[i] = sum;
+ }
+ m_del(mp_float_t, x, lenx);
+ m_del(mp_float_t, prod, (deg+1)*(deg+1));
+ for(uint8_t i=0; i < (deg+1)/2; i++) {
+ // We have to reverse the array, for the leading coefficient comes first.
+ SWAP(mp_float_t, betav[i], betav[deg-i]);
+ }
+ return MP_OBJ_FROM_PTR(beta);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj, 2, 3, poly_polyfit);
+#endif
+
+#if ULAB_NUMPY_HAS_POLYVAL
+
+mp_obj_t poly_polyval(mp_obj_t o_p, mp_obj_t o_x) {
+ if(!ndarray_object_is_array_like(o_p) || !ndarray_object_is_array_like(o_x)) {
+ mp_raise_TypeError(translate("inputs are not iterable"));
+ }
+ #if ULAB_SUPPORTS_COMPLEX
+ ndarray_obj_t *input;
+ if(mp_obj_is_type(o_p, &ulab_ndarray_type)) {
+ input = MP_OBJ_TO_PTR(o_p);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
+ }
+ if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
+ input = MP_OBJ_TO_PTR(o_x);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(input->dtype)
+ }
+ #endif
+ // p had better be a one-dimensional standard iterable
+ uint8_t plen = mp_obj_get_int(mp_obj_len_maybe(o_p));
+ mp_float_t *p = m_new(mp_float_t, plen);
+ mp_obj_iter_buf_t p_buf;
+ mp_obj_t p_item, p_iterable = mp_getiter(o_p, &p_buf);
+ uint8_t i = 0;
+ while((p_item = mp_iternext(p_iterable)) != MP_OBJ_STOP_ITERATION) {
+ p[i] = mp_obj_get_float(p_item);
+ i++;
+ }
+
+ // polynomials are going to be of type float, except, when both
+ // the coefficients and the independent variable are integers
+ ndarray_obj_t *ndarray;
+ if(mp_obj_is_type(o_x, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(o_x);
+ uint8_t *sarray = (uint8_t *)source->array;
+ ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+
+ mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
+
+ // TODO: these loops are really nothing, but the re-impplementation of
+ // ITERATE_VECTOR from vectorise.c. We could pass a function pointer here
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t y = p[0];
+ mp_float_t _x = func(sarray);
+ for(uint8_t m=0; m < plen-1; m++) {
+ y *= _x;
+ y += p[m+1];
+ }
+ *array++ = y;
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ } else {
+ // o_x had better be a one-dimensional standard iterable
+ ndarray = ndarray_new_linear_array(mp_obj_get_int(mp_obj_len_maybe(o_x)), NDARRAY_FLOAT);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ mp_obj_iter_buf_t x_buf;
+ mp_obj_t x_item, x_iterable = mp_getiter(o_x, &x_buf);
+ while ((x_item = mp_iternext(x_iterable)) != MP_OBJ_STOP_ITERATION) {
+ mp_float_t _x = mp_obj_get_float(x_item);
+ mp_float_t y = p[0];
+ for(uint8_t j=0; j < plen-1; j++) {
+ y *= _x;
+ y += p[j+1];
+ }
+ *array++ = y;
+ }
+ }
+ m_del(mp_float_t, p, plen);
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(poly_polyval_obj, poly_polyval);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/poly.h b/circuitpython/extmod/ulab/code/numpy/poly.h
new file mode 100644
index 0000000..59cb9f5
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/poly.h
@@ -0,0 +1,21 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _POLY_
+#define _POLY_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(poly_polyfit_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(poly_polyval_obj);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/stats.c b/circuitpython/extmod/ulab/code/numpy/stats.c
new file mode 100644
index 0000000..2d34889
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/stats.c
@@ -0,0 +1,54 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Roberto Colistete Jr.
+ * 2020 Taku Fukada
+ *
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "stats.h"
+
+#if ULAB_MAX_DIMS > 1
+#if ULAB_NUMPY_HAS_TRACE
+
+//| def trace(m: ulab.numpy.ndarray) -> _float:
+//| """
+//| :param m: a square matrix
+//|
+//| Compute the trace of the matrix, the sum of its diagonal elements."""
+//| ...
+//|
+
+static mp_obj_t stats_trace(mp_obj_t oin) {
+ ndarray_obj_t *ndarray = tools_object_is_square(oin);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray->dtype)
+ mp_float_t trace = 0.0;
+ for(size_t i=0; i < ndarray->shape[ULAB_MAX_DIMS - 1]; i++) {
+ int32_t pos = i * (ndarray->strides[ULAB_MAX_DIMS - 1] + ndarray->strides[ULAB_MAX_DIMS - 2]);
+ trace += ndarray_get_float_index(ndarray->array, ndarray->dtype, pos/ndarray->itemsize);
+ }
+ if(ndarray->dtype == NDARRAY_FLOAT) {
+ return mp_obj_new_float(trace);
+ }
+ return mp_obj_new_int_from_float(trace);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(stats_trace_obj, stats_trace);
+#endif
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/stats.h b/circuitpython/extmod/ulab/code/numpy/stats.h
new file mode 100644
index 0000000..62bba9f
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/stats.h
@@ -0,0 +1,20 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _STATS_
+#define _STATS_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+MP_DECLARE_CONST_FUN_OBJ_1(stats_trace_obj);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/transform.c b/circuitpython/extmod/ulab/code/numpy/transform.c
new file mode 100644
index 0000000..f0e3e70
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/transform.c
@@ -0,0 +1,224 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ *
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "transform.h"
+
+#if ULAB_NUMPY_HAS_COMPRESS
+static mp_obj_t transform_compress(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_axis, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_obj_t condition = args[0].u_obj;
+ ndarray_obj_t *ndarray = MP_OBJ_TO_PTR(args[1].u_obj);
+ uint8_t *array = (uint8_t *)ndarray->array;
+ mp_obj_t axis = args[2].u_obj;
+
+ size_t len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len_maybe(condition));
+ int8_t ax, shift_ax;
+
+ if(axis != mp_const_none) {
+ ax = tools_get_axis(axis, ndarray->ndim);
+ shift_ax = ULAB_MAX_DIMS - ndarray->ndim + ax;
+ }
+
+ if(((axis == mp_const_none) && (len != ndarray->len)) ||
+ ((axis != mp_const_none) && (len != ndarray->shape[shift_ax]))) {
+ mp_raise_ValueError(translate("wrong length of condition array"));
+ }
+
+ size_t true_count = 0;
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t item, iterable = mp_getiter(condition, &iter_buf);
+ while((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ if(mp_obj_is_true(item)) {
+ true_count++;
+ }
+ }
+
+ iterable = mp_getiter(condition, &iter_buf);
+
+ ndarray_obj_t *result = NULL;
+ uint8_t *rarray = NULL;
+
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ memcpy(shape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
+
+ size_t *rshape = m_new(size_t, ULAB_MAX_DIMS);
+ memcpy(rshape, ndarray->shape, ULAB_MAX_DIMS * sizeof(size_t));
+
+ int32_t *strides = m_new(int32_t, ULAB_MAX_DIMS);
+ memcpy(strides, ndarray->strides, ULAB_MAX_DIMS * sizeof(int32_t));
+
+ int32_t *rstrides = m_new(int32_t, ULAB_MAX_DIMS);
+
+ if(axis == mp_const_none) {
+ result = ndarray_new_linear_array(true_count, ndarray->dtype);
+ rarray = (uint8_t *)result->array;
+ memset(rstrides, 0, ndarray->ndim * sizeof(int32_t));
+ rstrides[ULAB_MAX_DIMS - 1] = ndarray->itemsize;
+ rshape[ULAB_MAX_DIMS - 1] = 0;
+ } else {
+ rshape[shift_ax] = true_count;
+
+ result = ndarray_new_dense_ndarray(ndarray->ndim, rshape, ndarray->dtype);
+ rarray = (uint8_t *)result->array;
+
+ SWAP(size_t, shape[shift_ax], shape[ULAB_MAX_DIMS - 1]);
+ SWAP(size_t, rshape[shift_ax], rshape[ULAB_MAX_DIMS - 1]);
+ SWAP(int32_t, strides[shift_ax], strides[ULAB_MAX_DIMS - 1]);
+
+ memcpy(rstrides, result->strides, ULAB_MAX_DIMS * sizeof(int32_t));
+ SWAP(int32_t, rstrides[shift_ax], rstrides[ULAB_MAX_DIMS - 1]);
+ }
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ if(axis != mp_const_none) {
+ iterable = mp_getiter(condition, &iter_buf);
+ }
+ do {
+ item = mp_iternext(iterable);
+ if(mp_obj_is_true(item)) {
+ memcpy(rarray, array, ndarray->itemsize);
+ rarray += rstrides[ULAB_MAX_DIMS - 1];
+ }
+ array += strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ array -= strides[ULAB_MAX_DIMS - 1] * shape[ULAB_MAX_DIMS - 1];
+ array += strides[ULAB_MAX_DIMS - 2];
+ rarray -= rstrides[ULAB_MAX_DIMS - 1] * rshape[ULAB_MAX_DIMS - 1];
+ rarray += rstrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ array -= strides[ULAB_MAX_DIMS - 2] * shape[ULAB_MAX_DIMS - 2];
+ array += strides[ULAB_MAX_DIMS - 3];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ array -= strides[ULAB_MAX_DIMS - 3] * shape[ULAB_MAX_DIMS - 3];
+ array += strides[ULAB_MAX_DIMS - 4];
+ rarray -= rstrides[ULAB_MAX_DIMS - 2] * rshape[ULAB_MAX_DIMS - 2];
+ rarray += rstrides[ULAB_MAX_DIMS - 3];
+ i++;
+ } while(i < shape[ULAB_MAX_DIMS - 4]);
+ #endif
+
+ return result;
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(transform_compress_obj, 2, transform_compress);
+#endif /* ULAB_NUMPY_HAS_COMPRESS */
+
+#if ULAB_MAX_DIMS > 1
+#if ULAB_NUMPY_HAS_DOT
+//| def dot(m1: ulab.numpy.ndarray, m2: ulab.numpy.ndarray) -> Union[ulab.numpy.ndarray, _float]:
+//| """
+//| :param ~ulab.numpy.ndarray m1: a matrix, or a vector
+//| :param ~ulab.numpy.ndarray m2: a matrix, or a vector
+//|
+//| Computes the product of two matrices, or two vectors. In the letter case, the inner product is returned."""
+//| ...
+//|
+
+mp_obj_t transform_dot(mp_obj_t _m1, mp_obj_t _m2) {
+ // TODO: should the results be upcast?
+ // This implements 2D operations only!
+ if(!mp_obj_is_type(_m1, &ulab_ndarray_type) || !mp_obj_is_type(_m2, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("arguments must be ndarrays"));
+ }
+ ndarray_obj_t *m1 = MP_OBJ_TO_PTR(_m1);
+ ndarray_obj_t *m2 = MP_OBJ_TO_PTR(_m2);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(m1->dtype)
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(m2->dtype)
+
+ uint8_t *array1 = (uint8_t *)m1->array;
+ uint8_t *array2 = (uint8_t *)m2->array;
+
+ mp_float_t (*func1)(void *) = ndarray_get_float_function(m1->dtype);
+ mp_float_t (*func2)(void *) = ndarray_get_float_function(m2->dtype);
+
+ if(m1->shape[ULAB_MAX_DIMS - 1] != m2->shape[ULAB_MAX_DIMS - m2->ndim]) {
+ mp_raise_ValueError(translate("dimensions do not match"));
+ }
+ uint8_t ndim = MIN(m1->ndim, m2->ndim);
+ size_t shape1 = m1->ndim == 2 ? m1->shape[ULAB_MAX_DIMS - m1->ndim] : 1;
+ size_t shape2 = m2->ndim == 2 ? m2->shape[ULAB_MAX_DIMS - 1] : 1;
+
+ size_t *shape = NULL;
+ if(ndim == 2) { // matrix times matrix -> matrix
+ shape = ndarray_shape_vector(0, 0, shape1, shape2);
+ } else { // matrix times vector -> vector, vector times vector -> vector (size 1)
+ shape = ndarray_shape_vector(0, 0, 0, shape1);
+ }
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
+ mp_float_t *rarray = (mp_float_t *)results->array;
+
+ for(size_t i=0; i < shape1; i++) { // rows of m1
+ for(size_t j=0; j < shape2; j++) { // columns of m2
+ mp_float_t dot = 0.0;
+ for(size_t k=0; k < m1->shape[ULAB_MAX_DIMS - 1]; k++) {
+ // (i, k) * (k, j)
+ dot += func1(array1) * func2(array2);
+ array1 += m1->strides[ULAB_MAX_DIMS - 1];
+ array2 += m2->strides[ULAB_MAX_DIMS - m2->ndim];
+ }
+ *rarray++ = dot;
+ array1 -= m1->strides[ULAB_MAX_DIMS - 1] * m1->shape[ULAB_MAX_DIMS - 1];
+ array2 -= m2->strides[ULAB_MAX_DIMS - m2->ndim] * m2->shape[ULAB_MAX_DIMS - m2->ndim];
+ array2 += m2->strides[ULAB_MAX_DIMS - 1];
+ }
+ array1 += m1->strides[ULAB_MAX_DIMS - m1->ndim];
+ array2 = m2->array;
+ }
+ if((m1->ndim * m2->ndim) == 1) { // return a scalar, if product of two vectors
+ return mp_obj_new_float(*(--rarray));
+ } else {
+ return MP_OBJ_FROM_PTR(results);
+ }
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(transform_dot_obj, transform_dot);
+#endif
+#endif \ No newline at end of file
diff --git a/circuitpython/extmod/ulab/code/numpy/transform.h b/circuitpython/extmod/ulab/code/numpy/transform.h
new file mode 100644
index 0000000..039dcea
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/transform.h
@@ -0,0 +1,29 @@
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ *
+*/
+
+#ifndef _TRANSFORM_
+#define _TRANSFORM_
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "py/obj.h"
+#include "py/runtime.h"
+#include "py/misc.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "transform.h"
+
+MP_DECLARE_CONST_FUN_OBJ_KW(transform_compress_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(transform_dot_obj);
+
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/vector.c b/circuitpython/extmod/ulab/code/numpy/vector.c
new file mode 100644
index 0000000..97ab66d
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/vector.c
@@ -0,0 +1,844 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+ * 2020 Jeff Epler for Adafruit Industries
+ * 2020 Scott Shawcroft for Adafruit Industries
+ * 2020 Taku Fukada
+*/
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "py/runtime.h"
+#include "py/binary.h"
+#include "py/obj.h"
+#include "py/objarray.h"
+
+#include "../ulab.h"
+#include "../ulab_tools.h"
+#include "carray/carray_tools.h"
+#include "vector.h"
+
+//| """Element-by-element functions
+//|
+//| These functions can operate on numbers, 1-D iterables, and arrays of 1 to 4 dimensions by
+//| applying the function to every element in the array. This is typically
+//| much more efficient than expressing the same operation as a Python loop."""
+//|
+
+static mp_obj_t vector_generic_vector(mp_obj_t o_in, mp_float_t (*f)(mp_float_t)) {
+ // Return a single value, if o_in is not iterable
+ if(mp_obj_is_float(o_in) || mp_obj_is_int(o_in)) {
+ return mp_obj_new_float(f(mp_obj_get_float(o_in)));
+ }
+ ndarray_obj_t *ndarray = NULL;
+ if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
+ uint8_t *sarray = (uint8_t *)source->array;
+ ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+
+ #if ULAB_VECTORISE_USES_FUN_POINTER
+
+ mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t value = func(sarray);
+ *array++ = f(value);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ #else
+ if(source->dtype == NDARRAY_UINT8) {
+ ITERATE_VECTOR(uint8_t, array, source, sarray);
+ } else if(source->dtype == NDARRAY_INT8) {
+ ITERATE_VECTOR(int8_t, array, source, sarray);
+ } else if(source->dtype == NDARRAY_UINT16) {
+ ITERATE_VECTOR(uint16_t, array, source, sarray);
+ } else if(source->dtype == NDARRAY_INT16) {
+ ITERATE_VECTOR(int16_t, array, source, sarray);
+ } else {
+ ITERATE_VECTOR(mp_float_t, array, source, sarray);
+ }
+ #endif /* ULAB_VECTORISE_USES_FUN_POINTER */
+ } else {
+ ndarray = ndarray_from_mp_obj(o_in, 0);
+ mp_float_t *narray = (mp_float_t *)ndarray->array;
+ for(size_t i = 0; i < ndarray->len; i++) {
+ *narray = f(*narray);
+ narray++;
+ }
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+#if ULAB_NUMPY_HAS_ACOS
+//| def acos(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse cosine function"""
+//| ...
+//|
+
+MATH_FUN_1(acos, acos);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_acos_obj, vector_acos);
+#endif
+
+#if ULAB_NUMPY_HAS_ACOSH
+//| def acosh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse hyperbolic cosine function"""
+//| ...
+//|
+
+MATH_FUN_1(acosh, acosh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_acosh_obj, vector_acosh);
+#endif
+
+#if ULAB_NUMPY_HAS_ASIN
+//| def asin(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse sine function"""
+//| ...
+//|
+
+MATH_FUN_1(asin, asin);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_asin_obj, vector_asin);
+#endif
+
+#if ULAB_NUMPY_HAS_ASINH
+//| def asinh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse hyperbolic sine function"""
+//| ...
+//|
+
+MATH_FUN_1(asinh, asinh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_asinh_obj, vector_asinh);
+#endif
+
+#if ULAB_NUMPY_HAS_AROUND
+//| def around(a: _ArrayLike, *, decimals: int = 0) -> ulab.numpy.ndarray:
+//| """Returns a new float array in which each element is rounded to
+//| ``decimals`` places."""
+//| ...
+//|
+
+mp_obj_t vector_around(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
+ { MP_QSTR_decimals, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0 } }
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ if(!mp_obj_is_type(args[0].u_obj, &ulab_ndarray_type)) {
+ mp_raise_TypeError(translate("first argument must be an ndarray"));
+ }
+ int8_t n = args[1].u_int;
+ mp_float_t mul = MICROPY_FLOAT_C_FUN(pow)(10.0, n);
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0].u_obj);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_FLOAT);
+ mp_float_t *narray = (mp_float_t *)ndarray->array;
+ uint8_t *sarray = (uint8_t *)source->array;
+
+ mp_float_t (*func)(void *) = ndarray_get_float_function(source->dtype);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t f = func(sarray);
+ *narray++ = MICROPY_FLOAT_C_FUN(round)(f * mul) / mul;
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+ return MP_OBJ_FROM_PTR(ndarray);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(vector_around_obj, 1, vector_around);
+#endif
+
+#if ULAB_NUMPY_HAS_ATAN
+//| def atan(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse tangent function; the return values are in the
+//| range [-pi/2,pi/2]."""
+//| ...
+//|
+
+MATH_FUN_1(atan, atan);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_atan_obj, vector_atan);
+#endif
+
+#if ULAB_NUMPY_HAS_ARCTAN2
+//| def arctan2(ya: _ArrayLike, xa: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse tangent function of y/x; the return values are in
+//| the range [-pi, pi]."""
+//| ...
+//|
+
+mp_obj_t vector_arctan2(mp_obj_t y, mp_obj_t x) {
+ ndarray_obj_t *ndarray_x = ndarray_from_mp_obj(x, 0);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_x->dtype)
+
+ ndarray_obj_t *ndarray_y = ndarray_from_mp_obj(y, 0);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(ndarray_y->dtype)
+
+ uint8_t ndim = 0;
+ size_t *shape = m_new(size_t, ULAB_MAX_DIMS);
+ int32_t *xstrides = m_new(int32_t, ULAB_MAX_DIMS);
+ int32_t *ystrides = m_new(int32_t, ULAB_MAX_DIMS);
+ if(!ndarray_can_broadcast(ndarray_x, ndarray_y, &ndim, shape, xstrides, ystrides)) {
+ mp_raise_ValueError(translate("operands could not be broadcast together"));
+ m_del(size_t, shape, ULAB_MAX_DIMS);
+ m_del(int32_t, xstrides, ULAB_MAX_DIMS);
+ m_del(int32_t, ystrides, ULAB_MAX_DIMS);
+ }
+
+ uint8_t *xarray = (uint8_t *)ndarray_x->array;
+ uint8_t *yarray = (uint8_t *)ndarray_y->array;
+
+ ndarray_obj_t *results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT);
+ mp_float_t *rarray = (mp_float_t *)results->array;
+
+ mp_float_t (*funcx)(void *) = ndarray_get_float_function(ndarray_x->dtype);
+ mp_float_t (*funcy)(void *) = ndarray_get_float_function(ndarray_y->dtype);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t _x = funcx(xarray);
+ mp_float_t _y = funcy(yarray);
+ *rarray++ = MICROPY_FLOAT_C_FUN(atan2)(_y, _x);
+ xarray += xstrides[ULAB_MAX_DIMS - 1];
+ yarray += ystrides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < results->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ xarray -= xstrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ xarray += xstrides[ULAB_MAX_DIMS - 2];
+ yarray -= ystrides[ULAB_MAX_DIMS - 1] * results->shape[ULAB_MAX_DIMS-1];
+ yarray += ystrides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < results->shape[ULAB_MAX_DIMS - 2]);
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ xarray -= xstrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ xarray += xstrides[ULAB_MAX_DIMS - 3];
+ yarray -= ystrides[ULAB_MAX_DIMS - 2] * results->shape[ULAB_MAX_DIMS-2];
+ yarray += ystrides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < results->shape[ULAB_MAX_DIMS - 3]);
+ #endif
+ #if ULAB_MAX_DIMS > 3
+ xarray -= xstrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ xarray += xstrides[ULAB_MAX_DIMS - 4];
+ yarray -= ystrides[ULAB_MAX_DIMS - 3] * results->shape[ULAB_MAX_DIMS-3];
+ yarray += ystrides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < results->shape[ULAB_MAX_DIMS - 4]);
+ #endif
+
+ return MP_OBJ_FROM_PTR(results);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_2(vector_arctan2_obj, vector_arctan2);
+#endif /* ULAB_VECTORISE_HAS_ARCTAN2 */
+
+#if ULAB_NUMPY_HAS_ATANH
+//| def atanh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the inverse hyperbolic tangent function"""
+//| ...
+//|
+
+MATH_FUN_1(atanh, atanh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_atanh_obj, vector_atanh);
+#endif
+
+#if ULAB_NUMPY_HAS_CEIL
+//| def ceil(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Rounds numbers up to the next whole number"""
+//| ...
+//|
+
+MATH_FUN_1(ceil, ceil);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_ceil_obj, vector_ceil);
+#endif
+
+#if ULAB_NUMPY_HAS_COS
+//| def cos(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the cosine function"""
+//| ...
+//|
+
+MATH_FUN_1(cos, cos);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_cos_obj, vector_cos);
+#endif
+
+#if ULAB_NUMPY_HAS_COSH
+//| def cosh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the hyperbolic cosine function"""
+//| ...
+//|
+
+MATH_FUN_1(cosh, cosh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_cosh_obj, vector_cosh);
+#endif
+
+#if ULAB_NUMPY_HAS_DEGREES
+//| def degrees(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Converts angles from radians to degrees"""
+//| ...
+//|
+
+static mp_float_t vector_degrees_(mp_float_t value) {
+ return value * MICROPY_FLOAT_CONST(180.0) / MP_PI;
+}
+
+static mp_obj_t vector_degrees(mp_obj_t x_obj) {
+ return vector_generic_vector(x_obj, vector_degrees_);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(vector_degrees_obj, vector_degrees);
+#endif
+
+#if ULAB_SCIPY_SPECIAL_HAS_ERF
+//| def erf(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the error function, which has applications in statistics"""
+//| ...
+//|
+
+MATH_FUN_1(erf, erf);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_erf_obj, vector_erf);
+#endif
+
+#if ULAB_SCIPY_SPECIAL_HAS_ERFC
+//| def erfc(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the complementary error function, which has applications in statistics"""
+//| ...
+//|
+
+MATH_FUN_1(erfc, erfc);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_erfc_obj, vector_erfc);
+#endif
+
+#if ULAB_NUMPY_HAS_EXP
+//| def exp(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the exponent function."""
+//| ...
+//|
+
+static mp_obj_t vector_exp(mp_obj_t o_in) {
+ #if ULAB_SUPPORTS_COMPLEX
+ if(mp_obj_is_type(o_in, &mp_type_complex)) {
+ mp_float_t real, imag;
+ mp_obj_get_complex(o_in, &real, &imag);
+ mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
+ return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag));
+ } else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
+ if(source->dtype == NDARRAY_COMPLEX) {
+ uint8_t *sarray = (uint8_t *)source->array;
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ uint8_t itemsize = sizeof(mp_float_t);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t real = *(mp_float_t *)sarray;
+ mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
+ mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
+ *array++ = exp_real * MICROPY_FLOAT_C_FUN(cos)(imag);
+ *array++ = exp_real * MICROPY_FLOAT_C_FUN(sin)(imag);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ return MP_OBJ_FROM_PTR(ndarray);
+ }
+ }
+ #endif
+ return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(exp));
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(vector_exp_obj, vector_exp);
+#endif
+
+#if ULAB_NUMPY_HAS_EXPM1
+//| def expm1(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes $e^x-1$. In certain applications, using this function preserves numeric accuracy better than the `exp` function."""
+//| ...
+//|
+
+MATH_FUN_1(expm1, expm1);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_expm1_obj, vector_expm1);
+#endif
+
+#if ULAB_NUMPY_HAS_FLOOR
+//| def floor(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Rounds numbers up to the next whole number"""
+//| ...
+//|
+
+MATH_FUN_1(floor, floor);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_floor_obj, vector_floor);
+#endif
+
+#if ULAB_SCIPY_SPECIAL_HAS_GAMMA
+//| def gamma(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the gamma function"""
+//| ...
+//|
+
+MATH_FUN_1(gamma, tgamma);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_gamma_obj, vector_gamma);
+#endif
+
+#if ULAB_SCIPY_SPECIAL_HAS_GAMMALN
+//| def lgamma(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the natural log of the gamma function"""
+//| ...
+//|
+
+MATH_FUN_1(lgamma, lgamma);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_lgamma_obj, vector_lgamma);
+#endif
+
+#if ULAB_NUMPY_HAS_LOG
+//| def log(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the natural log"""
+//| ...
+//|
+
+MATH_FUN_1(log, log);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_log_obj, vector_log);
+#endif
+
+#if ULAB_NUMPY_HAS_LOG10
+//| def log10(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the log base 10"""
+//| ...
+//|
+
+MATH_FUN_1(log10, log10);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_log10_obj, vector_log10);
+#endif
+
+#if ULAB_NUMPY_HAS_LOG2
+//| def log2(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the log base 2"""
+//| ...
+//|
+
+MATH_FUN_1(log2, log2);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_log2_obj, vector_log2);
+#endif
+
+#if ULAB_NUMPY_HAS_RADIANS
+//| def radians(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Converts angles from degrees to radians"""
+//| ...
+//|
+
+static mp_float_t vector_radians_(mp_float_t value) {
+ return value * MP_PI / MICROPY_FLOAT_CONST(180.0);
+}
+
+static mp_obj_t vector_radians(mp_obj_t x_obj) {
+ return vector_generic_vector(x_obj, vector_radians_);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_1(vector_radians_obj, vector_radians);
+#endif
+
+#if ULAB_NUMPY_HAS_SIN
+//| def sin(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the sine function"""
+//| ...
+//|
+
+MATH_FUN_1(sin, sin);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_sin_obj, vector_sin);
+#endif
+
+#if ULAB_NUMPY_HAS_SINH
+//| def sinh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the hyperbolic sine"""
+//| ...
+//|
+
+MATH_FUN_1(sinh, sinh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_sinh_obj, vector_sinh);
+#endif
+
+
+#if ULAB_NUMPY_HAS_SQRT
+//| def sqrt(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the square root"""
+//| ...
+//|
+
+#if ULAB_SUPPORTS_COMPLEX
+mp_obj_t vector_sqrt(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, { .u_rom_obj = mp_const_none } },
+ { MP_QSTR_dtype, MP_ARG_KW_ONLY | MP_ARG_OBJ, { .u_rom_obj = MP_ROM_INT(NDARRAY_FLOAT) } },
+ };
+
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+
+ mp_obj_t o_in = args[0].u_obj;
+ uint8_t dtype = mp_obj_get_int(args[1].u_obj);
+ if((dtype != NDARRAY_FLOAT) && (dtype != NDARRAY_COMPLEX)) {
+ mp_raise_TypeError(translate("dtype must be float, or complex"));
+ }
+
+ if(mp_obj_is_type(o_in, &mp_type_complex)) {
+ mp_float_t real, imag;
+ mp_obj_get_complex(o_in, &real, &imag);
+ mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
+ sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
+ mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
+ return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta));
+ } else if(mp_obj_is_type(o_in, &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(o_in);
+ if((source->dtype == NDARRAY_COMPLEX) && (dtype == NDARRAY_FLOAT)) {
+ mp_raise_TypeError(translate("can't convert complex to float"));
+ }
+
+ if(dtype == NDARRAY_COMPLEX) {
+ if(source->dtype == NDARRAY_COMPLEX) {
+ uint8_t *sarray = (uint8_t *)source->array;
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+ uint8_t itemsize = sizeof(mp_float_t);
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t real = *(mp_float_t *)sarray;
+ mp_float_t imag = *(mp_float_t *)(sarray + itemsize);
+ mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag);
+ sqrt_abs = MICROPY_FLOAT_C_FUN(sqrt)(sqrt_abs);
+ mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
+ *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta);
+ *array++ = sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta);
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else if(source->dtype == NDARRAY_FLOAT) {
+ uint8_t *sarray = (uint8_t *)source->array;
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, NDARRAY_COMPLEX);
+ mp_float_t *array = (mp_float_t *)ndarray->array;
+
+ #if ULAB_MAX_DIMS > 3
+ size_t i = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 2
+ size_t j = 0;
+ do {
+ #endif
+ #if ULAB_MAX_DIMS > 1
+ size_t k = 0;
+ do {
+ #endif
+ size_t l = 0;
+ do {
+ mp_float_t value = *(mp_float_t *)sarray;
+ if(value >= MICROPY_FLOAT_CONST(0.0)) {
+ *array++ = MICROPY_FLOAT_C_FUN(sqrt)(value);
+ array++;
+ } else {
+ array++;
+ *array++ = MICROPY_FLOAT_C_FUN(sqrt)(-value);
+ }
+ sarray += source->strides[ULAB_MAX_DIMS - 1];
+ l++;
+ } while(l < source->shape[ULAB_MAX_DIMS - 1]);
+ #if ULAB_MAX_DIMS > 1
+ sarray -= source->strides[ULAB_MAX_DIMS - 1] * source->shape[ULAB_MAX_DIMS-1];
+ sarray += source->strides[ULAB_MAX_DIMS - 2];
+ k++;
+ } while(k < source->shape[ULAB_MAX_DIMS - 2]);
+ #endif /* ULAB_MAX_DIMS > 1 */
+ #if ULAB_MAX_DIMS > 2
+ sarray -= source->strides[ULAB_MAX_DIMS - 2] * source->shape[ULAB_MAX_DIMS-2];
+ sarray += source->strides[ULAB_MAX_DIMS - 3];
+ j++;
+ } while(j < source->shape[ULAB_MAX_DIMS - 3]);
+ #endif /* ULAB_MAX_DIMS > 2 */
+ #if ULAB_MAX_DIMS > 3
+ sarray -= source->strides[ULAB_MAX_DIMS - 3] * source->shape[ULAB_MAX_DIMS-3];
+ sarray += source->strides[ULAB_MAX_DIMS - 4];
+ i++;
+ } while(i < source->shape[ULAB_MAX_DIMS - 4]);
+ #endif /* ULAB_MAX_DIMS > 3 */
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else {
+ mp_raise_TypeError(translate("input dtype must be float or complex"));
+ }
+ }
+ }
+ return vector_generic_vector(o_in, MICROPY_FLOAT_C_FUN(sqrt));
+}
+MP_DEFINE_CONST_FUN_OBJ_KW(vector_sqrt_obj, 1, vector_sqrt);
+#else
+MATH_FUN_1(sqrt, sqrt);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_sqrt_obj, vector_sqrt);
+#endif /* ULAB_SUPPORTS_COMPLEX */
+
+#endif /* ULAB_NUMPY_HAS_SQRT */
+
+#if ULAB_NUMPY_HAS_TAN
+//| def tan(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the tangent"""
+//| ...
+//|
+
+MATH_FUN_1(tan, tan);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_tan_obj, vector_tan);
+#endif
+
+#if ULAB_NUMPY_HAS_TANH
+//| def tanh(a: _ArrayLike) -> ulab.numpy.ndarray:
+//| """Computes the hyperbolic tangent"""
+//| ...
+
+MATH_FUN_1(tanh, tanh);
+MP_DEFINE_CONST_FUN_OBJ_1(vector_tanh_obj, vector_tanh);
+#endif
+
+#if ULAB_NUMPY_HAS_VECTORIZE
+static mp_obj_t vector_vectorized_function_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
+ (void) n_args;
+ (void) n_kw;
+ vectorized_function_obj_t *self = MP_OBJ_TO_PTR(self_in);
+ mp_obj_t avalue[1];
+ mp_obj_t fvalue;
+ if(mp_obj_is_type(args[0], &ulab_ndarray_type)) {
+ ndarray_obj_t *source = MP_OBJ_TO_PTR(args[0]);
+ COMPLEX_DTYPE_NOT_IMPLEMENTED(source->dtype)
+ ndarray_obj_t *ndarray = ndarray_new_dense_ndarray(source->ndim, source->shape, self->otypes);
+ for(size_t i=0; i < source->len; i++) {
+ avalue[0] = mp_binary_get_val_array(source->dtype, source->array, i);
+ fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, avalue);
+ ndarray_set_value(self->otypes, ndarray->array, i, fvalue);
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else if(mp_obj_is_type(args[0], &mp_type_tuple) || mp_obj_is_type(args[0], &mp_type_list) ||
+ mp_obj_is_type(args[0], &mp_type_range)) { // i.e., the input is a generic iterable
+ size_t len = (size_t)mp_obj_get_int(mp_obj_len_maybe(args[0]));
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(len, self->otypes);
+ mp_obj_iter_buf_t iter_buf;
+ mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
+ size_t i=0;
+ while ((avalue[0] = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
+ fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, avalue);
+ ndarray_set_value(self->otypes, ndarray->array, i, fvalue);
+ i++;
+ }
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else if(mp_obj_is_int(args[0]) || mp_obj_is_float(args[0])) {
+ ndarray_obj_t *ndarray = ndarray_new_linear_array(1, self->otypes);
+ fvalue = self->type->MP_TYPE_CALL(self->fun, 1, 0, args);
+ ndarray_set_value(self->otypes, ndarray->array, 0, fvalue);
+ return MP_OBJ_FROM_PTR(ndarray);
+ } else {
+ mp_raise_ValueError(translate("wrong input type"));
+ }
+ return mp_const_none;
+}
+
+const mp_obj_type_t vector_function_type = {
+ { &mp_type_type },
+ .flags = MP_TYPE_FLAG_EXTENDED,
+ .name = MP_QSTR_,
+ MP_TYPE_EXTENDED_FIELDS(
+ .call = vector_vectorized_function_call,
+ )
+};
+
+//| def vectorize(
+//| f: Union[Callable[[int], _float], Callable[[_float], _float]],
+//| *,
+//| otypes: Optional[_DType] = None
+//| ) -> Callable[[_ArrayLike], ulab.numpy.ndarray]:
+//| """
+//| :param callable f: The function to wrap
+//| :param otypes: List of array types that may be returned by the function. None is interpreted to mean the return value is float.
+//|
+//| Wrap a Python function ``f`` so that it can be applied to arrays.
+//| The callable must return only values of the types specified by ``otypes``, or the result is undefined."""
+//| ...
+//|
+
+static mp_obj_t vector_vectorize(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
+ static const mp_arg_t allowed_args[] = {
+ { MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} },
+ { MP_QSTR_otypes, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }
+ };
+ mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
+ mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
+ const mp_obj_type_t *type = mp_obj_get_type(args[0].u_obj);
+ if(mp_type_get_call_slot(type) == NULL) {
+ mp_raise_TypeError(translate("first argument must be a callable"));
+ }
+ mp_obj_t _otypes = args[1].u_obj;
+ uint8_t otypes = NDARRAY_FLOAT;
+ if(_otypes == mp_const_none) {
+ // TODO: is this what numpy does?
+ otypes = NDARRAY_FLOAT;
+ } else if(mp_obj_is_int(_otypes)) {
+ otypes = mp_obj_get_int(_otypes);
+ if(otypes != NDARRAY_FLOAT && otypes != NDARRAY_UINT8 && otypes != NDARRAY_INT8 &&
+ otypes != NDARRAY_UINT16 && otypes != NDARRAY_INT16) {
+ mp_raise_ValueError(translate("wrong output type"));
+ }
+ }
+ else {
+ mp_raise_ValueError(translate("wrong output type"));
+ }
+ vectorized_function_obj_t *function = m_new_obj(vectorized_function_obj_t);
+ function->base.type = &vector_function_type;
+ function->otypes = otypes;
+ function->fun = args[0].u_obj;
+ function->type = type;
+ return MP_OBJ_FROM_PTR(function);
+}
+
+MP_DEFINE_CONST_FUN_OBJ_KW(vector_vectorize_obj, 1, vector_vectorize);
+#endif
diff --git a/circuitpython/extmod/ulab/code/numpy/vector.h b/circuitpython/extmod/ulab/code/numpy/vector.h
new file mode 100644
index 0000000..ea38b0f
--- /dev/null
+++ b/circuitpython/extmod/ulab/code/numpy/vector.h
@@ -0,0 +1,161 @@
+
+/*
+ * This file is part of the micropython-ulab project,
+ *
+ * https://github.com/v923z/micropython-ulab
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019-2021 Zoltán Vörös
+*/
+
+#ifndef _VECTOR_
+#define _VECTOR_
+
+#include "../ulab.h"
+#include "../ndarray.h"
+
+MP_DECLARE_CONST_FUN_OBJ_1(vector_acos_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_acosh_obj);
+MP_DECLARE_CONST_FUN_OBJ_2(vector_arctan2_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(vector_around_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_asin_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_asinh_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_atan_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_atanh_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_ceil_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_cos_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_cosh_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_degrees_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_erf_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_erfc_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_exp_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_expm1_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_floor_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_gamma_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_lgamma_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_log_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_log10_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_log2_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_radians_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_sin_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_sinh_obj);
+#if ULAB_SUPPORTS_COMPLEX
+MP_DECLARE_CONST_FUN_OBJ_KW(vector_sqrt_obj);
+#else
+MP_DECLARE_CONST_FUN_OBJ_1(vector_sqrt_obj);
+#endif
+MP_DECLARE_CONST_FUN_OBJ_1(vector_tan_obj);
+MP_DECLARE_CONST_FUN_OBJ_1(vector_tanh_obj);
+MP_DECLARE_CONST_FUN_OBJ_KW(vector_vectorize_obj);
+
+typedef struct _vectorized_function_obj_t {
+ mp_obj_base_t base;
+ uint8_t otypes;
+ mp_obj_t fun;
+ const mp_obj_type_t *type;
+} vectorized_function_obj_t;
+
+#if ULAB_HAS_FUNCTION_ITERATOR
+#define ITERATE_VECTOR(type, array, source, sarray, shift)\
+({\
+ size_t *scoords = ndarray_new_coords((source)->ndim);\
+ for(size_t i=0; i < (source)->len/(source)->shape[ULAB_MAX_DIMS -1]; i++) {\
+ for(size_t l=0; l < (source)->shape[ULAB_MAX_DIMS - 1]; l++) {\
+ *(array) = f(*((type *)(sarray)));\
+ (array) += (shift);\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
+ }\
+ ndarray_rewind_array((source)->ndim, sarray, (source)->shape, (source)->strides, scoords);\
+ }\
+})
+
+#else
+
+#if ULAB_MAX_DIMS == 4
+#define ITERATE_VECTOR(type, array, source, sarray) do {\
+ size_t i=0;\
+ do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *(array)++ = f(*((type *)(sarray)));\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 3] * (source)->shape[ULAB_MAX_DIMS-3];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 4];\
+ i++;\
+ } while(i < (source)->shape[ULAB_MAX_DIMS-4]);\
+} while(0)
+#endif /* ULAB_MAX_DIMS == 4 */
+
+#if ULAB_MAX_DIMS == 3
+#define ITERATE_VECTOR(type, array, source, sarray) do {\
+ size_t j = 0;\
+ do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *(array)++ = f(*((type *)(sarray)));\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 2] * (source)->shape[ULAB_MAX_DIMS-2];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 3];\
+ j++;\
+ } while(j < (source)->shape[ULAB_MAX_DIMS-3]);\
+} while(0)
+#endif /* ULAB_MAX_DIMS == 3 */
+
+#if ULAB_MAX_DIMS == 2
+#define ITERATE_VECTOR(type, array, source, sarray) do {\
+ size_t k = 0;\
+ do {\
+ size_t l = 0;\
+ do {\
+ *(array)++ = f(*((type *)(sarray)));\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
+ (sarray) -= (source)->strides[ULAB_MAX_DIMS - 1] * (source)->shape[ULAB_MAX_DIMS-1];\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 2];\
+ k++;\
+ } while(k < (source)->shape[ULAB_MAX_DIMS-2]);\
+} while(0)
+#endif /* ULAB_MAX_DIMS == 2 */
+
+#if ULAB_MAX_DIMS == 1
+#define ITERATE_VECTOR(type, array, source, sarray) do {\
+ size_t l = 0;\
+ do {\
+ *(array)++ = f(*((type *)(sarray)));\
+ (sarray) += (source)->strides[ULAB_MAX_DIMS - 1];\
+ l++;\
+ } while(l < (source)->shape[ULAB_MAX_DIMS-1]);\
+} while(0)
+#endif /* ULAB_MAX_DIMS == 1 */
+#endif /* ULAB_HAS_FUNCTION_ITERATOR */
+
+#define MATH_FUN_1(py_name, c_name) \
+ static mp_obj_t vector_ ## py_name(mp_obj_t x_obj) { \
+ return vector_generic_vector(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
+}
+
+#endif /* _VECTOR_ */