1 #include "EXTERN.h"
2 #include "perl.h"
3 #include "XSUB.h"
4
5 #include "ppport.h"
6
7 struct sort_elem {
8 SV *key;
9 SV *orig;
10 };
11
12 static I32
sv_cmp_str_asc(pTHX_ SV * sv1,SV * sv2)13 sv_cmp_str_asc(pTHX_ SV *sv1, SV *sv2)
14 {
15 struct sort_elem *se1, *se2;
16
17 se1 = (struct sort_elem*)SvIV(sv1);
18 se2 = (struct sort_elem*)SvIV(sv2);
19
20 return sv_cmp_locale(se1->key, se2->key);
21 }
22
23 static I32
sv_cmp_str_desc(pTHX_ SV * sv1,SV * sv2)24 sv_cmp_str_desc(pTHX_ SV *sv1, SV *sv2)
25 {
26 struct sort_elem *se1, *se2;
27
28 se1 = (struct sort_elem*)SvIV(sv1);
29 se2 = (struct sort_elem*)SvIV(sv2);
30
31 return sv_cmp_locale(se2->key, se1->key);
32 }
33
34 static I32
sv_cmp_number_asc(pTHX_ SV * sv1,SV * sv2)35 sv_cmp_number_asc(pTHX_ SV *sv1, SV *sv2)
36 {
37 struct sort_elem *se1, *se2;
38 NV key1, key2;
39
40 se1 = (struct sort_elem*)SvIV(sv1);
41 se2 = (struct sort_elem*)SvIV(sv2);
42
43 key1 = SvNV(se1->key);
44 key2 = SvNV(se2->key);
45
46 return (key1 > key2)
47 ? 1 : (key1 == key2)
48 ? 0 : -1;
49 }
50
51 static I32
sv_cmp_number_desc(pTHX_ SV * sv1,SV * sv2)52 sv_cmp_number_desc(pTHX_ SV *sv1, SV *sv2)
53 {
54 struct sort_elem *se1, *se2;
55 NV key1, key2;
56
57 se1 = (struct sort_elem*)SvIV(sv1);
58 se2 = (struct sort_elem*)SvIV(sv2);
59
60 key1 = SvNV(se2->key);
61 key2 = SvNV(se1->key);
62
63 return (key1 > key2)
64 ? 1 : (key1 == key2)
65 ? 0 : -1;
66 }
67
68 MODULE = List::UtilsBy::XS PACKAGE = List::UtilsBy::XS
69
70 void
sort_by(code,...)71 sort_by (code, ...)
72 SV *code
73 PROTOTYPE: &@
74 ALIAS:
75 sort_by = 0
76 rev_sort_by = 1
77 CODE:
78 {
79 dMULTICALL;
80 GV *gv;
81 HV *stash;
82 I32 gimme = G_SCALAR;
83 SV **args = &PL_stack_base[ax];
84 int i;
85 AV *tmps;
86 struct sort_elem *elems;
87
88 if (items <= 1) {
89 XSRETURN_EMPTY;
90 }
91
92 tmps = (AV *)sv_2mortal((SV *)newAV());
93
94 cv = sv_2cv(code, &stash, &gv, 0);
95 if (cv == Nullcv) {
96 croak("Not a subroutine reference");
97 }
98
99 PUSH_MULTICALL(cv);
100 SAVESPTR(GvSV(PL_defgv));
101
102 Newx(elems, items - 1, struct sort_elem);
103
104 for (i = 1; i < items; i++) {
105 struct sort_elem *elem = &elems[i - 1];
106
107 GvSV(PL_defgv) = args[i];
108 MULTICALL;
109
110 elem->key = newSVsv(*PL_stack_sp);
111 elem->orig = newSVsv(args[i]);
112
113 av_push(tmps, newSViv((IV)elem));
114 }
115
116 POP_MULTICALL;
117
118 if (ix) {
119 sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_str_desc);
120 } else {
121 sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_str_asc);
122 }
123
124 for (i = 1; i < items; i++) {
125 struct sort_elem *elem;
126 elem = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
127 ST(i-1) = sv_2mortal(elem->orig);
128 (void)sv_2mortal(elem->key);
129 }
130
131 Safefree(elems);
132
133 XSRETURN(items - 1);
134 }
135
136 void
nsort_by(code,...)137 nsort_by (code, ...)
138 SV *code
139 PROTOTYPE: &@
140 ALIAS:
141 nsort_by = 0
142 rev_nsort_by = 1
143 CODE:
144 {
145 dMULTICALL;
146 GV *gv;
147 HV *stash;
148 I32 gimme = G_SCALAR;
149 SV **args = &PL_stack_base[ax];
150 int i;
151 AV *tmps;
152 struct sort_elem *elems;
153
154 if (items <= 1) {
155 XSRETURN_EMPTY;
156 }
157
158 tmps = (AV *)sv_2mortal((SV *)newAV());
159
160 cv = sv_2cv(code, &stash, &gv, 0);
161 if (cv == Nullcv) {
162 croak("Not a subroutine reference");
163 }
164
165 PUSH_MULTICALL(cv);
166 SAVESPTR(GvSV(PL_defgv));
167
168 Newx(elems, items - 1, struct sort_elem);
169
170 for (i = 1; i < items; i++) {
171 struct sort_elem *elem = &elems[i - 1];
172
173 GvSV(PL_defgv) = args[i];
174 MULTICALL;
175
176 elem->key = newSVsv(*PL_stack_sp);
177 elem->orig = newSVsv(args[i]);
178
179 av_push(tmps, newSViv((IV)elem));
180 }
181
182 POP_MULTICALL;
183
184 if (ix) {
185 sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_number_desc);
186 } else {
187 sortsv(AvARRAY(tmps), av_len(tmps) + 1, sv_cmp_number_asc);
188 }
189
190 for (i = 1; i < items; i++) {
191 struct sort_elem *elem;
192 elem = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
193 ST(i-1) = sv_2mortal(elem->orig);
194 (void)sv_2mortal(elem->key);
195 }
196
197 Safefree(elems);
198
199 XSRETURN(items - 1);
200 }
201
202 void
min_by(code,...)203 min_by (code, ...)
204 SV *code
205 PROTOTYPE: &@
206 ALIAS:
207 min_by = 0
208 max_by = 1
209 nmin_by = 2
210 nmax_by = 3
211 CODE:
212 {
213 dMULTICALL;
214 GV *gv;
215 HV *stash;
216 I32 gimme = G_SCALAR;
217 SV **args = &ST(1);
218 I32 const len = items - 1;
219 int i;
220 AV *tmps;
221 NV max;
222 IV ret_count = 0;
223 struct sort_elem *elems, *first;
224
225 if (len < 1) {
226 XSRETURN_EMPTY;
227 }
228
229 tmps = (AV *)sv_2mortal((SV *)newAV());
230
231 cv = sv_2cv(code, &stash, &gv, 0);
232 if (cv == Nullcv) {
233 croak("Not a subroutine reference");
234 }
235
236 PUSH_MULTICALL(cv);
237 SAVESPTR(GvSV(PL_defgv));
238
239 Newx(elems, items - 1, struct sort_elem);
240
241 for (i = 0; i < len; i++) {
242 struct sort_elem *elem = &elems[i];
243
244 GvSV(PL_defgv) = args[i];
245 MULTICALL;
246
247 elem->key = newSVsv(*PL_stack_sp);
248 elem->orig = newSVsv(args[i]);
249
250 av_push(tmps, newSViv((IV)elem));
251 }
252
253 POP_MULTICALL;
254
255 if (ix & 0x1) {
256 sortsv(AvARRAY(tmps), len, sv_cmp_number_desc);
257 } else {
258 sortsv(AvARRAY(tmps), len, sv_cmp_number_asc);
259 }
260
261 for(i = 0; i < len; i++) {
262 struct sort_elem* elem
263 = (struct sort_elem*)SvIVx(*av_fetch(tmps, i, TRUE));
264 sv_2mortal(elem->key);
265 sv_2mortal(elem->orig);
266 }
267
268 first = (struct sort_elem *)SvIV(*av_fetch(tmps, 0, 0));
269 max = SvNV(first->key);
270 ST(0) = first->orig;
271 ret_count++;
272
273 if (GIMME_V != G_ARRAY) {
274 goto ret;
275 }
276
277 for (i = 2; i < items; i++) {
278 struct sort_elem *elem;
279 elem = (struct sort_elem *)SvIV(*av_fetch(tmps, i-1, 0));
280
281 if (max == SvNV(elem->key)) {
282 ST(ret_count) = elem->orig;
283 ret_count++;
284 } else {
285 goto ret;
286 }
287 }
288
289 ret:
290 Safefree(elems);
291 XSRETURN(ret_count);
292 }
293
294 void
uniq_by(code,...)295 uniq_by (code, ...)
296 SV *code
297 PROTOTYPE: &@
298 CODE:
299 {
300 dMULTICALL;
301 GV *gv;
302 HV *stash;
303 I32 gimme = G_SCALAR;
304 SV **args = &PL_stack_base[ax];
305 int i;
306 AV *tmps;
307 HV *rh;
308
309 if (items <= 1) {
310 XSRETURN_EMPTY;
311 }
312
313 tmps = (AV *)sv_2mortal((SV *)newAV());
314 rh = (HV *)sv_2mortal((SV *)newHV());
315
316 cv = sv_2cv(code, &stash, &gv, 0);
317 if (cv == Nullcv) {
318 croak("Not a subroutine reference");
319 }
320
321 PUSH_MULTICALL(cv);
322 SAVESPTR(GvSV(PL_defgv));
323
324 for (i = 1; i < items; i++) {
325 STRLEN len;
326 char *str;
327
328 GvSV(PL_defgv) = args[i];
329 MULTICALL;
330
331 str = SvPV(*PL_stack_sp, len);
332 if (!hv_exists(rh, str, len)) {
333 av_push(tmps, newSVsv(args[i]));
334 (void)hv_store(rh, str, len, newSViv(1), 0);
335 }
336 }
337
338 POP_MULTICALL;
339
340 for (i = 0; i <= av_len(tmps); i++) {
341 ST(i) = *av_fetch(tmps, i, 0);
342 }
343
344 XSRETURN(av_len(tmps) + 1);
345 }
346
347 void
partition_by(code,...)348 partition_by (code, ...)
349 SV *code
350 PROTOTYPE: &@
351 CODE:
352 {
353 dMULTICALL;
354 GV *gv;
355 HV *stash;
356 I32 gimme = G_SCALAR;
357 SV **args = &PL_stack_base[ax];
358 int i;
359 HV *rh;
360 HE *iter = NULL;
361
362 if (items <= 1) {
363 XSRETURN_EMPTY;
364 }
365
366 rh = (HV *)sv_2mortal((SV *)newHV());
367
368 cv = sv_2cv(code, &stash, &gv, 0);
369 if (cv == Nullcv) {
370 croak("Not a subroutine reference");
371 }
372
373 PUSH_MULTICALL(cv);
374 SAVESPTR(GvSV(PL_defgv));
375
376 for (i = 1; i < items; i++) {
377 STRLEN len;
378 char *str;
379
380 GvSV(PL_defgv) = args[i];
381 MULTICALL;
382
383 str = SvPV(*PL_stack_sp, len);
384 if (!hv_exists(rh, str, len)) {
385 AV* av = (AV *)sv_2mortal((SV *)newAV());
386 av_push(av, newSVsv(args[i]));
387 (void)hv_store(rh, str, len, newRV_inc((SV *)av), 0);
388 } else {
389 AV *av = (AV *)SvRV(*hv_fetch(rh, str, len, 0));
390 av_push(av, newSVsv(args[i]));
391 }
392 }
393
394 POP_MULTICALL;
395
396 hv_iterinit(rh);
397
398 i = 0;
399 while ( (iter = hv_iternext( rh )) != NULL ) {
400 ST(i) = hv_iterkeysv(iter);
401 i++;
402 ST(i) = hv_iterval(rh, iter);
403 i++;
404 }
405
406 XSRETURN(i);
407 }
408
409 void
count_by(code,...)410 count_by (code, ...)
411 SV *code
412 PROTOTYPE: &@
413 CODE:
414 {
415 dMULTICALL;
416 GV *gv;
417 HV *stash;
418 I32 gimme = G_SCALAR;
419 SV **args = &PL_stack_base[ax];
420 int i;
421 HV *rh;
422 HE *iter = NULL;
423
424 if (items <= 1) {
425 XSRETURN_EMPTY;
426 }
427
428 rh = (HV *)sv_2mortal((SV *)newHV());
429
430 cv = sv_2cv(code, &stash, &gv, 0);
431 if (cv == Nullcv) {
432 croak("Not a subroutine reference");
433 }
434
435 PUSH_MULTICALL(cv);
436 SAVESPTR(GvSV(PL_defgv));
437
438 for (i = 1; i < items; i++) {
439 STRLEN len;
440 char *str;
441
442 GvSV(PL_defgv) = args[i];
443 MULTICALL;
444
445 str = SvPV(*PL_stack_sp, len);
446 if (!hv_exists(rh, str, len)) {
447 SV* count = newSViv(1);
448 (void)hv_store(rh, str, len, count, 0);
449 } else {
450 SV **count = hv_fetch(rh, str, len, 0);
451 sv_inc(*count);
452 }
453 }
454
455 POP_MULTICALL;
456
457 hv_iterinit(rh);
458
459 i = 0;
460 while ( (iter = hv_iternext( rh )) != NULL ) {
461 ST(i) = hv_iterkeysv(iter);
462 i++;
463 ST(i) = hv_iterval(rh, iter);
464 i++;
465 }
466
467 XSRETURN(i);
468 }
469
470 void
zip_by(code,...)471 zip_by (code, ...)
472 SV *code
473 PROTOTYPE: &@
474 CODE:
475 {
476 dSP;
477 SV **args = &PL_stack_base[ax];
478 AV *tmps, *retvals;
479 I32 i, j, count;
480 I32 len, max_length = -1;
481
482 if (items <= 1) {
483 XSRETURN_EMPTY;
484 }
485
486 tmps = (AV *)sv_2mortal((SV *)newAV());
487 retvals = (AV *)sv_2mortal((SV *)newAV());
488
489 for (i = 1; i < items; i++) {
490 if (!SvROK(args[i]) || (SvTYPE(SvRV(args[i])) != SVt_PVAV)) {
491 croak("arguments should be ArrayRef");
492 }
493
494 len = av_len((AV*)SvRV(args[i]));
495 if (len > max_length) {
496 max_length = len;
497 }
498
499 av_push(tmps, newSVsv(args[i]));
500 }
501
502 SAVESPTR(GvSV(PL_defgv));
503
504 for (i = 0; i <= max_length; i++) {
505 ENTER;
506 SAVETMPS;
507
508 PUSHMARK(sp);
509 for (j = 1; j < items; j++) {
510 AV *av = (AV*)SvRV( *av_fetch(tmps, j-1, 0) );
511
512 if (av_exists(av, i)) {
513 SV *elem = *av_fetch(av, i, 0);
514 XPUSHs(sv_2mortal(newSVsv(elem)));
515 } else {
516 XPUSHs(&PL_sv_undef);
517 }
518 }
519 PUTBACK;
520
521 count = call_sv(code, G_ARRAY);
522
523 SPAGAIN;
524
525 len = av_len(retvals);
526 for (j = 0; j < count; j++) {
527 av_store(retvals, len + (count - j), newSVsv(POPs));
528 }
529
530 PUTBACK;
531 FREETMPS;
532 LEAVE;
533 }
534
535 len = av_len(retvals) + 1;
536 for (i = 0; i < len; i++) {
537 ST(i) = *av_fetch(retvals, i, 0);
538 }
539
540 XSRETURN(len);
541 }
542
543 void
unzip_by(code,...)544 unzip_by (code, ...)
545 SV *code
546 PROTOTYPE: &@
547 CODE:
548 {
549 dSP;
550 SV **args = &PL_stack_base[ax];
551 AV *retvals;
552 I32 i, j, count;
553 I32 len, max_len = 0;
554
555 if (items <= 1) {
556 XSRETURN_EMPTY;
557 }
558
559 retvals = (AV *)sv_2mortal((SV *)newAV());
560
561 SAVESPTR(GvSV(PL_defgv));
562
563 for (i = 1; i < items; i++) {
564 ENTER;
565 SAVETMPS;
566
567 PUSHMARK(sp);
568 XPUSHs(sv_2mortal(newSVsv(args[i])));
569 PUTBACK;
570
571 GvSV(PL_defgv) = args[i];
572 count = call_sv(code, G_ARRAY);
573
574 SPAGAIN;
575
576 for (j = max_len; j < count; j++) {
577 AV *tmp = (AV *)sv_2mortal((SV *)newAV());
578 av_store(retvals, j, newRV((SV*)tmp));
579 }
580
581 if (max_len < count) {
582 max_len = count;
583 }
584
585 for (j = count - 1; j >= 0; j--) {
586 SV *ret = newSVsv(POPs);
587 AV *tmp = (AV *)SvRV((SV*)*av_fetch(retvals, j, 0));
588 av_store(tmp, i - 1, ret);
589 }
590
591 PUTBACK;
592 FREETMPS;
593 LEAVE;
594 }
595
596 len = av_len(retvals) + 1;
597 for (i = 0; i < len; i++) {
598 AV *tmp = (AV *)SvRV((SV*)*av_fetch(retvals, i, 0));
599 for (j = av_len(tmp) + 1; j < (items - 1); j++) {
600 av_push(tmp, &PL_sv_undef);
601 }
602 }
603
604 for (i = 0; i < len; i++) {
605 ST(i) = *av_fetch(retvals, i, 0);
606 }
607
608 XSRETURN(len);
609 }
610
611 void
extract_by(code,...)612 extract_by (code, ...)
613 SV *code
614 PROTOTYPE: &\@
615 CODE:
616 {
617 dMULTICALL;
618 GV *gv;
619 HV *stash;
620 I32 gimme = G_SCALAR, ret_gimme = GIMME_V;
621 SV **args = &PL_stack_base[ax];
622 IV i, len;
623 AV *ret_vals, *remains, *origs;
624
625 if (items <= 1) {
626 XSRETURN_EMPTY;
627 }
628
629 ret_vals = (AV *)sv_2mortal((SV *)newAV());
630 remains = (AV *)sv_2mortal((SV *)newAV());
631
632 cv = sv_2cv(code, &stash, &gv, 0);
633 if (cv == Nullcv) {
634 croak("Not a subroutine reference");
635 }
636
637 if (!SvROK(args[1]) || (SvTYPE(SvRV(args[1])) != SVt_PVAV)) {
638 croak("arguments should be ArrayRef");
639 }
640
641 origs = (AV*)SvRV(args[1]);
642 len = av_len((AV*)SvRV(args[1])) + 1;
643
644 PUSH_MULTICALL(cv);
645 SAVESPTR(GvSV(PL_defgv));
646
647 for (i = 0; i < len; i++) {
648 SV *val, *arg;
649
650 arg = *av_fetch(origs, i, 0);
651 GvSV(PL_defgv) = arg;
652 MULTICALL;
653
654 val = newSVsv(*PL_stack_sp);
655 if (SvTRUE(val)) {
656 av_push(ret_vals, newSVsv(arg));
657 } else {
658 SV *val = newSVsv(arg);
659 SvFLAGS(val) = SvFLAGS(arg);
660 av_push(remains, val);
661 }
662 }
663
664 POP_MULTICALL;
665
666 av_clear(origs);
667
668 len = av_len(remains) + 1;
669 for (i = 0; i < len; i++) {
670 SV *val = *av_fetch(remains, i, 0);
671 av_push(origs, newSVsv(val));
672 }
673
674 if (ret_gimme == G_SCALAR) {
675 len = 1;
676 ST(0) = sv_2mortal(newSViv(av_len(ret_vals)+1));
677 } else {
678 len = av_len(ret_vals) + 1;
679 for (i = 0; i < len; i++) {
680 ST(i) = sv_mortalcopy(*av_fetch(ret_vals, i, 0));
681 }
682 }
683
684 XSRETURN(len);
685 }
686
687 void
weighted_shuffle_by(code,...)688 weighted_shuffle_by (code, ...)
689 SV *code
690 PROTOTYPE: &@
691 CODE:
692 {
693 dMULTICALL;
694 GV *gv;
695 HV *stash;
696 I32 gimme = G_SCALAR;
697 SV **args = &PL_stack_base[ax];
698 I32 i, len;
699 AV *weights, *origs, *retvals;
700
701 if (items <= 1) {
702 XSRETURN_EMPTY;
703 }
704
705 weights = (AV *)sv_2mortal((SV *)newAV());
706 origs = (AV *)sv_2mortal((SV *)newAV());
707 retvals = (AV *)sv_2mortal((SV *)newAV());
708
709 cv = sv_2cv(code, &stash, &gv, 0);
710 if (cv == Nullcv) {
711 croak("Not a subroutine reference");
712 }
713
714 PUSH_MULTICALL(cv);
715 SAVESPTR(GvSV(PL_defgv));
716
717 for (i = 1; i < items; i++) {
718 av_push(origs, newSVsv(args[i]));
719
720 GvSV(PL_defgv) = args[i];
721 MULTICALL;
722
723 av_push(weights, newSVsv(*PL_stack_sp));
724 }
725
726 POP_MULTICALL;
727
728 /* Initialize Drand01 if rand() or srand() has
729 not already been called
730 */
731 if (!PL_srand_called) {
732 (void)seedDrand01((Rand_seed_t)seed());
733 PL_srand_called = TRUE;
734 }
735
736 while ( (av_len(origs) + 1) > 1) {
737 IV total = 0;
738 I32 select;
739 I32 idx;
740 SV *selected, *last;
741
742 len = av_len(weights) + 1;
743 for (i = 0; i < len; i++) {
744 total += SvIV(*av_fetch(weights, i, 0));
745 }
746
747 select = (I32)(Drand01() * (double)total);
748 idx = 0;
749 while (select >= SvIV(*av_fetch(weights, idx, 0))) {
750 select -= SvIV(*av_fetch(weights, idx, 0));
751
752 if (av_len(weights) > idx) {
753 idx++;
754 } else {
755 break;
756 }
757 }
758
759 selected = *av_fetch(origs, idx, 0);
760 av_push(retvals, newSVsv(selected));
761
762 last = *av_fetch(origs, av_len(origs), 0);
763 av_store(origs, idx, last);
764 (void)av_pop(origs);
765
766 last = *av_fetch(weights, av_len(weights), 0);
767 av_store(weights, idx, last);
768 (void)av_pop(weights);
769 }
770
771 len = av_len(origs) + 1;
772 for (i = 0 ; i < len; i++) {
773 av_push(retvals, av_shift(origs));
774 }
775
776 for (i = 1 ; i < items; i++) {
777 ST(i-1) = sv_2mortal(newSVsv( *av_fetch(retvals, i-1, 0) ));
778 }
779
780 XSRETURN(items-1);
781 }
782
783 void
bundle_by(code,...)784 bundle_by (code, ...)
785 SV *code
786 PROTOTYPE: &@
787 CODE:
788 {
789 dSP;
790 SV **args = &PL_stack_base[ax];
791 AV *retvals;
792 IV argnum;
793 I32 i, j, count, len, loop;
794
795 if (items <= 1) {
796 XSRETURN_EMPTY;
797 }
798
799 argnum = SvIV(args[1]);
800 if (argnum <= 0) {
801 croak("bundle number is larger than 0");
802 }
803
804 retvals = (AV *)sv_2mortal((SV *)newAV());
805
806 SAVESPTR(GvSV(PL_defgv));
807
808 for (i = 2, loop = 0; i < items; i += argnum, loop++) {
809 ENTER;
810 SAVETMPS;
811
812 PUSHMARK(sp);
813 for (j = 0; j < argnum; j++) {
814 I32 index = (loop * argnum) + j + 2;
815 if (SvOK(args[index])) {
816 XPUSHs(sv_2mortal(newSVsv(args[index])));
817 } else {
818 XPUSHs(&PL_sv_undef);
819 }
820 }
821 PUTBACK;
822
823 count = call_sv(code, G_ARRAY);
824
825 SPAGAIN;
826
827 len = av_len(retvals);
828 for (j = 0; j < count; j++) {
829 av_store(retvals, len + (count - j), newSVsv(POPs));
830 }
831
832 PUTBACK;
833 FREETMPS;
834 LEAVE;
835 }
836
837 len = av_len(retvals) + 1;
838 for (i = 0; i < len; i++) {
839 ST(i) = *av_fetch(retvals, i, 0);
840 }
841
842 XSRETURN(len);
843 }
844