comparison remap_neon.c @ 1:b829afbea564

more testing
author Peter Meerwald <p.meerwald@bct-electronic.com>
date Fri, 20 Apr 2012 14:26:14 +0200
parents e0040ee59c3c
children 09ee6a01a3d3
comparison
equal deleted inserted replaced
0:e0040ee59c3c 1:b829afbea564
8 #include <string.h> 8 #include <string.h>
9 #include <math.h> 9 #include <math.h>
10 #include <sys/time.h> 10 #include <sys/time.h>
11 #include <assert.h> 11 #include <assert.h>
12 12
13 13 typedef unsigned char uint8_t;
14 typedef short int16_t; 14 typedef short int16_t;
15 typedef unsigned int uint32_t;
16
15 typedef enum pa_sample_format { 17 typedef enum pa_sample_format {
16 PA_SAMPLE_S16LE, 18 PA_SAMPLE_S16LE,
17 PA_SAMPLE_FLOAT32LE, 19 PA_SAMPLE_FLOAT32LE,
18 } pa_sample_format_t; 20 } pa_sample_format_t;
19 #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE 21 #define PA_SAMPLE_S16NE PA_SAMPLE_S16LE
20 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE 22 #define PA_SAMPLE_FLOAT32NE PA_SAMPLE_FLOAT32LE
23
24 typedef struct pa_sample_spec {
25 pa_sample_format_t format;
26 uint32_t rate;
27 uint8_t channels;
28 } pa_sample_spec;
29
30 #define PA_CHANNELS_MAX 32
21 typedef struct { 31 typedef struct {
22 pa_sample_format_t *format; 32 pa_sample_format_t *format;
33 pa_sample_spec *i_ss, *o_ss;
34 float map_table_f[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
35 int32_t map_table_i[PA_CHANNELS_MAX][PA_CHANNELS_MAX];
23 } pa_remap_t; 36 } pa_remap_t;
24 typedef void (*pa_remap_func_t)(pa_remap_t *m, void *dst, const void *src, unsigned n); 37
25 typedef long long unsigned int pa_usec_t; 38 typedef long long unsigned int pa_usec_t;
26 39
27 #define pa_assert(x) assert(x) 40 #define pa_assert(x) assert(x)
28 #define pa_assert_not_reached() assert(0) 41 #define pa_assert_not_reached() assert(0)
29 42
46 gettimeofday(&tv, NULL); 59 gettimeofday(&tv, NULL);
47 60
48 return tv.tv_sec * 1000000ULL + tv.tv_usec; 61 return tv.tv_sec * 1000000ULL + tv.tv_usec;
49 } 62 }
50 63
64 static void remap_channels_matrix_c(pa_remap_t *m, void *dst, const void *src, unsigned n) {
65 unsigned oc, ic, i;
66 unsigned n_ic, n_oc;
67
68 n_ic = m->i_ss->channels;
69 n_oc = m->o_ss->channels;
70
71 switch (*m->format) {
72 case PA_SAMPLE_FLOAT32NE:
73 {
74 float *d, *s;
75
76 memset(dst, 0, n * sizeof(float) * n_oc);
77
78 for (oc = 0; oc < n_oc; oc++) {
79
80 for (ic = 0; ic < n_ic; ic++) {
81 float vol;
82
83 vol = m->map_table_f[oc][ic];
84
85 if (vol <= 0.0)
86 continue;
87
88 d = (float *)dst + oc;
89 s = (float *)src + ic;
90
91 if (vol >= 1.0) {
92 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
93 *d += *s;
94 } else {
95 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
96 *d += *s * vol;
97 }
98 }
99 }
100
101 break;
102 }
103 case PA_SAMPLE_S16NE:
104 {
105 int16_t *d, *s;
106
107 memset(dst, 0, n * sizeof(int16_t) * n_oc);
108
109 for (oc = 0; oc < n_oc; oc++) {
110
111 for (ic = 0; ic < n_ic; ic++) {
112 int32_t vol;
113
114 vol = m->map_table_i[oc][ic];
115
116 if (vol <= 0)
117 continue;
118
119 d = (int16_t *)dst + oc;
120 s = (int16_t *)src + ic;
121
122 if (vol >= 0x10000) {
123 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
124 *d += *s;
125 } else {
126 for (i = n; i > 0; i--, s += n_ic, d += n_oc)
127 *d += (int16_t) (((int32_t)*s * vol) >> 16);
128 }
129 }
130 }
131 break;
132 }
133 default:
134 pa_assert_not_reached();
135 }
136 }
137
51 static void remap_mono_to_stereo_c(pa_remap_t *m, void *dst, const void *src, unsigned n) { 138 static void remap_mono_to_stereo_c(pa_remap_t *m, void *dst, const void *src, unsigned n) {
52 unsigned i; 139 unsigned i;
53 140
54 switch (*m->format) { 141 switch (*m->format) {
55 case PA_SAMPLE_FLOAT32NE: 142 case PA_SAMPLE_FLOAT32NE:
98 } 185 }
99 default: 186 default:
100 pa_assert_not_reached(); 187 pa_assert_not_reached();
101 } 188 }
102 } 189 }
190
191
192
193 static void remap_stereo_to_mono_c(pa_remap_t *m, void *dst, const void *src, unsigned n) {
194 unsigned i;
195
196 switch (*m->format) {
197 case PA_SAMPLE_FLOAT32NE:
198 {
199 float *d = (float *) dst, *s = (float *) src;
200
201 for (i = n >> 2; i > 0; i--) {
202 d[0] = s[0] + s[1];
203 d[1] = s[2] + s[3];
204 d[2] = s[4] + s[5];
205 d[3] = s[6] + s[7];
206 s += 8;
207 d += 4;
208 }
209 for (i = n & 3; i; i--) {
210 d[0] = s[0] + s[1];
211 s += 2;
212 d += 1;
213 }
214 break;
215 }
216 case PA_SAMPLE_S16NE:
217 {
218 int16_t *d = (int16_t *) dst, *s = (int16_t *) src;
219
220 for (i = n >> 2; i > 0; i--) {
221 *d++ += s[0] + s[1];
222 *d++ += s[2] + s[3];
223 *d++ += s[4] + s[5];
224 *d++ += s[6] + s[7];
225 s += 8;
226 }
227 for (i = n & 3; i; i--) {
228 *d++ += s[0] + s[1];
229 s += 2;
230 }
231 break;
232 }
233 default:
234 pa_assert_not_reached();
235 }
236 }
237
103 238
104 #if defined(__arm__) 239 #if defined(__arm__)
105 240
106 #include "arm_neon.h" 241 #include "arm_neon.h"
107 242
151 default: 286 default:
152 pa_assert_not_reached(); 287 pa_assert_not_reached();
153 } 288 }
154 } 289 }
155 290
291 /* don't inline, causes ICE, see https://bugs.launchpad.net/bugs/936863 */
292 static __attribute__ ((noinline)) void stereo_to_mono_float(float *d, const float *s, unsigned n) {
293 unsigned i;
294
295 for (i = 0; i < n/4; i++) {
296 float32x4x2_t stereo = vld2q_f32(s);
297 float32x4_t mono = vaddq_f32(stereo.val[0], stereo.val[1]);
298 vst1q_f32(d, mono);
299 s += 8;
300 d += 4;
301 }
302 for (i = n & ~3; i < n; i++) {
303 d[0] = s[0] + s[1];
304 s += 2;
305 d++;
306 }
307 }
308
309 /* don't inline, causes ICE, see https://bugs.launchpad.net/bugs/936863 */
310 static __attribute__ ((noinline)) void stereo_to_mono_int16(int16_t *d, const int16_t *s, unsigned n) {
311 unsigned int i;
312
313 for (i = 0; i < n/8; i++) {
314 int16x8x2_t stereo = vld2q_s16(s);
315 int16x8_t mono = vaddq_s16(stereo.val[0], stereo.val[1]);
316 vst1q_s16(d, mono);
317 s += 16;
318 d += 8;
319 }
320 for (i = n & ~7; i < n; i++) {
321 d[0] = s[0] + s[1];
322 s += 2;
323 d++;
324 }
325 }
326
327 static void remap_stereo_to_mono_neon(pa_remap_t *m, void *dst, const void *src, unsigned n) {
328 switch (*m->format) {
329 case PA_SAMPLE_FLOAT32NE:
330 stereo_to_mono_float(dst, src, n);
331 break;
332 case PA_SAMPLE_S16NE:
333 stereo_to_mono_int16(dst, src, n);
334 break;
335 default:
336 pa_assert_not_reached();
337 }
338 }
156 #define SAMPLES 1019 339 #define SAMPLES 1019
157 #define TIMES 10000 340 #define TIMES 10000
158 341
159 static void run_test_float(void) { 342 static void run_test_mono_to_stereo_float(void) {
160 float stereo[2*SAMPLES]; 343 float stereo[2*SAMPLES];
161 float stereo_ref[2*SAMPLES]; 344 float stereo_ref[2*SAMPLES];
345 float stereo_gen[2*SAMPLES];
162 float mono[SAMPLES]; 346 float mono[SAMPLES];
163 int i; 347 int i;
164 pa_usec_t start, stop; 348 pa_usec_t start, stop;
165 pa_remap_func_t func;
166 pa_sample_format_t sf; 349 pa_sample_format_t sf;
350 pa_sample_spec iss, oss;
167 pa_remap_t remap; 351 pa_remap_t remap;
168 352
169 pa_log_debug("checking NEON remap_mono_to_stereo(float, %d)", SAMPLES); 353 pa_log_debug("checking NEON remap_mono_to_stereo(float, %d)", SAMPLES);
170 354
171 memset(stereo_ref, 0, sizeof(stereo_ref)); 355 memset(stereo_ref, 0, sizeof(stereo_ref));
175 mono[i] = rand()/(float) RAND_MAX - 0.5f; 359 mono[i] = rand()/(float) RAND_MAX - 0.5f;
176 } 360 }
177 361
178 sf = PA_SAMPLE_FLOAT32NE; 362 sf = PA_SAMPLE_FLOAT32NE;
179 remap.format = &sf; 363 remap.format = &sf;
180 func = (pa_remap_func_t) remap_mono_to_stereo_c; 364 iss.format = PA_SAMPLE_FLOAT32NE;
181 func(&remap, stereo_ref, mono, SAMPLES); 365 iss.channels = 1;
366 oss.format = PA_SAMPLE_FLOAT32NE;
367 oss.channels = 2;
368 remap.i_ss = &iss;
369 remap.o_ss = &oss;
370 remap.map_table_f[0][0] = 1.0;
371 remap.map_table_f[1][0] = 1.0;
372
373 remap_mono_to_stereo_c(&remap, stereo_ref, mono, SAMPLES);
374 remap_channels_matrix_c(&remap, stereo_gen, mono, SAMPLES);
182 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES); 375 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES);
183 376
184 for (i = 0; i < 2*SAMPLES; i++) { 377 for (i = 0; i < 2*SAMPLES; i++) {
185 if (fabsf(stereo[i] - stereo_ref[i]) > 0.00001) { 378 if (fabsf(stereo[i] - stereo_ref[i]) > 0.00001) {
186 pa_log_debug("%d: %.3f != %.3f (%.3f)", i, stereo[i], stereo_ref[i], 379 pa_log_debug("%d: %.3f != %.3f (%.3f)", i, stereo[i], stereo_ref[i],
187 mono[i/2]); 380 mono[i/2]);
188 } 381 }
189 } 382 }
383 for (i = 0; i < 2*SAMPLES; i++) {
384 if (fabsf(stereo[i] - stereo_gen[i]) > 0.00001) {
385 pa_log_debug("%d: %.3f != %.3f (%.3f)", i, stereo[i], stereo_gen[i],
386 mono[i/2]);
387 }
388 }
190 389
191 start = pa_rtclock_now(); 390 start = pa_rtclock_now();
192 for (i = 0; i < TIMES; i++) { 391 for (i = 0; i < TIMES; i++) {
193 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES); 392 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES);
194 } 393 }
195 stop = pa_rtclock_now(); 394 stop = pa_rtclock_now();
196 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start)); 395 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start));
197 396
198 start = pa_rtclock_now(); 397 start = pa_rtclock_now();
199 for (i = 0; i < TIMES; i++) { 398 for (i = 0; i < TIMES; i++) {
200 func(&remap, stereo_ref, mono, SAMPLES); 399 remap_mono_to_stereo_c(&remap, stereo_ref, mono, SAMPLES);
201 } 400 }
202 stop = pa_rtclock_now(); 401 stop = pa_rtclock_now();
203 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start)); 402 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
204 } 403
205 404 start = pa_rtclock_now();
206 static void run_test_s16(void) { 405 for (i = 0; i < TIMES; i++) {
406 remap_channels_matrix_c(&remap, stereo_gen, mono, SAMPLES);
407 }
408 stop = pa_rtclock_now();
409 pa_log_info("generic: %llu usec.", (long long unsigned int)(stop - start));
410 }
411
412 static void run_test_stereo_to_mono_float(void) {
413 float stereo[2*SAMPLES];
414 float mono_ref[SAMPLES];
415 float mono_gen[SAMPLES];
416 float mono[SAMPLES];
417 int i;
418 pa_usec_t start, stop;
419 pa_sample_format_t sf;
420 pa_sample_spec iss, oss;
421 pa_remap_t remap;
422
423 pa_log_debug("checking NEON remap_stereo_to_mono(float, %d)", SAMPLES);
424
425 memset(mono_ref, 0, sizeof(mono_ref));
426 memset(mono, 0, sizeof(mono));
427
428 for (i = 0; i < 2*SAMPLES; i++) {
429 stereo[i] = rand()/(float) RAND_MAX - 0.5f;
430 }
431
432 sf = PA_SAMPLE_FLOAT32NE;
433 remap.format = &sf;
434 iss.format = PA_SAMPLE_FLOAT32NE;
435 iss.channels = 2;
436 oss.format = PA_SAMPLE_FLOAT32NE;
437 oss.channels = 1;
438 remap.i_ss = &iss;
439 remap.o_ss = &oss;
440 remap.map_table_f[0][0] = 1.0;
441 remap.map_table_f[0][1] = 1.0;
442
443 remap_stereo_to_mono_c(&remap, mono_ref, stereo, SAMPLES);
444 remap_channels_matrix_c(&remap, mono_gen, stereo, SAMPLES);
445 remap_stereo_to_mono_neon(&remap, mono, stereo, SAMPLES);
446
447 for (i = 0; i < SAMPLES; i++) {
448 if (fabsf(mono[i] - mono_ref[i]) > 0.00001) {
449 pa_log_debug("%d: %.3f != %.3f (%.3f %0.3f)", i, mono[i], mono_ref[i],
450 stereo[2*i+0], stereo[2*i+1]);
451 }
452 }
453
454 start = pa_rtclock_now();
455 for (i = 0; i < TIMES; i++) {
456 remap_stereo_to_mono_neon(&remap, mono, stereo, SAMPLES);
457 }
458 stop = pa_rtclock_now();
459 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start));
460
461 start = pa_rtclock_now();
462 for (i = 0; i < TIMES; i++) {
463 remap_stereo_to_mono_c(&remap, mono_ref, stereo, SAMPLES);
464 }
465 stop = pa_rtclock_now();
466 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
467
468 start = pa_rtclock_now();
469 for (i = 0; i < TIMES; i++) {
470 remap_channels_matrix_c(&remap, mono_gen, stereo, SAMPLES);
471 }
472 stop = pa_rtclock_now();
473 pa_log_info("generic: %llu usec.", (long long unsigned int)(stop - start));
474 }
475
476 static void run_test_mono_to_stereo_s16(void) {
207 int16_t stereo[2*SAMPLES]; 477 int16_t stereo[2*SAMPLES];
208 int16_t stereo_ref[2*SAMPLES]; 478 int16_t stereo_ref[2*SAMPLES];
479 int16_t stereo_gen[2*SAMPLES];
209 int16_t mono[SAMPLES]; 480 int16_t mono[SAMPLES];
210 int i; 481 int i;
211 pa_usec_t start, stop; 482 pa_usec_t start, stop;
212 pa_remap_func_t func;
213 pa_sample_format_t sf; 483 pa_sample_format_t sf;
484 pa_sample_spec iss, oss;
214 pa_remap_t remap; 485 pa_remap_t remap;
215 486
216 pa_log_debug("checking NEON remap_mono_to_stereo(s16, %d)", SAMPLES); 487 pa_log_debug("checking NEON remap_mono_to_stereo(s16, %d)", SAMPLES);
217 488
218 memset(stereo_ref, 0, sizeof(stereo_ref)); 489 memset(stereo_ref, 0, sizeof(stereo_ref));
222 mono[i] = rand() - RAND_MAX/2; 493 mono[i] = rand() - RAND_MAX/2;
223 } 494 }
224 495
225 sf = PA_SAMPLE_S16NE; 496 sf = PA_SAMPLE_S16NE;
226 remap.format = &sf; 497 remap.format = &sf;
227 func = (pa_remap_func_t) remap_mono_to_stereo_c; 498 iss.format = PA_SAMPLE_S16NE;
228 func(&remap, stereo_ref, mono, SAMPLES); 499 iss.channels = 1;
500 oss.format = PA_SAMPLE_S16NE;
501 oss.channels = 2;
502 remap.i_ss = &iss;
503 remap.o_ss = &oss;
504 remap.map_table_f[0][0] = 1.0;
505 remap.map_table_f[1][0] = 1.0;
506
507 remap_mono_to_stereo_c(&remap, stereo_ref, mono, SAMPLES);
508 remap_channels_matrix_c(&remap, stereo_gen, mono, SAMPLES);
229 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES); 509 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES);
230 510
231 for (i = 0; i < 2*SAMPLES; i++) { 511 for (i = 0; i < 2*SAMPLES; i++) {
232 if (abs(stereo[i] - stereo_ref[i]) > 0) { 512 if (abs(stereo[i] - stereo_ref[i]) > 0) {
233 pa_log_debug("%d: %d != %d (%d)", i, stereo[i], stereo_ref[i], 513 pa_log_debug("%d: %d != %d (%d)", i, stereo[i], stereo_ref[i],
234 mono[i/2]); 514 mono[i/2]);
235 } 515 }
236 } 516 }
237 517
518 for (i = 0; i < 2*SAMPLES; i++) {
519 if (abs(stereo[i] - stereo_gen[i]) > 0) {
520 pa_log_debug("%d: %d != %d (%d)", i, stereo[i], stereo_gen[i],
521 mono[i/2]);
522 }
523 }
524
238 start = pa_rtclock_now(); 525 start = pa_rtclock_now();
239 for (i = 0; i < TIMES; i++) { 526 for (i = 0; i < TIMES; i++) {
240 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES); 527 remap_mono_to_stereo_neon(&remap, stereo, mono, SAMPLES);
241 } 528 }
242 stop = pa_rtclock_now(); 529 stop = pa_rtclock_now();
243 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start)); 530 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start));
244 531
245 start = pa_rtclock_now(); 532 start = pa_rtclock_now();
246 for (i = 0; i < TIMES; i++) { 533 for (i = 0; i < TIMES; i++) {
247 func(&remap, stereo_ref, mono, SAMPLES); 534 remap_mono_to_stereo_c(&remap, stereo_ref, mono, SAMPLES);
248 } 535 }
249 stop = pa_rtclock_now(); 536 stop = pa_rtclock_now();
250 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start)); 537 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
251 } 538
539 start = pa_rtclock_now();
540 for (i = 0; i < TIMES; i++) {
541 remap_channels_matrix_c(&remap, stereo_gen, mono, SAMPLES);
542 }
543 stop = pa_rtclock_now();
544 pa_log_info("generic: %llu usec.", (long long unsigned int)(stop - start));
545 }
546
547 static void run_test_stereo_to_mono_s16(void) {
548 int16_t stereo[2*SAMPLES];
549 int16_t mono_ref[SAMPLES];
550 int16_t mono_gen[SAMPLES];
551 int16_t mono[SAMPLES];
552 int i;
553 pa_usec_t start, stop;
554 pa_sample_format_t sf;
555 pa_sample_spec iss, oss;
556 pa_remap_t remap;
557
558 pa_log_debug("checking NEON remap_stereo_to_mono(s16, %d)", SAMPLES);
559
560 memset(mono_ref, 0, sizeof(mono_ref));
561 memset(mono, 0, sizeof(mono));
562
563 for (i = 0; i < 2*SAMPLES; i++) {
564 stereo[i] = rand() - RAND_MAX/2;
565 }
566
567 sf = PA_SAMPLE_S16NE;
568 remap.format = &sf;
569 iss.format = PA_SAMPLE_S16NE;
570 iss.channels = 2;
571 oss.format = PA_SAMPLE_S16NE;
572 oss.channels = 1;
573 remap.i_ss = &iss;
574 remap.o_ss = &oss;
575 remap.map_table_f[0][0] = 1.0;
576 remap.map_table_f[0][1] = 1.0;
577
578 remap_stereo_to_mono_c(&remap, mono_ref, stereo, SAMPLES);
579 remap_channels_matrix_c(&remap, mono_gen, stereo, SAMPLES);
580 remap_stereo_to_mono_neon(&remap, mono, stereo, SAMPLES);
581
582 for (i = 0; i < SAMPLES; i++) {
583 if (abs(mono[i] - mono_ref[i]) > 0) {
584 pa_log_debug("%d: %d != %d (%d)", i, mono[i], mono_ref[i],
585 stereo[2*i+0], stereo[2*i+1]);
586 }
587 }
588 for (i = 0; i < SAMPLES; i++) {
589 if (abs(mono[i] - mono_gen[i]) > 0) {
590 pa_log_debug("%d: %d != %d (%d)", i, mono[i], mono_gen[i],
591 stereo[2*i+0], stereo[2*i+1]);
592 }
593 }
594
595 start = pa_rtclock_now();
596 for (i = 0; i < TIMES; i++) {
597 remap_stereo_to_mono_neon(&remap, mono, stereo, SAMPLES);
598 }
599 stop = pa_rtclock_now();
600 pa_log_info("NEON: %llu usec.", (long long unsigned int)(stop - start));
601
602 start = pa_rtclock_now();
603 for (i = 0; i < TIMES; i++) {
604 remap_stereo_to_mono_c(&remap, mono_ref, stereo, SAMPLES);
605 }
606 stop = pa_rtclock_now();
607 pa_log_info("ref: %llu usec.", (long long unsigned int)(stop - start));
608
609 start = pa_rtclock_now();
610 for (i = 0; i < TIMES; i++) {
611 remap_channels_matrix_c(&remap, mono_gen, stereo, SAMPLES);
612 }
613 stop = pa_rtclock_now();
614 pa_log_info("generic: %llu usec.", (long long unsigned int)(stop - start));
615 }
616
252 617
253 #endif /* defined(__arm__) */ 618 #endif /* defined(__arm__) */
254 619
255 int main() { 620 int main() {
256 621
257 run_test_float(); 622 run_test_stereo_to_mono_float();
258 run_test_s16(); 623 run_test_stereo_to_mono_s16();
624
625 run_test_mono_to_stereo_float();
626 run_test_mono_to_stereo_s16();
627
259 628
260 return EXIT_SUCCESS; 629 return EXIT_SUCCESS;
261 } 630 }

Repositories maintained by Peter Meerwald, pmeerw@pmeerw.net.