1 |
|
|
/* |
2 |
|
|
Copyright 2023 Joel Svensson svenssonjoel@yahoo.se |
3 |
|
|
|
4 |
|
|
This program is free software: you can redistribute it and/or modify |
5 |
|
|
it under the terms of the GNU General Public License as published by |
6 |
|
|
the Free Software Foundation, either version 3 of the License, or |
7 |
|
|
(at your option) any later version. |
8 |
|
|
|
9 |
|
|
This program is distributed in the hope that it will be useful, |
10 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
|
|
GNU General Public License for more details. |
13 |
|
|
|
14 |
|
|
You should have received a copy of the GNU General Public License |
15 |
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 |
|
|
*/ |
17 |
|
|
|
18 |
|
|
#include "extensions.h" |
19 |
|
|
#include "lbm_utils.h" |
20 |
|
|
#include "lbm_custom_type.h" |
21 |
|
|
|
22 |
|
|
#include <math.h> |
23 |
|
|
|
24 |
|
|
static const char *vector_float_desc = "Vector-Float"; |
25 |
|
|
static const char *matrix_float_desc = "Matrix-Float"; |
26 |
|
|
|
27 |
|
|
typedef struct { |
28 |
|
|
lbm_uint size; |
29 |
|
|
float data[1]; |
30 |
|
|
} vector_float_t; |
31 |
|
|
|
32 |
|
28 |
static bool common_destructor(lbm_uint value) { |
33 |
|
28 |
lbm_free((void*)value); |
34 |
|
28 |
return true; |
35 |
|
|
} |
36 |
|
|
|
37 |
|
420 |
static lbm_value vector_float_allocate(lbm_uint size) { |
38 |
|
420 |
vector_float_t *mem = lbm_malloc( 1 * sizeof(lbm_uint) + |
39 |
|
|
size * sizeof(float)); |
40 |
✗✓ |
420 |
if (!mem) return ENC_SYM_MERROR; |
41 |
|
420 |
mem->size = size; |
42 |
|
420 |
lbm_value res = 0; |
43 |
|
420 |
lbm_custom_type_create((lbm_uint)mem, |
44 |
|
|
common_destructor, |
45 |
|
|
vector_float_desc, |
46 |
|
|
&res); |
47 |
|
420 |
return res; |
48 |
|
|
} |
49 |
|
|
|
50 |
|
504 |
static bool is_vector_float(lbm_value v) { |
51 |
✓✓✓✗
|
504 |
return (lbm_is_custom(v) && ((lbm_uint)lbm_get_custom_descriptor(v) == (lbm_uint)vector_float_desc)); |
52 |
|
|
} |
53 |
|
|
|
54 |
|
|
/* ************************************************** |
55 |
|
|
* Matrices stored in row-major form |
56 |
|
|
*/ |
57 |
|
|
|
58 |
|
|
typedef struct { |
59 |
|
|
lbm_uint rows; |
60 |
|
|
lbm_uint cols; |
61 |
|
|
float data[1]; |
62 |
|
|
} matrix_float_t; |
63 |
|
|
|
64 |
|
28 |
static lbm_value matrix_float_allocate(unsigned int rows, unsigned int cols) { |
65 |
|
28 |
matrix_float_t *mem = lbm_malloc(1 * sizeof(lbm_uint) + |
66 |
|
28 |
1 * sizeof(lbm_uint) + |
67 |
|
28 |
rows * cols * sizeof(float)); |
68 |
✗✓ |
28 |
if (!mem) return ENC_SYM_MERROR; |
69 |
|
28 |
mem->rows = rows; |
70 |
|
28 |
mem->cols = cols; |
71 |
|
28 |
lbm_value res = 0; |
72 |
|
28 |
lbm_custom_type_create((lbm_uint)mem, |
73 |
|
|
common_destructor, |
74 |
|
|
matrix_float_desc, |
75 |
|
|
&res); |
76 |
|
28 |
return res; |
77 |
|
|
} |
78 |
|
|
|
79 |
|
28 |
static bool is_matrix_float(lbm_value m) { |
80 |
✓✗✓✗
|
28 |
return (lbm_is_custom(m) && (lbm_uint)lbm_get_custom_descriptor(m) == (lbm_uint)matrix_float_desc); |
81 |
|
|
} |
82 |
|
|
|
83 |
|
|
/* ************************************************** |
84 |
|
|
* Extension implementations |
85 |
|
|
*/ |
86 |
|
|
|
87 |
|
196 |
static lbm_value ext_vector(lbm_value *args, lbm_uint argn) { |
88 |
|
|
|
89 |
✗✓ |
196 |
LBM_CHECK_NUMBER_ALL(); |
90 |
|
|
|
91 |
✗✓ |
196 |
if (argn < 1) return ENC_SYM_TERROR; |
92 |
|
|
|
93 |
|
196 |
lbm_value vec = vector_float_allocate(argn); |
94 |
✗✓ |
196 |
if (lbm_is_error(vec)) return vec; |
95 |
|
|
|
96 |
|
196 |
vector_float_t *lvec = (vector_float_t*)lbm_get_custom_value(vec); |
97 |
|
|
|
98 |
✓✓ |
896 |
for (lbm_uint i = 0; i < argn; i ++) { |
99 |
|
700 |
lvec->data[i] = lbm_dec_as_float(args[i]); |
100 |
|
|
} |
101 |
|
196 |
return vec; |
102 |
|
|
} |
103 |
|
|
|
104 |
|
140 |
static lbm_value ext_list_to_vector(lbm_value *args, lbm_uint argn) { |
105 |
|
|
|
106 |
|
140 |
lbm_value res = ENC_SYM_TERROR; |
107 |
|
|
|
108 |
✓✗✓✗
|
280 |
if (argn == 1 && |
109 |
|
140 |
lbm_is_list(args[0])) { |
110 |
|
|
|
111 |
|
140 |
bool nums = true; |
112 |
|
140 |
unsigned int len = lbm_list_length_pred(args[0], &nums, lbm_is_number); |
113 |
|
|
|
114 |
✓✗✓✗
|
140 |
if (len > 0 && nums) { |
115 |
|
140 |
lbm_value vec = vector_float_allocate(len); |
116 |
✗✓ |
140 |
if (lbm_is_error(vec)) return vec; |
117 |
|
140 |
vector_float_t *lvec = (vector_float_t*)lbm_get_custom_value(vec); |
118 |
|
|
|
119 |
|
140 |
lbm_value curr = args[0]; |
120 |
|
140 |
unsigned int i = 0; |
121 |
✓✓ |
644 |
while (lbm_is_cons(curr)) { |
122 |
|
504 |
lvec->data[i] = lbm_dec_as_float(lbm_car(curr)); |
123 |
|
504 |
i ++; |
124 |
|
504 |
curr = lbm_cdr(curr); |
125 |
|
|
} |
126 |
|
140 |
res = vec; |
127 |
|
|
} |
128 |
|
|
} |
129 |
|
140 |
return res; |
130 |
|
|
} |
131 |
|
|
|
132 |
|
112 |
static lbm_value ext_vector_to_list(lbm_value *args, lbm_uint argn) { |
133 |
|
|
|
134 |
|
112 |
lbm_value res = ENC_SYM_TERROR; |
135 |
✓✗✓✗
|
112 |
if (argn == 1 && is_vector_float(args[0])) { |
136 |
|
112 |
vector_float_t *lvec = (vector_float_t*)lbm_get_custom_value(args[0]); |
137 |
|
|
|
138 |
|
112 |
lbm_value result = lbm_heap_allocate_list(lvec->size); |
139 |
✓✗ |
112 |
if (lbm_is_cons(result)) { |
140 |
|
112 |
lbm_value curr = result; |
141 |
✓✓ |
504 |
for (lbm_uint i = 0; i < lvec->size; i ++) { |
142 |
|
392 |
lbm_value f_val = lbm_enc_float(lvec->data[i]); |
143 |
✗✓ |
392 |
if (lbm_is_error(f_val)) { |
144 |
|
|
result = f_val; |
145 |
|
|
break; |
146 |
|
|
} |
147 |
|
392 |
lbm_set_car(curr, f_val); |
148 |
|
392 |
curr = lbm_cdr(curr); |
149 |
|
|
} |
150 |
|
112 |
res = result; |
151 |
|
|
} |
152 |
|
|
} |
153 |
|
112 |
return res; |
154 |
|
|
} |
155 |
|
|
|
156 |
|
112 |
static lbm_value ext_vproj(lbm_value *args, lbm_uint argn) { |
157 |
|
112 |
lbm_value res = ENC_SYM_TERROR; |
158 |
✓✗✓✗
|
224 |
if (argn == 2 && |
159 |
✓✗ |
224 |
is_vector_float(args[0]) && |
160 |
|
112 |
lbm_is_number(args[1])) { |
161 |
|
112 |
vector_float_t *vec = (vector_float_t*)lbm_get_custom_value(args[0]); |
162 |
|
112 |
uint32_t i = lbm_dec_as_u32(args[1]); |
163 |
✓✗ |
112 |
if (i < vec->size) { |
164 |
|
112 |
res = lbm_enc_float(vec->data[i]); |
165 |
|
|
} |
166 |
|
|
} |
167 |
|
112 |
return res; |
168 |
|
|
} |
169 |
|
|
|
170 |
|
56 |
static lbm_value ext_axpy(lbm_value *args, lbm_uint argn ) { |
171 |
|
|
|
172 |
|
56 |
lbm_value res = ENC_SYM_TERROR; |
173 |
|
|
|
174 |
✗✓ |
56 |
if (argn != 3) return res; |
175 |
|
56 |
lbm_value a = args[0]; |
176 |
|
56 |
lbm_value x = args[1]; |
177 |
|
56 |
lbm_value y = args[2]; |
178 |
|
|
|
179 |
✓✗✓✗ ✓✗ |
56 |
if (is_vector_float(x) && is_vector_float(y) && lbm_is_number(a)) { |
180 |
|
|
|
181 |
|
56 |
float alpha = lbm_dec_as_float(a); |
182 |
|
56 |
vector_float_t *X = (vector_float_t*)lbm_get_custom_value(x); |
183 |
|
56 |
vector_float_t *Y = (vector_float_t*)lbm_get_custom_value(y); |
184 |
|
|
|
185 |
✓✗ |
56 |
if (X->size == Y->size) { |
186 |
|
|
|
187 |
|
56 |
lbm_uint res_size = X->size; |
188 |
|
|
|
189 |
|
56 |
res = vector_float_allocate(res_size); |
190 |
✓✗ |
56 |
if (!lbm_is_symbol_merror(res)) { |
191 |
|
|
|
192 |
|
56 |
vector_float_t *R = (vector_float_t*)lbm_get_custom_value(res); |
193 |
|
|
|
194 |
✓✓ |
280 |
for (unsigned i = 0; i < res_size; i ++) { |
195 |
|
224 |
R->data[i] = alpha * X->data[i] + Y->data[i]; |
196 |
|
|
} |
197 |
|
|
} |
198 |
|
|
} |
199 |
|
|
} |
200 |
|
56 |
return res; |
201 |
|
|
} |
202 |
|
|
|
203 |
|
28 |
static lbm_value ext_dot(lbm_value *args, lbm_uint argn) { |
204 |
|
|
|
205 |
|
28 |
lbm_value res = ENC_SYM_TERROR; |
206 |
|
|
|
207 |
✗✓ |
28 |
if (argn != 2) return res; |
208 |
|
28 |
lbm_value x = args[0]; |
209 |
|
28 |
lbm_value y = args[1]; |
210 |
|
|
|
211 |
✓✗✓✗
|
28 |
if (is_vector_float(x) && is_vector_float(y)) { |
212 |
|
|
|
213 |
|
28 |
vector_float_t *X = (vector_float_t*)lbm_get_custom_value(x); |
214 |
|
28 |
vector_float_t *Y = (vector_float_t*)lbm_get_custom_value(y); |
215 |
|
|
|
216 |
✓✗ |
28 |
if (X->size == Y->size) { |
217 |
|
28 |
lbm_uint res_size = X->size; |
218 |
|
|
|
219 |
|
28 |
float f_res = 0; |
220 |
✓✓ |
112 |
for (unsigned i = 0; i < res_size; i ++) { |
221 |
|
84 |
f_res += X->data[i] * Y->data[i]; |
222 |
|
|
} |
223 |
|
28 |
res = lbm_enc_float(f_res); |
224 |
|
|
} |
225 |
|
|
} |
226 |
|
28 |
return res; |
227 |
|
|
} |
228 |
|
|
|
229 |
|
56 |
static float vector_float_mag(vector_float_t *v) { |
230 |
|
56 |
float mag = 0.0; |
231 |
✓✓ |
280 |
for (unsigned int i = 0; i < v->size; i ++) { |
232 |
|
224 |
mag += (v->data[i] * v->data[i]); |
233 |
|
|
} |
234 |
|
56 |
mag = sqrtf(mag); |
235 |
|
56 |
return mag; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
84 |
static lbm_value ext_mag(lbm_value *args, lbm_uint argn) { |
239 |
|
84 |
lbm_value res = ENC_SYM_TERROR; |
240 |
|
|
|
241 |
✓✗✓✓
|
168 |
if (argn == 1 && |
242 |
|
84 |
is_vector_float(args[0])) { |
243 |
|
56 |
vector_float_t *v = (vector_float_t *)lbm_get_custom_value(args[0]); |
244 |
|
56 |
float mag = vector_float_mag(v); |
245 |
|
56 |
res = lbm_enc_float(mag); |
246 |
|
|
} |
247 |
|
84 |
return res; |
248 |
|
|
} |
249 |
|
|
|
250 |
|
28 |
static lbm_value ext_vmult(lbm_value *args, lbm_uint argn) { |
251 |
|
28 |
lbm_value res = ENC_SYM_TERROR; |
252 |
✓✗✓✗
|
56 |
if (argn == 2 && |
253 |
✓✗ |
56 |
lbm_is_number(args[0]) && |
254 |
|
28 |
is_vector_float(args[1])) { |
255 |
|
|
|
256 |
|
28 |
float alpha = lbm_dec_as_float(args[0]); |
257 |
|
28 |
vector_float_t *x = (vector_float_t *)lbm_get_custom_value(args[1]); |
258 |
|
28 |
lbm_value y = vector_float_allocate(x->size); |
259 |
|
28 |
res = y; |
260 |
✓✗ |
28 |
if (!lbm_is_error(y)) { |
261 |
|
28 |
vector_float_t *y_vec = (vector_float_t *)lbm_get_custom_value(y); |
262 |
✓✓ |
112 |
for (unsigned int i = 0; i < x->size; i ++) { |
263 |
|
84 |
y_vec->data[i] = alpha * x->data[i]; |
264 |
|
|
} |
265 |
|
|
} |
266 |
|
|
} |
267 |
|
28 |
return res; |
268 |
|
|
} |
269 |
|
|
|
270 |
|
28 |
static lbm_value ext_list_to_matrix(lbm_value *args, lbm_uint argn) { |
271 |
|
|
|
272 |
|
28 |
lbm_value res = ENC_SYM_TERROR; |
273 |
|
|
|
274 |
✓✗✓✗
|
56 |
if (argn == 2 && |
275 |
✓✗ |
56 |
lbm_is_number(args[0]) && |
276 |
|
28 |
lbm_is_list(args[1])) { |
277 |
|
|
|
278 |
|
28 |
bool nums = true; |
279 |
|
28 |
unsigned int len = lbm_list_length_pred(args[1], &nums, lbm_is_number); |
280 |
|
|
|
281 |
✓✗✓✗
|
28 |
if (len > 0 && nums) { |
282 |
|
28 |
uint32_t cols = lbm_dec_as_u32(args[0]); |
283 |
|
28 |
uint32_t rows = len / cols; |
284 |
|
|
|
285 |
✓✗ |
28 |
if (len % cols == 0) { |
286 |
|
28 |
lbm_value mat = matrix_float_allocate(rows, cols); |
287 |
✗✓ |
28 |
if (lbm_is_error(mat)) return mat; |
288 |
|
28 |
matrix_float_t *lmat = (matrix_float_t*)lbm_get_custom_value(mat); |
289 |
|
|
|
290 |
|
28 |
lbm_value curr = args[1]; |
291 |
|
28 |
unsigned int i = 0; |
292 |
✓✓ |
280 |
while (lbm_is_cons(curr)) { |
293 |
|
252 |
lmat->data[i] = lbm_dec_as_float(lbm_car(curr)); |
294 |
|
252 |
i ++; |
295 |
|
252 |
curr = lbm_cdr(curr); |
296 |
|
|
} |
297 |
|
28 |
res = mat; |
298 |
|
|
} |
299 |
|
|
} |
300 |
|
|
} |
301 |
|
28 |
return res; |
302 |
|
|
} |
303 |
|
|
|
304 |
|
28 |
static lbm_value ext_matrix_to_list(lbm_value *args, lbm_uint argn) { |
305 |
|
|
|
306 |
|
28 |
lbm_value res = ENC_SYM_TERROR; |
307 |
✓✗✓✗
|
28 |
if (argn == 1 && is_matrix_float(args[0])) { |
308 |
|
28 |
matrix_float_t *lmat = (matrix_float_t*)lbm_get_custom_value(args[0]); |
309 |
|
28 |
lbm_uint size = lmat->rows * lmat->cols; |
310 |
|
|
|
311 |
|
28 |
res = lbm_heap_allocate_list(size); |
312 |
✓✗ |
28 |
if (lbm_is_cons(res)) { |
313 |
|
28 |
lbm_value curr = res; |
314 |
✓✓ |
280 |
for (unsigned int i = 0; i < size; i ++) { |
315 |
|
252 |
lbm_value f_val = lbm_enc_float(lmat->data[i]); |
316 |
✗✓ |
252 |
if (lbm_is_error(f_val)) { |
317 |
|
|
res = f_val; |
318 |
|
|
break; |
319 |
|
|
} |
320 |
|
252 |
lbm_set_car(curr, f_val); |
321 |
|
252 |
curr = lbm_cdr(curr); |
322 |
|
|
} |
323 |
|
|
} |
324 |
|
|
} |
325 |
|
28 |
return res; |
326 |
|
|
} |
327 |
|
|
|
328 |
|
|
|
329 |
|
|
|
330 |
|
|
/* ************************************************** |
331 |
|
|
* Initialization |
332 |
|
|
*/ |
333 |
|
|
|
334 |
|
21672 |
void lbm_matvec_extensions_init(void) { |
335 |
|
|
// Vectors |
336 |
|
21672 |
lbm_add_extension("vector", ext_vector); |
337 |
|
21672 |
lbm_add_extension("list-to-vector", ext_list_to_vector); |
338 |
|
21672 |
lbm_add_extension("vector-to-list", ext_vector_to_list); |
339 |
|
21672 |
lbm_add_extension("vproj", ext_vproj); |
340 |
|
21672 |
lbm_add_extension("axpy", ext_axpy); |
341 |
|
21672 |
lbm_add_extension("dot", ext_dot); |
342 |
|
21672 |
lbm_add_extension("mag", ext_mag); |
343 |
|
21672 |
lbm_add_extension("vmult", ext_vmult); |
344 |
|
|
|
345 |
|
|
// Matrices |
346 |
|
21672 |
lbm_add_extension("list-to-matrix", ext_list_to_matrix); |
347 |
|
21672 |
lbm_add_extension("matrix-to-list", ext_matrix_to_list); |
348 |
|
21672 |
} |
349 |
|
|
|