File Coverage

Permute.xs
Criterion Covered Total %
statement 146 160 91.2
branch 81 104 77.8
condition n/a
subroutine n/a
pod n/a
total 227 264 85.9


line stmt bran cond sub pod time code
1             /*
2             Permute.xs
3              
4             Copyright (c) 1999 - 2008 Edwin Pratomo
5              
6             You may distribute under the terms of either the GNU General Public
7             License or the Artistic License, as specified in the Perl README file,
8             with the exception that it cannot be placed on a CD-ROM or similar media
9             for commercial distribution without the prior approval of the author.
10              
11             */
12              
13             #ifdef __cplusplus
14             extern "C" {
15             #endif
16             #include "EXTERN.h"
17             #include "perl.h"
18             #include "XSUB.h"
19             #include
20             #include "coollex.h"
21             #include "multicall.h"
22             #include "ppport.h"
23             #ifdef __cplusplus
24             }
25             #endif
26              
27             /* (Robin) This hack is stolen from Graham Barr's Scalar-List-Utils package.
28             The comment therein runs:
29              
30             Some platforms have strict exports. And before 5.7.3 cxinc (or Perl_cxinc)
31             was not exported. Therefore platforms like win32, VMS etc have problems
32             so we redefine it here -- GMB
33              
34             With any luck, it will enable us to build under ActiveState Perl.
35             */
36             #if PERL_VERSION < 7/* Not in 5.6.1. */
37             # ifdef cxinc
38             # undef cxinc
39             # endif
40             # define cxinc() my_cxinc(aTHX)
41             static I32
42             my_cxinc(pTHX)
43             {
44             cxstack_max = cxstack_max * 3 / 2;
45             Renew(cxstack, cxstack_max + 1, struct context); /* XXX should fix CXINC macro */
46             return cxstack_ix + 1;
47             }
48             #endif
49              
50             /* (Robin) Assigning to AvARRAY(array) expands to an assignment which has a typecast on the left-hand side.
51             * So it was technically illegal, but GCC is decent enough to accept it
52             * anyway. Unfortunately other compilers are not usually so forgiving...
53             */
54             #if PERL_VERSION >= 9
55             # define AvARRAY_set(av, val) ((av)->sv_u.svu_array) = val
56             #else
57             # define AvARRAY_set(av, val) ((XPVAV*) SvANY(av))->xav_array = (char*) val
58             #endif
59              
60             typedef unsigned int UINT;
61             typedef unsigned long ULONG;
62              
63             #ifdef USE_LINKEDLIST
64             typedef struct record {
65             int info;
66             struct record *link;
67             } listrecord;
68             #endif
69              
70             typedef struct {
71             bool is_done;
72             SV **items;
73             SV* aryref;
74             UV num;
75             #ifdef USE_LINKEDLIST
76             listrecord *ptr_head, **ptr, **pred;
77             #else
78             UINT *loc; /* location of n in p[] */
79             UINT *p;
80             #endif
81             COMBINATION *c;
82             } Permute;
83              
84             /* private _next */
85             #ifdef USE_LINKEDLIST
86             static bool _next(UV n, listrecord *ptr_head, listrecord **ptr, listrecord **pred)
87             #else
88 81           static bool _next(UV n, UINT *p, UINT *loc)
89             #endif
90             {
91             #ifndef USE_LINKEDLIST
92             int i;
93             #endif
94 81           bool is_done = FALSE;
95              
96 81 100         if (n <= 1) /* termination condition */
97 23           return TRUE;
98              
99             #ifdef USE_LINKEDLIST
100             /* less arithmetic */
101             if (ptr[n]->link != NULL) {
102             pred[n]->link = ptr[n]->link;
103             pred[n] = pred[n]->link;
104             ptr[n]->link = pred[n]->link;
105             pred[n]->link = ptr[n];
106             } else {
107             pred[n]->link = NULL;
108             is_done = _next(n - 1, ptr_head, ptr, pred);
109             ptr[n]->link = ptr_head->link;
110             ptr_head->link = ptr[n]; /* change head of list */
111             pred[n] = ptr_head;
112             }
113             #else
114 58 100         if (loc[n] < n) {
115             /* swap adjacent */
116 31           p[loc[n]] = p[loc[n] + 1];
117 31           p[++loc[n]] = n;
118             } else {
119 27           is_done = _next(n - 1, p, loc);
120             /* then shift right */
121 58 100         for (i = n - 1; i >= 1; i--)
122 31           p[i + 1] = p[i];
123             /* adjust both extremes */
124 27           p[1] = n;
125 27           loc[n] = 1;
126             }
127             #endif
128 58           return is_done;
129             }
130              
131              
132             /* permute_engine() and afp_destructor() are from Robin Houston
133             * */
134 46314           void permute_engine(
135             AV* av,
136             SV** array,
137             I32 level,
138             I32 len, SV*** tmparea, OP* callback)
139             {
140 46314           SV** copy = tmparea[level];
141 46314           int index = level;
142 46314           bool calling = (index + 1 == len);
143             SV* tmp;
144            
145 46314 50         Copy(array, copy, len, SV*);
146            
147 46314 100         if (calling)
148 40375           AvARRAY_set(av, copy);
149              
150             do {
151 409455 100         if (calling) {
152 363146           PL_op = callback;
153 363146           CALLRUNOPS(aTHX);
154             }
155             else {
156 46309           permute_engine(av, copy, level + 1, len, tmparea, callback);
157             }
158 409453 100         if (index != 0) {
159 363141           tmp = copy[index];
160 363141           copy[index] = copy[index - 1];
161 363141           copy[index - 1] = tmp;
162             }
163 409453 100         } while (index-- > 0);
164 46312           }
165              
166             struct afp_cache {
167             SV*** tmparea;
168             AV* array;
169             I32 len;
170             SV** array_array;
171             U32 array_flags;
172             SSize_t array_fill;
173             SV** copy; /* Non-magical SV list for magical array */
174             };
175              
176             static
177 5           void afp_destructor(void *cache)
178             {
179 5           struct afp_cache *c = cache;
180             I32 x;
181            
182             /* PerlIO_stdoutf("DESTROY!\n"); */
183              
184 35 100         for (x = c->len; x >= 0; x--) free(c->tmparea[x]);
185 5           free(c->tmparea);
186 5 100         if (c->copy) {
187 6 100         for (x = 0; x < c->len; x++) SvREFCNT_dec(c->copy[x]);
188 1           free(c->copy);
189             }
190            
191 5           AvARRAY_set(c->array, c->array_array);
192 5           SvFLAGS(c->array) = c->array_flags;
193 5           AvFILLp(c->array) = c->array_fill;
194 5           free(c);
195 5           }
196              
197             static
198 6           bool reset_combination(Permute *self, AV *av, UV r) {
199             UV n;
200 6           COMBINATION *c = NULL;
201 6 100         if ((n = av_len(av) + 1) == 0)
202 1           return 0;
203              
204 5           c = init_combination(n, r, av);
205             /* PerlIO_stdoutf("passed init_combination()\n"); */
206 5 50         if (c == NULL) {
207 0           warn("Unable to initialize combination");
208 0           return 0;
209             }
210 5           self->c = c;
211              
212 5           coollex(self->c);
213 5           coollex_visit(self->c, self->items + 1); /* base of items is 1 */
214 5           return 1;
215             }
216              
217             MODULE = Algorithm::Permute PACKAGE = Algorithm::Permute
218             PROTOTYPES: DISABLE
219              
220             Permute*
221             new(CLASS, av, ...)
222             char *CLASS
223             AV *av
224             PREINIT:
225             UV i, num;
226             UV r, n;
227             UV has_combination;
228             #ifdef USE_LINKEDLIST
229             listrecord *q; /* temporary holder */
230             #endif
231            
232             CODE:
233 4           RETVAL = (Permute*) safemalloc(sizeof(Permute));
234 4 50         if (RETVAL == NULL) {
235 0           warn("Unable to create an instance of Algorithm::Permute");
236 0           XSRETURN_UNDEF;
237             }
238              
239 4           RETVAL->is_done = FALSE;
240 4 50         if ((n = av_len(av) + 1) == 0)
241 0           XSRETURN_UNDEF;
242              
243             /* init combination if necessary */
244 4           has_combination = 0;
245 4           RETVAL->c = NULL;
246 4           num = n;
247 4 100         if (items > 2) {
248 3 50         r = SvUV(ST(2));
249 3 50         if (r > n) {
250 0           warn("Number of combination must be less or equal the number of elements");
251 0           XSRETURN_UNDEF;
252             }
253 3 50         if (r < n) {
254 3           has_combination = 1;
255 3           num = r;
256             }
257             }
258              
259 4           RETVAL->aryref = newRV_inc((SV*) av);
260 4           RETVAL->num = num;
261              
262 4 50         if ((RETVAL->items = (SV**) safemalloc(sizeof(SV*) * (num + 1))) == NULL)
263 0           XSRETURN_UNDEF;
264             #ifdef USE_LINKEDLIST
265             RETVAL->ptr_head = safemalloc(sizeof(listrecord));
266             if (RETVAL->ptr_head == NULL)
267             XSRETURN_UNDEF;
268             q = RETVAL->ptr_head;
269             RETVAL->ptr = safemalloc(sizeof(listrecord*) * (num + 1));
270             if (RETVAL->ptr == NULL)
271             XSRETURN_UNDEF;
272             RETVAL->pred = safemalloc(sizeof(listrecord*) * (num + 1));
273             if (RETVAL->pred == NULL)
274             XSRETURN_UNDEF;
275             #else
276 4           RETVAL->p = (UINT*) safemalloc(sizeof(UINT) * (num + 1));
277 4 50         if (RETVAL->p == NULL)
278 0           XSRETURN_UNDEF;
279 4           RETVAL->loc = (UINT*) safemalloc(sizeof(UINT) * (num + 1));
280 4 50         if (RETVAL->loc == NULL)
281 0           XSRETURN_UNDEF;
282             #endif
283              
284             /* initialize items, p, and loc */
285 13 100         for (i = 1; i <= num; i++) {
286 9 100         if (has_combination) {
287 6           *(RETVAL->items + i) = &PL_sv_undef;
288             } else {
289 3           *(RETVAL->items + i) = av_shift(av);
290             }
291             #ifdef USE_LINKEDLIST
292             q->link = safemalloc(sizeof(listrecord));
293             if (q->link == NULL)
294             XSRETURN_UNDEF;
295             q = q->link;
296              
297             q->info = num - i + 1;
298             RETVAL->ptr[q->info] = q;
299             RETVAL->pred[i] = RETVAL->ptr_head; /* all predecessors point to ptr_head */
300             #else
301 9           *(RETVAL->p + i) = num - i + 1;
302 9           *(RETVAL->loc + i) = 1;
303             #endif
304             }
305             #ifdef USE_LINKEDLIST
306             q->link = NULL; /* the tail of list points to NULL */
307             #endif
308              
309 4 100         if (has_combination) {
310 3 50         if(!reset_combination(RETVAL, av, r)) {
311 0           XSRETURN_UNDEF;
312             }
313             }
314              
315             OUTPUT:
316             RETVAL
317              
318             void
319             next(self)
320             Permute *self
321             PREINIT:
322             int i;
323             #ifdef USE_LINKEDLIST
324             listrecord *q; /* temporary holder */
325             #endif
326             PPCODE:
327 59 100         if (self->is_done) { /* done permutation for all combination */
328 5 100         if (self->c) {
329 3           free_combination(self->c);
330 3           self->c = NULL;
331             }
332 5           XSRETURN_EMPTY;
333             }
334             else {
335 54 50         EXTEND(sp, self->num);
336             #ifdef USE_LINKEDLIST
337             q = self->ptr_head->link;
338             while (q) {
339             PUSHs(sv_2mortal(newSVsv(*(self->items + q->info))));
340             /* PerlIO_stdoutf("%d\n", q->info); */
341             q = q->link;
342             }
343             self->is_done = _next(self->num, self->ptr_head, self->ptr, self->pred);
344             #else
345 174 100         for (i = 1; i <= self->num; i++) {
346 120           PUSHs(sv_2mortal(newSVsv(*(self->items + *(self->p + i)))));
347             }
348 54           self->is_done = _next(self->num, self->p, self->loc);
349             #endif
350             }
351             /* generate next combination if necessary */
352 54 100         if (self->is_done && self->c) { /* permutation done */
    100          
353 21           self->is_done = coollex(self->c); /* generate next combination */
354             #ifdef USE_LINKEDLIST
355             q = self->ptr_head;
356             for (i = 1; i <= self->num; i++) {
357             q = q->link;
358             q->info = self->num - i + 1;
359             self->pred[i] = self->ptr_head;
360             }
361             /* q->link = NULL; */
362             assert(q->link == NULL); /* should point to NULL */
363             #else
364             /* reset self->p and self->loc */
365 63 100         for (i = 1; i <= self->num; i++) {
366 42           *(self->p + i) = self->num - i + 1;
367 42           *(self->loc + i) = 1;
368             }
369             #endif
370             /* and update self->items */
371 21           coollex_visit(self->c, self->items + 1);
372             }
373              
374             void
375             DESTROY(self)
376             Permute *self
377             PREINIT:
378             int i;
379             #ifdef USE_LINKEDLIST
380             listrecord *q;
381             #endif
382             CODE:
383 4           SvREFCNT_dec(self->aryref);
384             #ifdef USE_LINKEDLIST
385             q = self->ptr_head;
386             for (i = 1; i <= self->num; i++) {
387             safefree(self->ptr[i]);
388             /* No need to deallocate this, in fact, it would be disaster */
389             /* safefree(self->pred[i]); */
390             SvREFCNT_dec(*(self->items + i));
391             }
392             safefree(self->ptr);
393             safefree(self->pred);
394             safefree(self->ptr_head);
395             #else
396 4           safefree(self->p); /* must free elements first? */
397 4           safefree(self->loc);
398 13 100         for (i = 1; i <= self->num; i++) { /* leakproof! */
399 9           SvREFCNT_dec(*(self->items + i));
400             }
401             #endif
402 4           safefree(self->items);
403 4           safefree(self);
404              
405             void
406             peek(self)
407             Permute *self
408             PREINIT:
409             #ifdef USE_LINKEDLIST
410             listrecord *q;
411             #else
412             int i;
413             #endif
414             PPCODE:
415 14 100         if (self->is_done)
416 1           XSRETURN_EMPTY;
417 13 50         EXTEND(sp, self->num);
418             #ifdef USE_LINKEDLIST
419             q = self->ptr_head->link;
420             while (q) {
421             PUSHs(sv_2mortal(newSVsv(*(self->items + q->info))));
422             q = q->link;
423             }
424             #else
425 40 100         for (i = 1; i <= self->num; i++)
426 27           PUSHs(sv_2mortal(newSVsv(*(self->items + *(self->p + i)))));
427             #endif
428              
429             void
430             reset(self)
431             Permute *self
432             PREINIT:
433             int i;
434             AV* av;
435             COMBINATION *c;
436             UV n;
437             #ifdef USE_LINKEDLIST
438             listrecord *q;
439             #endif
440             CODE:
441 3           self->is_done = FALSE;
442              
443 3           reset_combination(self, (AV*)(SvRV(self->aryref)), self->num);
444             #ifdef USE_LINKEDLIST
445             q = self->ptr_head;
446             for (i = 1; i <= self->num; i++) {
447             q = q->link;
448             q->info = self->num - i + 1;
449             self->pred[i] = self->ptr_head;
450             }
451             assert(q->link == NULL);
452             #else
453 10 100         for (i = 1; i <= self->num; i++) {
454 7           *(self->p + i) = self->num - i + 1;
455 7           *(self->loc + i) = 1;
456             }
457             #endif
458              
459             void
460             permute(callback_sv, array_sv)
461             SV* callback_sv;
462             SV* array_sv;
463             PROTOTYPE: &\@
464             PREINIT:
465             CV* callback;
466             GV* agv;
467             I32 x;
468             PERL_CONTEXT* cx;
469 6           I32 gimme = G_VOID; /* We call our callback in VOID context */
470              
471             bool old_catch;
472             struct afp_cache *c;
473 6           I32 hasargs = 0;
474             SV** newsp;
475             PPCODE:
476             {
477 6 50         if (!SvROK(callback_sv) || SvTYPE(SvRV(callback_sv)) != SVt_PVCV)
    50          
478 0           Perl_croak(aTHX_ "Callback is not a CODE reference");
479 6 50         if (!SvROK(array_sv) || SvTYPE(SvRV(array_sv)) != SVt_PVAV)
    50          
480 0           Perl_croak(aTHX_ "Array is not an ARRAY reference");
481            
482 6           c = malloc(sizeof(struct afp_cache));
483 6           callback = (CV*)SvRV(callback_sv);
484 6           c->array = (AV*)SvRV(array_sv);
485 6           c->len = 1 + av_len(c->array);
486            
487 6           agv = gv_fetchpv("A", TRUE, SVt_PVAV);
488 6           SAVESPTR(GvSV(agv));
489              
490 6 50         if (SvREADONLY(c->array))
491 0           Perl_croak(aTHX_ "Can't permute a read-only array");
492              
493 6 100         if (c->len == 0) {
494             /* Should we warn here? */
495 1           free(c);
496 1           return;
497             }
498            
499 5           c->array_array = AvARRAY(c->array);
500 5           c->array_flags = SvFLAGS(c->array);
501 5           c->array_fill = AvFILLp(c->array);
502              
503             /* Magical array. Realise it temporarily. */
504 5 100         if (SvRMAGICAL(c->array)) {
505 1           c->copy = (SV**) malloc (c->len * sizeof *(c->copy));
506 6 100         for (x = 0; x < c->len; x++) {
507 5           SV **svp = av_fetch(c->array, x, FALSE);
508 5 50         c->copy[x] = (svp) ? SvREFCNT_inc(*svp) : &PL_sv_undef;
509             }
510 1           SvRMAGICAL_off(c->array);
511 1           AvARRAY_set(c->array, c->copy);
512 1           AvFILLp(c->array) = c->len - 1;
513             } else {
514 4           c->copy = 0;
515             }
516            
517 5           SvREADONLY_on(c->array); /* Can't change the array during permute */
518            
519             /* Allocate memory for the engine to scribble on */
520 5           c->tmparea = (SV***) malloc((c->len + 1) * sizeof *(c->tmparea));
521 35 100         for (x = c->len; x >= 0; x--)
522 30           c->tmparea[x] = malloc(c->len * sizeof **(c->tmparea));
523            
524             {
525             dMULTICALL;
526 5 50         PUSH_MULTICALL(callback);
    50          
527 5           SAVEDESTRUCTOR(afp_destructor, c);
528 5           permute_engine(c->array, AvARRAY(c->array), 0, c->len,
529             c->tmparea, multicall_cop);
530 4 50         POP_MULTICALL;
    50          
531             }
532             }