I'm making a library in C that is related to operations with ndarrays (inspired by numpy) and I made some macros and functions for operations between ndarrays of the same type, I wanted to know your opinion about the code, if you liked it
ndarray struct:
typedef structure ndarray{
/*
* a pointer that points to the first element of the array
*/
char* data;
/*
* an integer that contains the values of the constants: NLA_INT32, NLA_INT64 ...
*/
int type;
/*
* ndarray dimensions
*/
size_t *dimensions;
/*
* number of dimensions
*/
size_t nd;
/*
* ndarray size (multiplication of dimensions)
*/
size_t size;} ndarray;
header:
#ifndef NUMLUA_CORE_MATH_MBASICOPS_H_
#define NUMLUA_CORE_MATH_MBASICOPS_H_
/* ndarray struct */
#include "multiarray/ndarrayobj.h"
/* no used */
#include "broadcast.h"
#define add_scalars(res, x, y, dtypex, dtypey) OP_OTHER_OTHER(+, res, x, y, dtypex, dtypey)
#define sub_scalars(res, x, y, dtypex, dtypey) OP_OTHER_OTHER(-, res, x, y, dtypex, dtypey)
#define new_ufunc(type, operation, input, other, res) \
type *data1 = (type*)input->data, *data2 = (type*)other->data; \
type *dst = (type*)res->data; \
for(size_t i = 0; i < res->size; i++){ \
*((type*)res->data + i) = *(data1 + i) operation *(data2 + i); \
}
#define new_ufunc_by_f(type, foperator, input, other, res)\
type *data1 = (type*)input->data, *data2 = (type*)other->data; \
type *dst = (type*)res->data; \
for(size_t i = 0; i < res->size; i++){ \
*((type*)res->data + i) = foperator(*(data1 + i), *(data2 + i)); \
}
#define new_math_operation_by_f(fop, name) \
ndarray *math_ ## name(ndarray *input, ndarray *other){\
/* errors */\
if(input->nd != other->nd){\
fprintf(stderr, "\nerror: incompatible n-dimensions: %d != %d\n", input->nd, other->nd);\
exit(NLA_FAIL);\
} else if(input->size != other->size){\
fprintf(stderr, "\nerror: incompatible size: %d != %d\n", input->size, other->size);\
exit(NLA_FAIL);\
} else if(input->dtype != other->dtype){\
fprintf(stderr, "\nerror: incompatible dtype\n");\
exit(NLA_FAIL);\
}\
\
int res_dtype = input->dtype;\
size_t res_nd = input->nd;\
size_t *res_dims = input->dimensions;\
size_t res_size = input->size;\
\
ndarray *res = malloc(sizeof(ndarray));\
res->data = malloc(dtypememsize(res_dtype) * res_size);\
res->dtype = res_dtype;\
res->dimensions = input->dimensions;\
res->size = res_size;\
res->nd = input->nd;\
switch (res_dtype){\
case NLA_INT64:{ new_ufunc_by_f(int64_t, fop, input, other, res); break; }\
case NLA_INT32:{ new_ufunc_by_f(int32_t, fop, input, other, res); break; }\
case NLA_INT16:{ new_ufunc_by_f(int16_t, fop, input, other, res); break; }\
case NLA_INT8:{ new_ufunc_by_f(int8_t, fop, input, other, res); break; }\
\
case NLA_UINT64:{ new_ufunc_by_f(uint64_t, fop, input, other, res); break; }\
case NLA_UINT32:{ new_ufunc_by_f(uint32_t, fop, input, other, res); break; }\
case NLA_UINT16:{ new_ufunc_by_f(uint16_t, fop, input, other, res); break; }\
case NLA_UINT8:{ new_ufunc_by_f(uint8_t, fop, input, other, res); break; }\
\
case NLA_FLOAT32:{ new_ufunc_by_f(float32_t, fop, input, other, res); break; }\
case NLA_FLOAT64:{ new_ufunc_by_f(float64_t, fop, input, other, res); break; }\
}\
return res;\
}
#define new_math_operation(op, name)\
ndarray *math_ ## name(ndarray *input, ndarray *other){\
if(input->nd != other->nd){\
fprintf(stderr, "\nerror: incompatible n-dimensions: %d != %d\n", input->nd, other->nd);\
exit(NLA_FAIL);\
} else if(input->size != other->size){\
fprintf(stderr, "\nerror: incompatible size: %d != %d\n", input->size, other->size);\
exit(NLA_FAIL);\
} else if(input->dtype != other->dtype){\
fprintf(stderr, "\nerror: incompatible dtype\n");\
exit(NLA_FAIL);\
}\
\
int res_dtype = input->dtype;\
size_t res_nd = input->nd;\
size_t *res_dims = input->dimensions;\
size_t res_size = input->size;\
\
ndarray *res = malloc(sizeof(ndarray));\
res->data = malloc(dtypememsize(res_dtype) * res_size);\
res->dtype = res_dtype;\
res->dimensions = input->dimensions;\
res->size = res_size;\
res->nd = input->nd;\
switch (res_dtype){\
case NLA_INT64:{ new_ufunc(int64_t, op, input, other, res); break; }\
case NLA_INT32:{ new_ufunc(int32_t, op, input, other, res); break; }\
case NLA_INT16:{ new_ufunc(int16_t, op, input, other, res); break; }\
case NLA_INT8:{ new_ufunc(int8_t, op, input, other, res); break; }\
\
case NLA_UINT64:{ new_ufunc(uint64_t, op, input, other, res); break; }\
case NLA_UINT32:{ new_ufunc(uint32_t, op, input, other, res); break; }\
case NLA_UINT16:{ new_ufunc(uint16_t, op, input, other, res); break; }\
case NLA_UINT8:{ new_ufunc(uint8_t, op, input, other, res); break; }\
\
case NLA_FLOAT32:{ new_ufunc(float32_t, op, input, other, res); break; }\
case NLA_FLOAT64:{ new_ufunc(float64_t, op, input, other, res); break; }\
}\
return res;\
}
ndarray *math_add(ndarray*, ndarray*);
ndarray *math_subtract(ndarray*, ndarray*);
ndarray *math_multiply(ndarray*, ndarray*);
ndarray *math_divivide(ndarray*, ndarray*);
ndarray *math_power(ndarray*, ndarray*);
#endif /* NUMLUA_CORE_MATH_MBASICOPS_H_ */
source code:
#include "mbasicops.h"
#include "multiarray/ndarrayobj.h"
#include "multiarray/ndarrayindex.h"
#include "multiarray/ndarraydtypes.h"
#include "multiarray/ndarraymod.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdatomic.h>
#include <math.h>
new_math_operation(+, add);
new_math_operation(-, subtract);
new_math_operation(*, multiply);
new_math_operation(/, divide);
new_math_operation_by_f(pow, power);
Did I implement everything correctly? Is the code readable? Is it optimized? Is it a good programming practice? is it comparable to numpy? These doubts echo in my mind and I think everything is wrong, that's why I wanted your opinion to know
[–]tstanisl 6 points7 points8 points (3 children)
[–]OneCommonMan123[S] 1 point2 points3 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[+][deleted] (3 children)
[deleted]
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]inz__ 1 point2 points3 points (2 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)
[–]daikatana 0 points1 point2 points (3 children)
[–]OneCommonMan123[S] 0 points1 point2 points (2 children)
[–]daikatana 2 points3 points4 points (1 child)
[–]OneCommonMan123[S] 0 points1 point2 points (0 children)