Algorithm-Permute
view release on metacpan or search on metacpan
pred[n] = ptr_head;
}
#else
if (loc[n] < n) {
/* swap adjacent */
p[loc[n]] = p[loc[n] + 1];
p[++loc[n]] = n;
} else {
is_done = _next(n - 1, p, loc);
/* then shift right */
for (i = n - 1; i >= 1; i--)
p[i + 1] = p[i];
/* adjust both extremes */
p[1] = n;
loc[n] = 1;
}
#endif
return is_done;
}
/* permute_engine() and afp_destructor() are from Robin Houston
* <robin@kitsite.com> */
void permute_engine(
AV* av,
SV** array,
I32 level,
I32 len, SV*** tmparea, OP* callback)
{
SV** copy = tmparea[level];
int index = level;
bool calling = (index + 1 == len);
SV* tmp;
Copy(array, copy, len, SV*);
if (calling)
AvARRAY_set(av, copy);
do {
if (calling) {
PL_op = callback;
CALLRUNOPS(aTHX);
}
else {
permute_engine(av, copy, level + 1, len, tmparea, callback);
}
if (index != 0) {
tmp = copy[index];
copy[index] = copy[index - 1];
copy[index - 1] = tmp;
}
} while (index-- > 0);
}
struct afp_cache {
SV*** tmparea;
AV* array;
I32 len;
SV** array_array;
U32 array_flags;
SSize_t array_fill;
SV** copy; /* Non-magical SV list for magical array */
};
static
void afp_destructor(void *cache)
{
struct afp_cache *c = cache;
I32 x;
/* PerlIO_stdoutf("DESTROY!\n"); */
for (x = c->len; x >= 0; x--) free(c->tmparea[x]);
free(c->tmparea);
if (c->copy) {
for (x = 0; x < c->len; x++) SvREFCNT_dec(c->copy[x]);
free(c->copy);
}
AvARRAY_set(c->array, c->array_array);
SvFLAGS(c->array) = c->array_flags;
AvFILLp(c->array) = c->array_fill;
free(c);
}
static
bool reset_combination(Permute *self, AV *av, UV r) {
UV n;
COMBINATION *c = NULL;
if ((n = av_len(av) + 1) == 0)
return 0;
c = init_combination(n, r, av);
/* PerlIO_stdoutf("passed init_combination()\n"); */
if (c == NULL) {
warn("Unable to initialize combination");
return 0;
}
self->c = c;
coollex(self->c);
coollex_visit(self->c, self->items + 1); /* base of items is 1 */
return 1;
}
MODULE = Algorithm::Permute PACKAGE = Algorithm::Permute
PROTOTYPES: DISABLE
Permute*
new(CLASS, av, ...)
char *CLASS
AV *av
PREINIT:
UV i, num;
UV r, n;
UV has_combination;
#ifdef USE_LINKEDLIST
listrecord *q; /* temporary holder */
#endif
CODE:
RETVAL = (Permute*) safemalloc(sizeof(Permute));
if (RETVAL == NULL) {
warn("Unable to create an instance of Algorithm::Permute");
XSRETURN_UNDEF;
}
RETVAL->is_done = FALSE;
if ((n = av_len(av) + 1) == 0)
XSRETURN_UNDEF;
/* init combination if necessary */
has_combination = 0;
RETVAL->c = NULL;
num = n;
if (items > 2) {
r = SvUV(ST(2));
if (r > n) {
warn("Number of combination must be less or equal the number of elements");
XSRETURN_UNDEF;
}
CODE:
self->is_done = FALSE;
reset_combination(self, (AV*)(SvRV(self->aryref)), self->num);
#ifdef USE_LINKEDLIST
q = self->ptr_head;
for (i = 1; i <= self->num; i++) {
q = q->link;
q->info = self->num - i + 1;
self->pred[i] = self->ptr_head;
}
assert(q->link == NULL);
#else
for (i = 1; i <= self->num; i++) {
*(self->p + i) = self->num - i + 1;
*(self->loc + i) = 1;
}
#endif
void
permute(callback_sv, array_sv)
SV* callback_sv;
SV* array_sv;
PROTOTYPE: &\@
PREINIT:
CV* callback;
GV* agv;
I32 x;
PERL_CONTEXT* cx;
I32 gimme = G_VOID; /* We call our callback in VOID context */
bool old_catch;
struct afp_cache *c;
I32 hasargs = 0;
SV** newsp;
PPCODE:
{
if (!SvROK(callback_sv) || SvTYPE(SvRV(callback_sv)) != SVt_PVCV)
Perl_croak(aTHX_ "Callback is not a CODE reference");
if (!SvROK(array_sv) || SvTYPE(SvRV(array_sv)) != SVt_PVAV)
Perl_croak(aTHX_ "Array is not an ARRAY reference");
c = malloc(sizeof(struct afp_cache));
callback = (CV*)SvRV(callback_sv);
c->array = (AV*)SvRV(array_sv);
c->len = 1 + av_len(c->array);
agv = gv_fetchpv("A", TRUE, SVt_PVAV);
SAVESPTR(GvSV(agv));
if (SvREADONLY(c->array))
Perl_croak(aTHX_ "Can't permute a read-only array");
if (c->len == 0) {
/* Should we warn here? */
free(c);
return;
}
c->array_array = AvARRAY(c->array);
c->array_flags = SvFLAGS(c->array);
c->array_fill = AvFILLp(c->array);
/* Magical array. Realise it temporarily. */
if (SvRMAGICAL(c->array)) {
c->copy = (SV**) malloc (c->len * sizeof *(c->copy));
for (x = 0; x < c->len; x++) {
SV **svp = av_fetch(c->array, x, FALSE);
c->copy[x] = (svp) ? SvREFCNT_inc(*svp) : &PL_sv_undef;
}
SvRMAGICAL_off(c->array);
AvARRAY_set(c->array, c->copy);
AvFILLp(c->array) = c->len - 1;
} else {
c->copy = 0;
}
SvREADONLY_on(c->array); /* Can't change the array during permute */
/* Allocate memory for the engine to scribble on */
c->tmparea = (SV***) malloc((c->len + 1) * sizeof *(c->tmparea));
for (x = c->len; x >= 0; x--)
c->tmparea[x] = malloc(c->len * sizeof **(c->tmparea));
{
dMULTICALL;
PUSH_MULTICALL(callback);
SAVEDESTRUCTOR(afp_destructor, c);
permute_engine(c->array, AvARRAY(c->array), 0, c->len,
c->tmparea, multicall_cop);
POP_MULTICALL;
}
}
( run in 3.435 seconds using v1.01-cache-2.11-cpan-cdf2f3d4e48 )