From a0999aba795ea63420790da3e8a39f9fb9cce4bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zolt=C3=A1n=20V=C3=B6r=C3=B6s?= Date: Mon, 4 Aug 2025 22:55:02 +0200 Subject: [PATCH] add modulo operator (#733) * add modulo operator * fix modulo loops * add in-place modulo operator * update readme --- README.md | 2 +- code/ndarray.c | 12 +++ code/ndarray_operators.c | 122 +++++++++++++++++++++++++++ code/ndarray_operators.h | 175 +++++++++++++++++++++++++++++++++++++++ code/ulab.c | 2 +- code/ulab.h | 8 ++ docs/ulab-ndarray.ipynb | 2 +- 7 files changed, 320 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 063c26d..72300f3 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ detected and handled. ## ndarray methods `ulab` implements `numpy`'s `ndarray` with the `==`, `!=`, `<`, `<=`, `>`, `>=`, `+`, `-`, `/`, `*`, `**`, -`+=`, `-=`, `*=`, `/=`, `**=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that +`%`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, and the `len`, `~`, `-`, `+`, `abs` unary operators that operate element-wise. Type-aware `ndarray`s can be initialised from any `micropython` iterable, lists of iterables via the `array` constructor, or by means of the `arange`, `concatenate`, `diag`, `eye`, `frombuffer`, `full`, `linspace`, `logspace`, `ones`, or `zeros` functions. diff --git a/code/ndarray.c b/code/ndarray.c index 489be2a..b297b3f 100644 --- a/code/ndarray.c +++ b/code/ndarray.c @@ -1648,6 +1648,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_inplace_ams(lhs, rhs, rstrides, op); break; #endif + #if NDARRAY_HAS_INPLACE_MODULO + case MP_BINARY_OP_INPLACE_MODULO: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); + return ndarray_inplace_modulo(lhs, rhs, rstrides); + break; + #endif #if NDARRAY_HAS_INPLACE_MULTIPLY case MP_BINARY_OP_INPLACE_MULTIPLY: COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); @@ -1703,6 +1709,12 @@ mp_obj_t ndarray_binary_op(mp_binary_op_t _op, mp_obj_t lobj, mp_obj_t robj) { return ndarray_binary_add(lhs, rhs, ndim, shape, lstrides, rstrides); break; #endif + #if NDARRAY_HAS_BINARY_OP_MODULO + case MP_BINARY_OP_MODULO: + COMPLEX_DTYPE_NOT_IMPLEMENTED(lhs->dtype); + return ndarray_binary_modulo(lhs, rhs, ndim, shape, lstrides, rstrides); + break; + #endif #if NDARRAY_HAS_BINARY_OP_MULTIPLY case MP_BINARY_OP_MULTIPLY: return ndarray_binary_multiply(lhs, rhs, ndim, shape, lstrides, rstrides); diff --git a/code/ndarray_operators.c b/code/ndarray_operators.c index e8de4d4..878fbc0 100644 --- a/code/ndarray_operators.c +++ b/code/ndarray_operators.c @@ -248,6 +248,105 @@ mp_obj_t ndarray_binary_add(ndarray_obj_t *lhs, ndarray_obj_t *rhs, } #endif /* NDARRAY_HAS_BINARY_OP_ADD */ +#if NDARRAY_HAS_BINARY_OP_MODULO +mp_obj_t ndarray_binary_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs, + uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { + + ndarray_obj_t *results = NULL; + uint8_t *larray = (uint8_t *)lhs->array; + uint8_t *rarray = (uint8_t *)rhs->array; + + if(lhs->dtype == NDARRAY_UINT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint8_t, uint8_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint8_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, uint8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, uint8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT8) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT8); + BINARY_LOOP(results, int8_t, int8_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int8_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, int8_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_UINT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT8); + BINARY_LOOP(results, uint16_t, uint16_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_UINT16); + BINARY_LOOP(results, uint16_t, uint16_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, uint16_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, uint16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_INT16) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, uint8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int8_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + BINARY_LOOP(results, mp_float_t, int16_t, uint16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_INT16); + BINARY_LOOP(results, int16_t, int16_t, int16_t, larray, lstrides, rarray, rstrides, %); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, int16_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } else if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT8) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int8_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, uint16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, int16_t, larray, lstrides, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_FLOAT) { + results = ndarray_new_dense_ndarray(ndim, shape, NDARRAY_FLOAT); + MODULO_FLOAT_LOOP(results, mp_float_t, mp_float_t, mp_float_t, larray, lstrides, rarray, rstrides); + } + } + + return MP_OBJ_FROM_PTR(results); +} +#endif /* NDARRAY_HAS_BINARY_OP_MODULO */ + #if NDARRAY_HAS_BINARY_OP_MULTIPLY mp_obj_t ndarray_binary_multiply(ndarray_obj_t *lhs, ndarray_obj_t *rhs, uint8_t ndim, size_t *shape, int32_t *lstrides, int32_t *rstrides) { @@ -1074,6 +1173,29 @@ mp_obj_t ndarray_inplace_ams(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rs } #endif /* NDARRAY_HAS_INPLACE_ADD || NDARRAY_HAS_INPLACE_MULTIPLY || NDARRAY_HAS_INPLACE_SUBTRACT */ + +#if NDARRAY_HAS_INPLACE_MODULO +mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { + if((lhs->dtype != NDARRAY_FLOAT) && (rhs->dtype == NDARRAY_FLOAT)) { + mp_raise_TypeError(MP_ERROR_TEXT("results cannot be cast to specified type")); + } + if(lhs->dtype == NDARRAY_FLOAT) { + if(rhs->dtype == NDARRAY_UINT8) { + INLINE_MODULO_FLOAT_LOOP(lhs, uint8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT8) { + INLINE_MODULO_FLOAT_LOOP(lhs, int8_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_UINT16) { + INLINE_MODULO_FLOAT_LOOP(lhs, uint16_t, larray, rarray, rstrides); + } else if(rhs->dtype == NDARRAY_INT16) { + INLINE_MODULO_FLOAT_LOOP(lhs, int16_t, larray, rarray, rstrides); + } else { + INLINE_MODULO_FLOAT_LOOP(lhs, mp_float_t, larray, rarray, rstrides); + } + } + return MP_OBJ_FROM_PTR(lhs); +} +#endif /* NDARRAY_HAS_INPLACE_MODULO */ + #if NDARRAY_HAS_INPLACE_TRUE_DIVIDE mp_obj_t ndarray_inplace_divide(ndarray_obj_t *lhs, ndarray_obj_t *rhs, int32_t *rstrides) { diff --git a/code/ndarray_operators.h b/code/ndarray_operators.h index f0f3c89..5f3b5d9 100644 --- a/code/ndarray_operators.h +++ b/code/ndarray_operators.h @@ -12,6 +12,7 @@ mp_obj_t ndarray_binary_equality(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); mp_obj_t ndarray_binary_add(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); +mp_obj_t ndarray_binary_modulo(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_multiply(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_binary_more(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *, mp_binary_op_t ); mp_obj_t ndarray_binary_power(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); @@ -21,6 +22,7 @@ mp_obj_t ndarray_binary_logical(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size mp_obj_t ndarray_binary_floor_divide(ndarray_obj_t *, ndarray_obj_t *, uint8_t , size_t *, int32_t *, int32_t *); mp_obj_t ndarray_inplace_ams(ndarray_obj_t *, ndarray_obj_t *, int32_t *, uint8_t ); +mp_obj_t ndarray_inplace_modulo(ndarray_obj_t *, ndarray_obj_t *, int32_t *); mp_obj_t ndarray_inplace_power(ndarray_obj_t *, ndarray_obj_t *, int32_t *); mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); @@ -537,3 +539,176 @@ mp_obj_t ndarray_inplace_divide(ndarray_obj_t *, ndarray_obj_t *, int32_t *); } while(0) #endif /* ULAB_MAX_DIMS == 4 */ + +#define MODULO_FLOAT1(results, array, type_left, type_right, larray, lstrides, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *(array)++ = MICROPY_FLOAT_C_FUN(fmod)(*((type_left *)(larray)), *((type_right *)(rarray)));\ + (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 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + MODULO_FLOAT1((results), (array), type_left, type_right, (larray), (lstrides), (rarray), (rstrides));\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + +#if ULAB_MAX_DIMS == 2 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, 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];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, 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];\ + l++;\ + } while(l < (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];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define MODULO_FLOAT_LOOP(results, type_out, type_left, type_right, larray, lstrides, rarray, rstrides) do {\ + type_out *array = (type_out *)(results)->array;\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + MODULO_FLOAT1((results), (array), type_left, 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];\ + l++;\ + } while(l < (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];\ + k++;\ + } while(k < (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];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 4 */ + + +#define INPLACE_MODULO_FLOAT1(results, type_right, larray, rarray, rstrides)\ +({\ + size_t l = 0;\ + do {\ + *((mp_float_t *)larray) = MICROPY_FLOAT_C_FUN(fmod)(*((mp_float_t *)(larray)), *((type_right *)(rarray)));\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 1];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 1]);\ +}) + + +#if ULAB_MAX_DIMS == 1 +#define INPLACE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ +} while(0) +#endif /* ULAB_MAX_DIMS == 1 */ + + +#if ULAB_MAX_DIMS == 2 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 2 */ + +#if ULAB_MAX_DIMS == 3 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 3 */ + +#if ULAB_MAX_DIMS == 4 +#define INLINE_MODULO_FLOAT_LOOP(results, type_right, larray, rarray, rstrides) do {\ + size_t j = 0;\ + do {\ + size_t k = 0;\ + do {\ + size_t l = 0;\ + do {\ + INPLACE_MODULO_FLOAT1((results), type_right, (larray), (rarray), (rstrides));\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 2];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 1] * (results)->shape[ULAB_MAX_DIMS - 1];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 2];\ + l++;\ + } while(l < (results)->shape[ULAB_MAX_DIMS - 2]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 3];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 2] * (results)->shape[ULAB_MAX_DIMS - 2];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 3];\ + k++;\ + } while(k < (results)->shape[ULAB_MAX_DIMS - 3]);\ + (larray) -= (results)->strides[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (larray) += (results)->strides[ULAB_MAX_DIMS - 4];\ + (rarray) -= (rstrides)[ULAB_MAX_DIMS - 3] * (results)->shape[ULAB_MAX_DIMS - 3];\ + (rarray) += (rstrides)[ULAB_MAX_DIMS - 4];\ + j++;\ + } while(j < (results)->shape[ULAB_MAX_DIMS - 4]);\ +} while(0) +#endif /* ULAB_MAX_DIMS == 4 */ \ No newline at end of file diff --git a/code/ulab.c b/code/ulab.c index fb42f6a..c1f32c0 100644 --- a/code/ulab.c +++ b/code/ulab.c @@ -33,7 +33,7 @@ #include "user/user.h" #include "utils/utils.h" -#define ULAB_VERSION 6.8.0 +#define ULAB_VERSION 6.9.0 #define xstr(s) str(s) #define str(s) #s diff --git a/code/ulab.h b/code/ulab.h index 38d4690..597cdef 100644 --- a/code/ulab.h +++ b/code/ulab.h @@ -117,6 +117,10 @@ #define NDARRAY_HAS_BINARY_OP_LESS_EQUAL (1) #endif +#ifndef NDARRAY_HAS_BINARY_OP_MODULO +#define NDARRAY_HAS_BINARY_OP_MODULO (1) +#endif + #ifndef NDARRAY_HAS_BINARY_OP_MORE #define NDARRAY_HAS_BINARY_OP_MORE (1) #endif @@ -161,6 +165,10 @@ #define NDARRAY_HAS_INPLACE_ADD (1) #endif +#ifndef NDARRAY_HAS_INPLACE_MODULO +#define NDARRAY_HAS_INPLACE_MODU (1) +#endif + #ifndef NDARRAY_HAS_INPLACE_MULTIPLY #define NDARRAY_HAS_INPLACE_MULTIPLY (1) #endif diff --git a/docs/ulab-ndarray.ipynb b/docs/ulab-ndarray.ipynb index 8d67ed9..c7518c2 100644 --- a/docs/ulab-ndarray.ipynb +++ b/docs/ulab-ndarray.ipynb @@ -2599,7 +2599,7 @@ "source": [ "# Binary operators\n", "\n", - "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", + "`ulab` implements the `+`, `-`, `*`, `/`, `**`, `%`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `+=`, `-=`, `*=`, `/=`, `**=`, `%=` binary operators, as well as the `AND`, `OR`, `XOR` bit-wise operators that work element-wise. Note that the bit-wise operators will raise an exception, if either of the operands is of `float` or `complex` type.\n", "\n", "Broadcasting is available, meaning that the two operands do not even have to have the same shape. If the lengths along the respective axes are equal, or one of them is 1, or the axis is missing, the element-wise operation can still be carried out. \n", "A thorough explanation of broadcasting can be found under https://numpy.org/doc/stable/user/basics.broadcasting.html. \n",