1 /*
2  * Copyright (c) 2021 Marti Riba Pons
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdbool.h>
10 #include <string.h>
11 #include <zsl/zsl.h>
12 #include <zsl/statistics.h>
13 
zsl_sta_mean(struct zsl_vec * v,zsl_real_t * m)14 int zsl_sta_mean(struct zsl_vec *v, zsl_real_t *m)
15 {
16 	zsl_vec_ar_mean(v, m);
17 
18 	return 0;
19 }
20 
zsl_sta_trim_mean(struct zsl_vec * v,zsl_real_t p,zsl_real_t * m)21 int zsl_sta_trim_mean(struct zsl_vec *v, zsl_real_t p, zsl_real_t *m)
22 {
23 #if CONFIG_ZSL_BOUNDS_CHECKS
24 	/* Make sure p is between 0 and 50. */
25 	if (p > 50.0 || p < 0.0) {
26 		return -EINVAL;
27 	}
28 #endif
29 
30 	ZSL_VECTOR_DEF(w, v->sz);
31 	zsl_real_t per_l, per_h;
32 	size_t first_val = 0, count = 0;
33 
34 	*m = 0.0;
35 	zsl_sta_percentile(v, p, &per_l);
36 	zsl_sta_percentile(v, 100 - p, &per_h);
37 
38 	zsl_vec_init(&w);
39 	zsl_vec_sort(v, &w);
40 
41 	for (size_t i = 0; i < v->sz; i++) {
42 		if (w.data[i] >= per_l && w.data[i] <= per_h) {
43 			count++;
44 		}
45 		if (count == 1) {
46 			first_val = i;
47 		}
48 	}
49 
50 	ZSL_VECTOR_DEF(sub, count);
51 	zsl_vec_get_subset(&w, first_val, count, &sub);
52 	zsl_sta_mean(&sub, m);
53 
54 	return 0;
55 }
56 
zsl_sta_weighted_mean(struct zsl_vec * v,struct zsl_vec * w,zsl_real_t * m)57 int zsl_sta_weighted_mean(struct zsl_vec *v, struct zsl_vec *w, zsl_real_t *m)
58 {
59 	zsl_real_t sumw = 0.0, sumwx = 0.0;
60 
61 	for (size_t i = 0; i < v->sz; i++) {
62 		sumw += w->data[i];
63 	}
64 
65 #if CONFIG_ZSL_BOUNDS_CHECKS
66 	/* Make sure the vectors dimensions match. */
67 	if (v->sz != w->sz) {
68 		return -EINVAL;
69 	}
70 
71 	/* Make sure the weights are positive or zero. */
72 	for (size_t i = 0; i < v->sz; i++) {
73 		if (w->data[i] < 0.0) {
74 			return -EINVAL;
75 		}
76 	}
77 
78 	/* Make sure that not all of the weights are zero. */
79 	if (ZSL_ABS(sumw) < 1E-6) {
80 		return -EINVAL;
81 	}
82 #endif
83 
84 	for (size_t i = 0; i < v->sz; i++) {
85 		sumwx += w->data[i] * v->data[i];
86 	}
87 
88 	*m = sumwx / sumw;
89 
90 	return 0;
91 }
92 
zsl_sta_time_weighted_mean(struct zsl_vec * v,struct zsl_vec * t,zsl_real_t * m)93 int zsl_sta_time_weighted_mean(struct zsl_vec *v, struct zsl_vec *t,
94 			       zsl_real_t *m)
95 {
96 #if CONFIG_ZSL_BOUNDS_CHECKS
97 	/* Make sure the vectors dimensions match. */
98 	if (v->sz != t->sz) {
99 		return -EINVAL;
100 	}
101 	/* Make sure that the values in 'v' are positive. */
102 	for (size_t i = 0; i < v->sz; i++) {
103 		if (v->data[i] < 0.0) {
104 			return -EINVAL;
105 		}
106 	}
107 	/* The vector 'x' can't have any repeated values. */
108 	for (size_t i = 0; i < t->sz; i++) {
109 		if (zsl_vec_contains(t, t->data[i], 1E-6) > 1) {
110 			return -EINVAL;
111 		}
112 	}
113 #endif
114 
115 	ZSL_VECTOR_DEF(ts, t->sz);
116 	ZSL_VECTOR_DEF(vs, v->sz);
117 
118 	zsl_real_t sum = 0.0;
119 
120 	zsl_vec_sort(t, &ts);
121 
122 	for (size_t i = 0; i < t->sz; i++) {
123 		for (size_t g = 0; g < t->sz; g++) {
124 			if (ZSL_ABS(ts.data[i] - t->data[g]) < 1E-6) {
125 				vs.data[i] = v->data[g];
126 			}
127 		}
128 	}
129 
130 	for (size_t j = 1; j < t->sz; j++) {
131 		sum += (ts.data[j] - ts.data[j - 1]) *
132 		       (vs.data[j] + vs.data[j - 1]) / 2.0;
133 	}
134 
135 	*m = sum / (ts.data[(t->sz - 1)] - ts.data[0]);
136 
137 	return 0;
138 }
139 
zsl_sta_demean(struct zsl_vec * v,struct zsl_vec * w)140 int zsl_sta_demean(struct zsl_vec *v, struct zsl_vec *w)
141 {
142 #if CONFIG_ZSL_BOUNDS_CHECKS
143 	/* Make sure the vectors dimensions match. */
144 	if (v->sz != w->sz) {
145 		return -EINVAL;
146 	}
147 #endif
148 
149 	zsl_real_t m;
150 
151 	zsl_vec_copy(w, v);
152 	zsl_sta_mean(v, &m);
153 	zsl_vec_scalar_add(w, -m);
154 
155 	return 0;
156 }
157 
zsl_sta_percentile(struct zsl_vec * v,zsl_real_t p,zsl_real_t * val)158 int zsl_sta_percentile(struct zsl_vec *v, zsl_real_t p, zsl_real_t *val)
159 {
160 #if CONFIG_ZSL_BOUNDS_CHECKS
161 	/* Make sure p is between 0 and 100. */
162 	if (p > 100.0 || p < 0.0) {
163 		return -EINVAL;
164 	}
165 #endif
166 
167 	ZSL_VECTOR_DEF(w, v->sz);
168 
169 	zsl_vec_init(&w);
170 	zsl_vec_sort(v, &w);
171 
172 	zsl_real_t x = (p * v->sz) / 100.;
173 	zsl_real_t per = ZSL_FLOOR(x);
174 
175 	if (x == per) {
176 		*val = (w.data[(int)per] + w.data[(int)per - 1]) / 2.;
177 	} else {
178 		*val = w.data[(int)per];
179 	}
180 
181 	return 0;
182 }
183 
zsl_sta_median(struct zsl_vec * v,zsl_real_t * m)184 int zsl_sta_median(struct zsl_vec *v, zsl_real_t *m)
185 {
186 	zsl_sta_percentile(v, 50, m);
187 
188 	return 0;
189 }
190 
zsl_sta_weighted_median(struct zsl_vec * v,struct zsl_vec * w,zsl_real_t * m)191 int zsl_sta_weighted_median(struct zsl_vec *v, struct zsl_vec *w, zsl_real_t *m)
192 {
193 #if CONFIG_ZSL_BOUNDS_CHECKS
194 	/* Make sure the vectors dimensions match. */
195 	if (v->sz != w->sz) {
196 		return -EINVAL;
197 	}
198 
199 	/* Make sure the weights are positive or zero. */
200 	zsl_real_t sum = 0.0;
201 	for (size_t i = 0; i < v->sz; i++) {
202 		sum += w->data[i];
203 		if (w->data[i] < 0.0) {
204 			return -EINVAL;
205 		}
206 	}
207 
208 	/* Make sure that that the sum of the weights is 1. */
209 	if (ZSL_ABS(sum - 1.0) > 1E-6) {
210 		return -EINVAL;
211 	}
212 #endif
213 
214 	ZSL_VECTOR_DEF(vsort, v->sz);
215 	zsl_real_t lsum = 0.0;
216 	size_t i = 0;
217 	while (lsum <= 0.5) {
218 		lsum += w->data[i];
219 		i++;
220 	}
221 
222 	lsum -= w->data[i - 1];
223 	zsl_vec_init(&vsort);
224 	zsl_vec_sort(v, &vsort);
225 
226 	if (ZSL_ABS(lsum - 0.5) < 1E-6) {
227 		*m = (vsort.data[i - 1] + vsort.data[i - 2]) / 2.0;
228 	} else {
229 		*m = vsort.data[i - 1];
230 	}
231 
232 	return 0;
233 }
234 
zsl_sta_quart(struct zsl_vec * v,zsl_real_t * q1,zsl_real_t * q2,zsl_real_t * q3)235 int zsl_sta_quart(struct zsl_vec *v, zsl_real_t *q1, zsl_real_t *q2,
236 		  zsl_real_t *q3)
237 {
238 	zsl_sta_percentile(v, 25, q1);
239 	zsl_sta_percentile(v, 50, q2);
240 	zsl_sta_percentile(v, 75, q3);
241 
242 	return 0;
243 }
244 
zsl_sta_quart_range(struct zsl_vec * v,zsl_real_t * r)245 int zsl_sta_quart_range(struct zsl_vec *v, zsl_real_t *r)
246 {
247 	zsl_real_t q1, q3;
248 
249 	zsl_sta_percentile(v, 25, &q1);
250 	zsl_sta_percentile(v, 75, &q3);
251 
252 	*r = q3 - q1;
253 
254 	return 0;
255 }
256 
zsl_sta_mode(struct zsl_vec * v,struct zsl_vec * w)257 int zsl_sta_mode(struct zsl_vec *v, struct zsl_vec *w)
258 {
259 #if CONFIG_ZSL_BOUNDS_CHECKS
260 	/* Make sure w and v are the same length. */
261 	if (v->sz != w->sz) {
262 		return -EINVAL;
263 	}
264 #endif
265 
266 	size_t i, j = 0, count, maxcount = 0;
267 
268 	ZSL_VECTOR_DEF(u, v->sz);
269 	ZSL_VECTOR_DEF(w_temp, v->sz);
270 
271 	zsl_vec_init(w);
272 
273 	for (i = 0; i < v->sz; i++) {
274 		count = zsl_vec_contains(v, v->data[i], 1E-7);
275 		u.data[i] = count;
276 		if (count > maxcount) {
277 			maxcount = count;
278 		}
279 	}
280 
281 	for (i = 0; i < v->sz; i++) {
282 		if (u.data[i] == maxcount) {
283 			w_temp.data[j] = v->data[i];
284 			j++;
285 		}
286 	}
287 
288 	w_temp.sz = j;
289 	count = 0;
290 
291 	for (i = 0; i < j; i++) {
292 		if (w_temp.data[i] >= 1E-5 || w_temp.data[i] <= 1E-5) {
293 			if (zsl_vec_contains(w, w_temp.data[i], 1E-5) == 0) {
294 				w->data[count] = w_temp.data[i];
295 				count++;
296 			}
297 		}
298 	}
299 
300 	if (zsl_vec_contains(&w_temp, 0.0, 1E-5) > 0) {
301 		count++;
302 	}
303 
304 	w->sz = count;
305 
306 	return 0;
307 }
308 
zsl_sta_data_range(struct zsl_vec * v,zsl_real_t * r)309 int zsl_sta_data_range(struct zsl_vec *v, zsl_real_t *r)
310 {
311 	ZSL_VECTOR_DEF(w, v->sz);
312 
313 	zsl_vec_sort(v, &w);
314 
315 	*r = w.data[v->sz - 1] - w.data[0];
316 
317 	return 0;
318 }
319 
zsl_sta_mean_abs_dev(struct zsl_vec * v,zsl_real_t * m)320 int zsl_sta_mean_abs_dev(struct zsl_vec *v, zsl_real_t *m)
321 {
322 #if CONFIG_ZSL_BOUNDS_CHECKS
323 	/* Make sure v has at least dimension 1. */
324 	if (v->sz == 0) {
325 		return -EINVAL;
326 	}
327 #endif
328 	ZSL_VECTOR_DEF(vdm, v->sz);
329 	zsl_sta_demean(v, &vdm);
330 
331 	zsl_real_t sum = 0.0;
332 	for (size_t i = 0; i < v->sz; i++) {
333 		sum += ZSL_ABS(vdm.data[i]);
334 	}
335 
336 	*m = sum / v->sz;
337 
338 	return 0;
339 }
340 
zsl_sta_median_abs_dev(struct zsl_vec * v,zsl_real_t * m)341 int zsl_sta_median_abs_dev(struct zsl_vec *v, zsl_real_t *m)
342 {
343 	ZSL_VECTOR_DEF(med, v->sz);
344 	zsl_real_t median;
345 
346 	zsl_sta_median(v, &median);
347 	for (size_t i = 0; i < v->sz; i++) {
348 		med.data[i] = ZSL_ABS(v->data[i] - median);
349 	}
350 
351 	zsl_sta_median(&med, m);
352 
353 	return 0;
354 }
355 
zsl_sta_var(struct zsl_vec * v,zsl_real_t * var)356 int zsl_sta_var(struct zsl_vec *v, zsl_real_t *var)
357 {
358 	ZSL_VECTOR_DEF(w, v->sz);
359 	*var = 0;
360 
361 	zsl_sta_demean(v, &w);
362 
363 	for (size_t i = 0; i < v->sz; i++) {
364 		*var += w.data[i] * w.data[i];
365 	}
366 
367 	*var /= v->sz - 1;
368 
369 	return 0;
370 }
371 
zsl_sta_std_dev(struct zsl_vec * v,zsl_real_t * s)372 int zsl_sta_std_dev(struct zsl_vec *v, zsl_real_t *s)
373 {
374 	zsl_real_t var;
375 
376 	zsl_sta_var(v, &var);
377 
378 	*s = ZSL_SQRT(var);
379 
380 	return 0;
381 }
382 
zsl_sta_covar(struct zsl_vec * v,struct zsl_vec * w,zsl_real_t * c)383 int zsl_sta_covar(struct zsl_vec *v, struct zsl_vec *w, zsl_real_t *c)
384 {
385 #if CONFIG_ZSL_BOUNDS_CHECKS
386 	/* Make sure v and w are equal length. */
387 	if (v->sz != w->sz) {
388 		return -EINVAL;
389 	}
390 #endif
391 
392 	ZSL_VECTOR_DEF(v_dm, v->sz);
393 	ZSL_VECTOR_DEF(w_dm, w->sz);
394 
395 	zsl_sta_demean(v, &v_dm);
396 	zsl_sta_demean(w, &w_dm);
397 
398 	*c = 0;
399 	for (size_t i = 0; i < v->sz; i++) {
400 		*c += v_dm.data[i] * w_dm.data[i];
401 	}
402 
403 	*c /= v->sz - 1;
404 
405 	return 0;
406 }
407 
zsl_sta_covar_mtx(struct zsl_mtx * m,struct zsl_mtx * mc)408 int zsl_sta_covar_mtx(struct zsl_mtx *m, struct zsl_mtx *mc)
409 {
410 #if CONFIG_ZSL_BOUNDS_CHECKS
411 	/* Make sure 'mc' is a square matrix with same num. columns as 'm'. */
412 	if (mc->sz_rows != mc->sz_cols || mc->sz_cols != m->sz_cols) {
413 		return -EINVAL;
414 	}
415 #endif
416 
417 	zsl_real_t c;
418 
419 	ZSL_VECTOR_DEF(v1, m->sz_rows);
420 	ZSL_VECTOR_DEF(v2, m->sz_rows);
421 
422 	for (size_t i = 0; i < m->sz_cols; i++) {
423 		for (size_t j = 0; j < m->sz_cols; j++) {
424 			zsl_mtx_get_col(m, i, v1.data);
425 			zsl_mtx_get_col(m, j, v2.data);
426 			zsl_sta_covar(&v1, &v2, &c);
427 			mc->data[(i * m->sz_cols) + j] = c;
428 		}
429 	}
430 
431 	return 0;
432 }
433 
zsl_sta_linear_reg(struct zsl_vec * x,struct zsl_vec * y,struct zsl_sta_linreg * c)434 int zsl_sta_linear_reg(struct zsl_vec *x, struct zsl_vec *y,
435 		       struct zsl_sta_linreg *c)
436 {
437 #if CONFIG_ZSL_BOUNDS_CHECKS
438 	/* Make sure x and y are equal length. */
439 	if (x->sz != y->sz) {
440 		return -EINVAL;
441 	}
442 #endif
443 
444 	zsl_real_t sumx, sumy, sumxy, sumxx, sumyy;
445 
446 	sumx = sumy = sumxy = sumxx = sumyy = 0.0;
447 
448 	for (size_t i = 0; i < x->sz; i++) {
449 		sumx += x->data[i];
450 		sumy += y->data[i];
451 		sumxy += x->data[i] * y->data[i];
452 		sumxx += x->data[i] * x->data[i];
453 		sumyy += y->data[i] * y->data[i];
454 	}
455 
456 	c->slope = (x->sz * sumxy - sumx * sumy) / (x->sz * sumxx - sumx * sumx);
457 	c->intercept = (sumy - c->slope * sumx) / x->sz;
458 	c->correlation = (x->sz * sumxy - sumx * sumy) /
459 			 ZSL_SQRT((x->sz * sumxx - sumx * sumx) *
460 				  (x->sz * sumyy - sumy * sumy));
461 
462 	return 0;
463 }
464 
465 #ifndef CONFIG_ZSL_SINGLE_PRECISION
zsl_sta_mult_linear_reg(struct zsl_mtx * x,struct zsl_vec * y,struct zsl_vec * b,zsl_real_t * r)466 int zsl_sta_mult_linear_reg(struct zsl_mtx *x, struct zsl_vec *y,
467 			    struct zsl_vec *b, zsl_real_t *r)
468 {
469 
470 #if CONFIG_ZSL_BOUNDS_CHECKS
471 	/* Make sure matrices and vectors' sizes match. */
472 	if (x->sz_rows != y->sz || x->sz_cols + 1 != b->sz) {
473 		return -EINVAL;
474 	}
475 #endif
476 
477 	ZSL_MATRIX_DEF(x_exp, x->sz_rows, (x->sz_cols + 1));
478 	ZSL_MATRIX_DEF(x_trans, (x->sz_cols + 1), x->sz_rows);
479 	ZSL_MATRIX_DEF(xx, (x->sz_cols + 1), (x->sz_cols + 1));
480 	ZSL_MATRIX_DEF(xinv, (x->sz_cols + 1), (x->sz_cols + 1));
481 	ZSL_MATRIX_DEF(ymtx, y->sz, 1);
482 	ZSL_MATRIX_DEF(xtemp, (x->sz_cols + 1), x->sz_rows);
483 	ZSL_MATRIX_DEF(bmtx, (x->sz_cols + 1), 1);
484 	ZSL_MATRIX_DEF(xtemp2, x->sz_rows, 1);
485 	ZSL_MATRIX_DEF(emtx, x->sz_rows, 1);
486 
487 	zsl_real_t v[x->sz_rows];
488 	zsl_mtx_init(&x_exp, NULL);
489 	for (size_t i = 0; i < x->sz_rows; i++) {
490 		v[i] = 1.;
491 	}
492 
493 	zsl_mtx_set_col(&x_exp, 0, v);
494 	for (size_t j = 0; j < x->sz_cols; j++) {
495 		zsl_mtx_get_col(x, j, v);
496 		zsl_mtx_set_col(&x_exp, j + 1, v);
497 	}
498 
499 	zsl_mtx_trans(&x_exp, &x_trans);
500 	zsl_mtx_mult(&x_trans, &x_exp, &xx);
501 
502 	zsl_real_t det;
503 	zsl_mtx_deter(&xx, &det);
504 	if (ZSL_ABS(det) < 1E-6) {
505 		/*
506 		 * Currently limited to square matrices. pinv could be used,
507 		 * but is too resource-intensive to add at the momemt.
508 		 */
509 		return -EINVAL;
510 	} else {
511 		zsl_mtx_inv(&xx, &xinv);
512 	}
513 
514 	zsl_mtx_from_arr(&ymtx, y->data);
515 	zsl_mtx_mult(&xinv, &x_trans, &xtemp);
516 	zsl_mtx_mult(&xtemp, &ymtx, &bmtx);
517 	zsl_vec_from_arr(b, bmtx.data);
518 
519 	zsl_mtx_mult(&x_exp, &bmtx, &xtemp2);
520 	zsl_mtx_sub(&ymtx, &xtemp2, &emtx);
521 	zsl_real_t e_norm = 0.0, ymean, ysum = 0.0;
522 	zsl_sta_mean(y, &ymean);
523 	for (size_t g = 0; g < x->sz_rows; g++) {
524 		e_norm += emtx.data[g] * emtx.data[g];
525 		ysum += (y->data[g] - ymean) * (y->data[g] - ymean);
526 	}
527 
528 	*r = 1. - e_norm / ysum;
529 
530 	return 0;
531 }
532 #endif
533 
534 #ifndef CONFIG_ZSL_SINGLE_PRECISION
zsl_sta_weighted_mult_linear_reg(struct zsl_mtx * x,struct zsl_vec * y,struct zsl_vec * w,struct zsl_vec * b,zsl_real_t * r)535 int zsl_sta_weighted_mult_linear_reg(struct zsl_mtx *x, struct zsl_vec *y,
536 				     struct zsl_vec *w, struct zsl_vec *b, zsl_real_t *r)
537 {
538 #if CONFIG_ZSL_BOUNDS_CHECKS
539 	/* Make sure matrices and vectors' sizes match. */
540 	if (x->sz_rows != y->sz || x->sz_cols + 1 != b->sz || x->sz_rows != w->sz) {
541 		return -EINVAL;
542 	}
543 	/* Make sure no weight is zero. */
544 	for (size_t k = 0; k < w->sz; k++) {
545 		if (ZSL_ABS(w->data[k]) < 1E-6) {
546 			return -EINVAL;
547 		}
548 	}
549 #endif
550 
551 	ZSL_MATRIX_DEF(x_exp, x->sz_rows, (x->sz_cols + 1));
552 	ZSL_MATRIX_DEF(x_trans, (x->sz_cols + 1), x->sz_rows);
553 	ZSL_MATRIX_DEF(xw, (x->sz_cols + 1), x->sz_rows);
554 	ZSL_MATRIX_DEF(xx, (x->sz_cols + 1), (x->sz_cols + 1));
555 	ZSL_MATRIX_DEF(xinv, (x->sz_cols + 1), (x->sz_cols + 1));
556 	ZSL_MATRIX_DEF(ymtx, y->sz, 1);
557 	ZSL_MATRIX_DEF(xtemp, (x->sz_cols + 1), x->sz_rows);
558 	ZSL_MATRIX_DEF(bmtx, (x->sz_cols + 1), 1);
559 	ZSL_MATRIX_DEF(xtemp2, x->sz_rows, 1);
560 	ZSL_MATRIX_DEF(emtx, x->sz_rows, 1);
561 	ZSL_MATRIX_DEF(idx, w->sz, w->sz);
562 
563 	zsl_mtx_init(&idx, zsl_mtx_entry_fn_identity);
564 	for (size_t k = 0; k < w->sz; k++) {
565 		w->data[k] = 1. / w->data[k];
566 		zsl_mtx_scalar_mult_row_d(&idx, k, w->data[k]);
567 	}
568 
569 	zsl_real_t v[x->sz_rows];
570 	zsl_mtx_init(&x_exp, NULL);
571 	for (size_t i = 0; i < x->sz_rows; i++) {
572 		v[i] = 1.;
573 	}
574 
575 	zsl_mtx_set_col(&x_exp, 0, v);
576 	for (size_t j = 0; j < x->sz_cols; j++) {
577 		zsl_mtx_get_col(x, j, v);
578 		zsl_mtx_set_col(&x_exp, j + 1, v);
579 	}
580 
581 	zsl_mtx_trans(&x_exp, &x_trans);
582 	zsl_mtx_mult(&x_trans, &idx, &xw);
583 	zsl_mtx_mult(&xw, &x_exp, &xx);
584 
585 	zsl_real_t det;
586 	zsl_mtx_deter(&xx, &det);
587 	if (ZSL_ABS(det) < 1E-6) {
588 		/*
589 		 * Currently limited to square matrices. pinv could be used,
590 		 * but is too resource-intensive to add at the momemt.
591 		 */
592 		return -EINVAL;
593 	} else {
594 		zsl_mtx_inv(&xx, &xinv);
595 	}
596 
597 	zsl_mtx_from_arr(&ymtx, y->data);
598 	zsl_mtx_mult(&xinv, &xw, &xtemp);
599 	zsl_mtx_mult(&xtemp, &ymtx, &bmtx);
600 	zsl_vec_from_arr(b, bmtx.data);
601 
602 	zsl_mtx_mult(&x_exp, &bmtx, &xtemp2);
603 	zsl_mtx_sub(&ymtx, &xtemp2, &emtx);
604 	zsl_real_t e_norm = 0.0, ymean, ysum = 0.0;
605 	zsl_sta_mean(y, &ymean);
606 	for (size_t g = 0; g < x->sz_rows; g++) {
607 		e_norm += emtx.data[g] * emtx.data[g];
608 		ysum += (y->data[g] - ymean) * (y->data[g] - ymean);
609 	}
610 
611 	*r = 1. - e_norm / ysum;
612 
613 	return 0;
614 }
615 #endif
616 
617 #ifndef CONFIG_ZSL_SINGLE_PRECISION
zsl_sta_quad_fit(struct zsl_mtx * m,struct zsl_vec * b)618 int zsl_sta_quad_fit(struct zsl_mtx *m, struct zsl_vec *b)
619 {
620 #if CONFIG_ZSL_BOUNDS_CHECKS
621 	/* Make sure matrices and vectors' sizes are adequate. */
622 	if (m->sz_cols != 3 || b->sz != 9) {
623 		return -EINVAL;
624 	}
625 #endif
626 
627 	ZSL_MATRIX_DEF(x, m->sz_rows, 9);
628 	ZSL_VECTOR_DEF(xv, 9);
629 	ZSL_VECTOR_DEF(mv, 3);
630 	ZSL_MATRIX_DEF(y, m->sz_rows, 1);
631 
632 	for (size_t i = 0; i < m->sz_rows; i++) {
633 		zsl_mtx_get_row(m, i, mv.data);
634 		xv.data[0] = mv.data[0] * mv.data[0];
635 		xv.data[1] = mv.data[1] * mv.data[1];
636 		xv.data[2] = mv.data[2] * mv.data[2];
637 		xv.data[3] = 2.0 * mv.data[0] * mv.data[1];
638 		xv.data[4] = 2.0 * mv.data[0] * mv.data[2];
639 		xv.data[5] = 2.0 * mv.data[1] * mv.data[2];
640 		xv.data[6] = 2.0 * mv.data[0];
641 		xv.data[7] = 2.0 * mv.data[1];
642 		xv.data[8] = 2.0 * mv.data[2];
643 		zsl_mtx_set_row(&x, i, xv.data);
644 		y.data[i] = 1.0;
645 	}
646 
647 	ZSL_MATRIX_DEF(xt, 9, m->sz_rows);
648 	ZSL_MATRIX_DEF(xtx, 9, 9);
649 	ZSL_MATRIX_DEF(inv, 9, 9);
650 	ZSL_MATRIX_DEF(xtmp, 9, (m->sz_rows));
651 	ZSL_MATRIX_DEF(btmp, 9, 1);
652 
653 	zsl_mtx_trans(&x, &xt);
654 	zsl_mtx_mult(&xt, &x, &xtx);
655 	zsl_mtx_inv(&xtx, &inv);
656 	zsl_mtx_mult(&inv, &xt, &xtmp);
657 	zsl_mtx_mult(&xtmp, &y, &btmp);
658 
659 	zsl_vec_from_arr(b, btmp.data);
660 
661 	return 0;
662 }
663 #endif
664 
zsl_sta_abs_err(zsl_real_t * val,zsl_real_t * exp_val,zsl_real_t * err)665 int zsl_sta_abs_err(zsl_real_t *val, zsl_real_t *exp_val, zsl_real_t *err)
666 {
667 	*err = ZSL_ABS(*val - *exp_val);
668 
669 	return 0;
670 }
671 
zsl_sta_rel_err(zsl_real_t * val,zsl_real_t * exp_val,zsl_real_t * err)672 int zsl_sta_rel_err(zsl_real_t *val, zsl_real_t *exp_val, zsl_real_t *err)
673 {
674 	*err = ZSL_ABS(100. * *val - 100. * *exp_val) / *exp_val;
675 
676 	return 0;
677 }
678 
zsl_sta_sta_err(struct zsl_vec * v,zsl_real_t * err)679 int zsl_sta_sta_err(struct zsl_vec *v, zsl_real_t *err)
680 {
681 #if CONFIG_ZSL_BOUNDS_CHECKS
682 	/* Make sure v has at least dimension 1. */
683 	if (v->sz == 0) {
684 		return -EINVAL;
685 	}
686 #endif
687 
688 	zsl_real_t var;
689 	zsl_sta_var(v, &var);
690 	*err = ZSL_SQRT(var / v->sz);
691 
692 	return 0;
693 }
694