Mercurial > hg > libxtract
comparison src/scalar.c @ 43:4a36f70a76e9
Numerous fixes and enhancements, see ChangeLog.
author | Jamie Bullock <jamie@postlude.co.uk> |
---|---|
date | Fri, 15 Dec 2006 21:17:12 +0000 |
parents | 84e69b155098 |
children | b2e7e24c9a9c |
comparison
equal
deleted
inserted
replaced
42:84e69b155098 | 43:4a36f70a76e9 |
---|---|
22 /* xtract_scalar.c: defines functions that extract a feature as a single value from an input vector */ | 22 /* xtract_scalar.c: defines functions that extract a feature as a single value from an input vector */ |
23 | 23 |
24 #include "xtract/libxtract.h" | 24 #include "xtract/libxtract.h" |
25 #include "math.h" | 25 #include "math.h" |
26 #include <stdlib.h> | 26 #include <stdlib.h> |
27 | 27 #include <string.h> |
28 int xtract_mean(float *data, int N, void *argv, float *result){ | 28 |
29 int xtract_mean(const float *data, const int N, const void *argv, float *result){ | |
29 | 30 |
30 int n = N; | 31 int n = N; |
31 | 32 |
32 while(n--) | 33 while(n--) |
33 *result += data[n]; | 34 *result += data[n]; |
35 *result /= N; | 36 *result /= N; |
36 | 37 |
37 return SUCCESS; | 38 return SUCCESS; |
38 } | 39 } |
39 | 40 |
40 int xtract_variance(float *data, int N, void *argv, float *result){ | 41 int xtract_variance(const float *data, const int N, const void *argv, float *result){ |
41 | 42 |
42 int n = N; | 43 int n = N; |
43 | 44 |
44 while(n--) | 45 while(n--) |
45 *result += data[n] - *(float *)argv; | 46 *result += pow(data[n] - *(float *)argv, 2); |
46 | 47 |
47 *result = SQ(*result) / (N - 1); | 48 *result = *result / (N - 1); |
48 | 49 |
49 return SUCCESS; | 50 return SUCCESS; |
50 } | 51 } |
51 | 52 |
52 int xtract_standard_deviation(float *data, int N, void *argv, float *result){ | 53 int xtract_standard_deviation(const float *data, const int N, const void *argv, float *result){ |
53 | 54 |
54 *result = sqrt(*(float *)argv); | 55 *result = sqrt(*(float *)argv); |
55 | 56 |
56 return SUCCESS; | 57 return SUCCESS; |
57 } | 58 } |
58 | 59 |
59 int xtract_average_deviation(float *data, int N, void *argv, float *result){ | 60 int xtract_average_deviation(const float *data, const int N, const void *argv, float *result){ |
60 | 61 |
61 int n = N; | 62 int n = N; |
62 | 63 |
63 while(n--) | 64 while(n--) |
64 *result += fabs(data[n] - *(float *)argv); | 65 *result += fabs(data[n] - *(float *)argv); |
66 *result /= N; | 67 *result /= N; |
67 | 68 |
68 return SUCCESS; | 69 return SUCCESS; |
69 } | 70 } |
70 | 71 |
71 int xtract_skewness(float *data, int N, void *argv, float *result){ | 72 int xtract_skewness(const float *data, const int N, const void *argv, float *result){ |
72 | 73 |
73 int n = N; | 74 int n = N; |
74 | 75 |
75 float temp; | 76 float temp; |
76 | 77 |
82 *result /= N; | 83 *result /= N; |
83 | 84 |
84 return SUCCESS; | 85 return SUCCESS; |
85 } | 86 } |
86 | 87 |
87 int xtract_kurtosis(float *data, int N, void *argv, float *result){ | 88 int xtract_kurtosis(const float *data, const int N, const void *argv, float *result){ |
88 | 89 |
89 int n = N; | 90 int n = N; |
90 | 91 |
91 float temp; | 92 float temp; |
92 | 93 |
100 | 101 |
101 return SUCCESS; | 102 return SUCCESS; |
102 } | 103 } |
103 | 104 |
104 | 105 |
105 int xtract_centroid(float *data, int N, void *argv, float *result){ | 106 int xtract_centroid(const float *data, const int N, const void *argv, float *result){ |
106 | 107 |
107 int n = (N >> 1); | 108 int n = (N >> 1); |
108 | 109 |
109 float *freqs, *amps, FA = 0.f, A = 0.f; | 110 const float *freqs, *amps; |
111 float FA = 0.f, A = 0.f; | |
110 | 112 |
111 freqs = data; | 113 freqs = data; |
112 amps = data + n; | 114 amps = data + n; |
113 | 115 |
114 while(n--){ | 116 while(n--){ |
119 *result = FA / A; | 121 *result = FA / A; |
120 | 122 |
121 return SUCCESS; | 123 return SUCCESS; |
122 } | 124 } |
123 | 125 |
124 int xtract_irregularity_k(float *data, int N, void *argv, float *result){ | 126 int xtract_irregularity_k(const float *data, const int N, const void *argv, float *result){ |
125 | 127 |
126 int n, | 128 int n, |
127 M = N - 1; | 129 M = N - 1; |
128 | 130 |
129 for(n = 1; n < M; n++) | 131 for(n = 1; n < M; n++) |
130 *result += fabs(data[n] - (data[n-1] + data[n] + data[n+1]) / 3); | 132 *result += fabs(data[n] - (data[n-1] + data[n] + data[n+1]) / 3); |
131 | 133 |
132 return SUCCESS; | 134 return SUCCESS; |
133 } | 135 } |
134 | 136 |
135 int xtract_irregularity_j(float *data, int N, void *argv, float *result){ | 137 int xtract_irregularity_j(const float *data, const int N, const void *argv, float *result){ |
136 | 138 |
137 int n = N; | 139 int n = N; |
138 | 140 |
139 float num = 0.f, den = 0.f; | 141 float num = 0.f, den = 0.f; |
140 | 142 |
146 *result = num / den; | 148 *result = num / den; |
147 | 149 |
148 return SUCCESS; | 150 return SUCCESS; |
149 } | 151 } |
150 | 152 |
151 int xtract_tristimulus_1(float *data, int N, void *argv, float *result){ | 153 int xtract_tristimulus_1(const float *data, const int N, const void *argv, float *result){ |
152 | 154 |
153 int n = N; | 155 int n = N; |
154 | 156 |
155 float den, p1, temp; | 157 float den, p1, temp; |
156 | 158 |
167 *result = p1 / den; | 169 *result = p1 / den; |
168 | 170 |
169 return SUCCESS; | 171 return SUCCESS; |
170 } | 172 } |
171 | 173 |
172 int xtract_tristimulus_2(float *data, int N, void *argv, float *result){ | 174 int xtract_tristimulus_2(const float *data, const int N, const void *argv, float *result){ |
173 | 175 |
174 int n = N; | 176 int n = N; |
175 | 177 |
176 float den, p2, p3, p4, temp; | 178 float den, p2, p3, p4, temp; |
177 | 179 |
192 *result = (p2 + p3 + p4) / den; | 194 *result = (p2 + p3 + p4) / den; |
193 | 195 |
194 return SUCCESS; | 196 return SUCCESS; |
195 } | 197 } |
196 | 198 |
197 int xtract_tristimulus_3(float *data, int N, void *argv, float *result){ | 199 int xtract_tristimulus_3(const float *data, const int N, const void *argv, float *result){ |
198 | 200 |
199 int n = N, count = 0; | 201 int n = N, count = 0; |
200 | 202 |
201 float den, num, temp; | 203 float den, num, temp; |
202 | 204 |
214 *result = num / den; | 216 *result = num / den; |
215 | 217 |
216 return SUCCESS; | 218 return SUCCESS; |
217 } | 219 } |
218 | 220 |
219 int xtract_smoothness(float *data, int N, void *argv, float *result){ | 221 int xtract_smoothness(const float *data, const int N, const void *argv, float *result){ |
220 | 222 |
221 int n = N; | 223 int n = N; |
222 | 224 |
223 if (data[0] <= 0) data[0] = 1; | 225 float *input; |
224 if (data[1] <= 0) data[1] = 1; | 226 |
227 input = (float *)malloc(N * sizeof(float)); | |
228 input = memcpy(input, data, N * sizeof(float)); | |
229 | |
230 if (input[0] <= 0) input[0] = 1; | |
231 if (input[1] <= 0) input[1] = 1; | |
225 | 232 |
226 for(n = 2; n < N; n++){ | 233 for(n = 2; n < N; n++){ |
227 if(data[n] <= 0) data[n] = 1; | 234 if(input[n] <= 0) input[n] = 1; |
228 *result += abs(20 * log(data[n-1]) - (20 * log(data[n-2]) + | 235 *result += abs(20 * log(input[n-1]) - (20 * log(input[n-2]) + |
229 20 * log(data[n-1]) + 20 * log(data[n])) / 3); | 236 20 * log(input[n-1]) + 20 * log(input[n])) / 3); |
230 } | 237 } |
231 | 238 |
232 return SUCCESS; | 239 free(input); |
233 } | 240 |
234 | 241 return SUCCESS; |
235 int xtract_spread(float *data, int N, void *argv, float *result){ | 242 } |
243 | |
244 int xtract_spread(const float *data, const int N, const void *argv, float *result){ | |
236 | 245 |
237 int n = N; | 246 int n = N; |
238 | 247 |
239 float num = 0.f, den = 0.f, tmp; | 248 float num = 0.f, den = 0.f, tmp; |
240 | 249 |
247 *result = sqrt(num / den); | 256 *result = sqrt(num / den); |
248 | 257 |
249 return SUCCESS; | 258 return SUCCESS; |
250 } | 259 } |
251 | 260 |
252 int xtract_zcr(float *data, int N, void *argv, float *result){ | 261 int xtract_zcr(const float *data, const int N, const void *argv, float *result){ |
253 | 262 |
254 int n = N; | 263 int n = N; |
255 | 264 |
256 for(n = 1; n < N; n++) | 265 for(n = 1; n < N; n++) |
257 if(data[n] * data[n-1] < 0) (*result)++; | 266 if(data[n] * data[n-1] < 0) (*result)++; |
259 *result /= N; | 268 *result /= N; |
260 | 269 |
261 return SUCCESS; | 270 return SUCCESS; |
262 } | 271 } |
263 | 272 |
264 int xtract_rolloff(float *data, int N, void *argv, float *result){ | 273 int xtract_rolloff(const float *data, const int N, const void *argv, float *result){ |
265 | 274 |
266 int n = N; | 275 int n = N; |
267 float pivot, temp; | 276 float pivot, temp; |
268 | 277 |
269 pivot = temp = 0.f; | 278 pivot = temp = 0.f; |
278 *result = (n / (float)N) * (((float *)argv)[1] * .5); | 287 *result = (n / (float)N) * (((float *)argv)[1] * .5); |
279 | 288 |
280 return SUCCESS; | 289 return SUCCESS; |
281 } | 290 } |
282 | 291 |
283 int xtract_loudness(float *data, int N, void *argv, float *result){ | 292 int xtract_loudness(const float *data, const int N, const void *argv, float *result){ |
284 | 293 |
285 int n = BARK_BANDS; | 294 int n = BARK_BANDS; |
286 | 295 |
287 /*if(n != N) return BAD_VECTOR_SIZE; */ | 296 /*if(n != N) return BAD_VECTOR_SIZE; */ |
288 | 297 |
291 | 300 |
292 return SUCCESS; | 301 return SUCCESS; |
293 } | 302 } |
294 | 303 |
295 | 304 |
296 int xtract_flatness(float *data, int N, void *argv, float *result){ | 305 int xtract_flatness(const float *data, const int N, const void *argv, float *result){ |
297 | 306 |
298 int n; | 307 int n; |
299 | 308 |
300 float num, den, temp; | 309 float num, den, temp, *tmp, prescale; |
301 | 310 int lower, upper; |
311 | |
312 tmp = (float *)argv; | |
313 lower = (int)tmp[0]; | |
314 upper = (int)tmp[1]; | |
315 prescale = (float)tmp[2]; | |
316 | |
317 upper = (upper > N ? N : upper); | |
318 lower = (lower < 0.f ? 0.f : lower); | |
319 | |
302 den = temp = num = 0.f; | 320 den = temp = num = 0.f; |
303 | 321 |
304 for(n = 0; n < N; n++){ | 322 for(n = lower; n < upper; n++){ |
305 if((temp = data[n])){ | 323 if((temp = data[n] * prescale)){ |
306 if(!num) | 324 if(!num) |
307 num = den = temp; | 325 num = den = temp; |
308 else{ | 326 else{ |
309 num *= temp; | 327 num *= temp; |
310 den += temp; | 328 den += temp; |
318 *result = num / den; | 336 *result = num / den; |
319 | 337 |
320 return SUCCESS; | 338 return SUCCESS; |
321 } | 339 } |
322 | 340 |
323 int xtract_tonality(float *data, int N, void *argv, float *result){ | 341 int xtract_tonality(const float *data, const int N, const void *argv, float *result){ |
324 | 342 |
325 float sfmdb, sfm; | 343 float sfmdb, sfm; |
326 | 344 |
327 sfm = *(float *)argv; | 345 sfm = *(float *)argv; |
328 | 346 |
331 *result = MIN(sfmdb, 1); | 349 *result = MIN(sfmdb, 1); |
332 | 350 |
333 return SUCCESS; | 351 return SUCCESS; |
334 } | 352 } |
335 | 353 |
336 int xtract_crest(float *data, int N, void *argv, float *result){ | 354 int xtract_crest(const float *data, const int N, const void *argv, float *result){ |
337 | 355 |
338 return FEATURE_NOT_IMPLEMENTED; | 356 return FEATURE_NOT_IMPLEMENTED; |
339 | 357 |
340 } | 358 } |
341 | 359 |
342 int xtract_noisiness(float *data, int N, void *argv, float *result){ | 360 int xtract_noisiness(const float *data, const int N, const void *argv, float *result){ |
343 | 361 |
344 return FEATURE_NOT_IMPLEMENTED; | 362 return FEATURE_NOT_IMPLEMENTED; |
345 | 363 |
346 } | 364 } |
347 | 365 |
348 int xtract_rms_amplitude(float *data, int N, void *argv, float *result){ | 366 int xtract_rms_amplitude(const float *data, const int N, const void *argv, float *result){ |
349 | 367 |
350 int n = N; | 368 int n = N; |
351 | 369 |
352 while(n--) *result += SQ(data[n]); | 370 while(n--) *result += SQ(data[n]); |
353 | 371 |
354 *result = sqrt(*result / N); | 372 *result = sqrt(*result / N); |
355 | 373 |
356 return SUCCESS; | 374 return SUCCESS; |
357 } | 375 } |
358 | 376 |
359 int xtract_inharmonicity(float *data, int N, void *argv, float *result){ | 377 int xtract_inharmonicity(const float *data, const int N, const void *argv, float *result){ |
360 | 378 |
361 int n = N >> 1; | 379 int n = N >> 1; |
362 float num = 0.f, den = 0.f, | 380 float num = 0.f, den = 0.f, fund; |
363 fund, *freqs, *amps; | 381 const float *freqs, *amps; |
364 | 382 |
365 fund = *(float *)argv; | 383 fund = *(float *)argv; |
366 freqs = data; | 384 freqs = data; |
367 amps = data + n; | 385 amps = data + n; |
368 | 386 |
375 | 393 |
376 return SUCCESS; | 394 return SUCCESS; |
377 } | 395 } |
378 | 396 |
379 | 397 |
380 int xtract_power(float *data, int N, void *argv, float *result){ | 398 int xtract_power(const float *data, const int N, const void *argv, float *result){ |
381 | 399 |
382 return FEATURE_NOT_IMPLEMENTED; | 400 return FEATURE_NOT_IMPLEMENTED; |
383 | 401 |
384 } | 402 } |
385 | 403 |
386 int xtract_odd_even_ratio(float *data, int N, void *argv, float *result){ | 404 int xtract_odd_even_ratio(const float *data, const int N, const void *argv, float *result){ |
387 | 405 |
388 int n = N, j, k; | 406 int M = (N >> 1), n; |
389 | 407 |
390 float num = 0.f, den = 0.f; | 408 float num = 0.f, den = 0.f, temp, f0; |
391 | 409 |
392 while(n--){ | 410 f0 = *(float *)argv; |
393 j = n * 2; | 411 |
394 k = j - 1; | 412 for(n = 0; n < M; n++){ |
395 num += data[k]; | 413 if((temp = data[n])){ |
396 den += data[j]; | 414 if(((int)(rintf(temp / f0)) % 2) != 0){ |
415 num += data[M + n]; | |
416 } | |
417 else{ | |
418 den += data[M + n]; | |
419 } | |
420 } | |
397 } | 421 } |
398 | 422 |
399 *result = num / den; | 423 *result = num / den; |
400 | 424 |
401 return SUCCESS; | 425 return SUCCESS; |
402 } | 426 } |
403 | 427 |
404 int xtract_sharpness(float *data, int N, void *argv, float *result){ | 428 int xtract_sharpness(const float *data, const int N, const void *argv, float *result){ |
405 | 429 |
406 return FEATURE_NOT_IMPLEMENTED; | 430 return FEATURE_NOT_IMPLEMENTED; |
407 | 431 |
408 } | 432 } |
409 | 433 |
410 int xtract_slope(float *data, int N, void *argv, float *result){ | 434 int xtract_slope(const float *data, const int N, const void *argv, float *result){ |
411 | 435 |
412 return FEATURE_NOT_IMPLEMENTED; | 436 return FEATURE_NOT_IMPLEMENTED; |
413 | 437 |
414 } | 438 } |
415 | 439 |
416 int xtract_lowest_match(float *data, int N, void *argv, float *result){ | 440 int xtract_lowest(const float *data, const int N, const void *argv, float *result){ |
417 | 441 |
418 float lowest_match = SR_LIMIT; | 442 float lower, upper, lowest; |
419 int n = N; | 443 int n = N; |
444 | |
445 lower = *(float *)argv; | |
446 upper = *((float *)argv+1); | |
447 | |
448 lowest = upper; | |
420 | 449 |
421 while(n--) { | 450 while(n--) { |
422 if(data[n] > 0) | 451 if(data[n] > lower) |
423 lowest_match = MIN(lowest_match, data[n]); | 452 *result = MIN(lowest, data[n]); |
424 } | 453 } |
425 | 454 |
426 *result = (lowest_match == SR_LIMIT ? 0 : lowest_match); | 455 *result = (*result == upper ? -0 : *result); |
427 | 456 |
428 return SUCCESS; | 457 return SUCCESS; |
429 } | 458 } |
430 | 459 |
431 int xtract_hps(float *data, int N, void *argv, float *result){ | 460 int xtract_hps(const float *data, const int N, const void *argv, float *result){ |
432 | 461 |
433 int n = N, M, m, l, peak_index, position1_lwr; | 462 int n = N, M, m, l, peak_index, position1_lwr; |
434 float *coeffs2, *coeffs3, *product, L, | 463 float *coeffs2, *coeffs3, *product, L, |
435 largest1_lwr, peak, ratio1, sr; | 464 largest1_lwr, peak, ratio1, sr; |
436 | 465 |
488 | 517 |
489 return SUCCESS; | 518 return SUCCESS; |
490 } | 519 } |
491 | 520 |
492 | 521 |
493 int xtract_f0(float *data, int N, void *argv, float *result){ | 522 int xtract_f0(const float *data, const int N, const void *argv, float *result){ |
494 | 523 |
495 int M, sr, tau, n; | 524 int M, sr, tau, n; |
496 float f0, err_tau_1, err_tau_x, array_max, threshold_peak, threshold_centre; | 525 size_t bytes; |
526 float f0, err_tau_1, err_tau_x, array_max, | |
527 threshold_peak, threshold_centre, | |
528 *input; | |
497 | 529 |
498 sr = *(float *)argv; | 530 sr = *(float *)argv; |
531 | |
532 input = (float *)malloc(bytes = N * sizeof(float)); | |
533 input = memcpy(input, data, bytes); | |
499 /* threshold_peak = *((float *)argv+1); | 534 /* threshold_peak = *((float *)argv+1); |
500 threshold_centre = *((float *)argv+2); | 535 threshold_centre = *((float *)argv+2); |
501 printf("peak: %.2f\tcentre: %.2f\n", threshold_peak, threshold_centre);*/ | 536 printf("peak: %.2f\tcentre: %.2f\n", threshold_peak, threshold_centre);*/ |
502 /* add temporary dynamic control over thresholds to test clipping effects */ | 537 /* add temporary dynamic control over thresholds to test clipping effects */ |
503 | 538 |
508 err_tau_1 = 0; | 543 err_tau_1 = 0; |
509 array_max = 0; | 544 array_max = 0; |
510 | 545 |
511 /* Find the array max */ | 546 /* Find the array max */ |
512 for(n = 0; n < N; n++){ | 547 for(n = 0; n < N; n++){ |
513 if (data[n] > array_max) | 548 if (input[n] > array_max) |
514 array_max = data[n]; | 549 array_max = input[n]; |
515 } | 550 } |
516 | 551 |
517 threshold_peak *= array_max; | 552 threshold_peak *= array_max; |
518 | 553 |
519 /* peak clip */ | 554 /* peak clip */ |
520 for(n = 0; n < N; n++){ | 555 for(n = 0; n < N; n++){ |
521 if(data[n] > threshold_peak) | 556 if(input[n] > threshold_peak) |
522 data[n] = threshold_peak; | 557 input[n] = threshold_peak; |
523 else if(data[n] < -threshold_peak) | 558 else if(input[n] < -threshold_peak) |
524 data[n] = -threshold_peak; | 559 input[n] = -threshold_peak; |
525 } | 560 } |
526 | 561 |
527 threshold_centre *= array_max; | 562 threshold_centre *= array_max; |
528 | 563 |
529 /* Centre clip */ | 564 /* Centre clip */ |
530 for(n = 0; n < N; n++){ | 565 for(n = 0; n < N; n++){ |
531 if (data[n] < threshold_centre) | 566 if (input[n] < threshold_centre) |
532 data[n] = 0; | 567 input[n] = 0; |
533 else | 568 else |
534 data[n] -= threshold_centre; | 569 input[n] -= threshold_centre; |
535 } | 570 } |
536 | 571 |
537 /* Estimate fundamental freq */ | 572 /* Estimate fundamental freq */ |
538 for (n = 1; n < M; n++) | 573 for (n = 1; n < M; n++) |
539 err_tau_1 = err_tau_1 + fabs(data[n] - data[n+1]); | 574 err_tau_1 = err_tau_1 + fabs(input[n] - input[n+1]); |
540 /* FIX: this doesn't pose too much load if it returns 'early', but if it can't find f0, load can be significant for larger block sizes M^2 iterations! */ | 575 /* FIX: this doesn't pose too much load if it returns 'early', but if it can't find f0, load can be significant for larger block sizes M^2 iterations! */ |
541 for (tau = 2; tau < M; tau++){ | 576 for (tau = 2; tau < M; tau++){ |
542 err_tau_x = 0; | 577 err_tau_x = 0; |
543 for (n = 1; n < M; n++){ | 578 for (n = 1; n < M; n++){ |
544 err_tau_x = err_tau_x + fabs(data[n] - data[n+tau]); | 579 err_tau_x = err_tau_x + fabs(input[n] - input[n+tau]); |
545 } | 580 } |
546 if (err_tau_x < err_tau_1) { | 581 if (err_tau_x < err_tau_1) { |
547 f0 = sr / (tau + (err_tau_x / err_tau_1)); | 582 f0 = sr / (tau + (err_tau_x / err_tau_1)); |
548 *result = f0; | 583 *result = f0; |
584 free(input); | |
549 return SUCCESS; | 585 return SUCCESS; |
550 } | 586 } |
551 } | 587 } |
588 *result = -0; | |
589 free(input); | |
552 return NO_RESULT; | 590 return NO_RESULT; |
553 } | 591 } |
592 | |
593 int xtract_failsafe_f0(const float *data, const int N, const void *argv, float *result){ | |
594 | |
595 float *magnitudes = NULL, argf[2], *peaks = NULL, return_code; | |
596 | |
597 return_code = xtract_f0(data, N, argv, result); | |
598 | |
599 if(return_code == NO_RESULT){ | |
600 | |
601 magnitudes = (float *)malloc(N * sizeof(float)); | |
602 peaks = (float *)malloc(N * sizeof(float)); | |
603 xtract_magnitude_spectrum(data, N, NULL, magnitudes); | |
604 argf[0] = 10.f; | |
605 argf[1] = *(float *)argv; | |
606 xtract_peaks(magnitudes, N, argf, peaks); | |
607 argf[0] = 0.f; | |
608 argf[1] = N >> 1; | |
609 xtract_lowest(peaks, argf[1], argf, result); | |
610 | |
611 free(magnitudes); | |
612 free(peaks); | |
613 } | |
614 | |
615 return SUCCESS; | |
616 | |
617 } | |
618 |