/* pdlfamily.c - functions for manipulating pdl families
in order to get dataflow with mutators (+=, ++..) work. */
/*-----------------------------------------*/
/* Here comes the hard part: we need to
* be able to do "+=" or other mutators.
* If the same pdl is given as input and output to some routine,
* it has to be copied if threading is enabled.
*
* Therefore, we define a "family" of PDLs as a set of pdls
* that are forward and backward propagated to each other.
* What needs to happen is:
* $b = $a->slice(foo);
* $b += 3;
*
* Orig: hash($a) -> a0, hash($b) -> b0
* Now: hash($a) -> a1, hash($b) -> b1,
* a0 and b0 saved in memory with tmp hashes,
* transformation between them duplicated.
*
* Because $b was a slice (and not, for example, inversion),
* it only covers a part of the original $a and therefore we
* need to first assign a0 to a1 and then add 3 from b0 to b1
* (or b1 to b1).
*
*/
#define PDL_CORE /* For certain ifdefs */
#include "pdl.h" /* Data structure declarations */
#include "pdlcore.h"
static void pdl_identity_redodims(pdl_trans *__tr);
static void pdl_identity_readdata(pdl_trans *__tr);
#define pdl_identity_trans pdl_trans
/* This is the function that cleverly changes who perl thinks
we are. This should actually be implemented a little differently
so that different places in perl could hold on to their ref
if they wanted. But for now, */
void pdl__xchghashes(pdl *a,pdl *b)
{
STRLEN n_a;
SV *t;
void *d;
HV *tmp = a->sv;
a->sv = b->sv;
b->sv = tmp;
if(a->sv) sv_setiv(a->sv,(IV) a);
if(b->sv) sv_setiv(b->sv,(IV) b);
t = a->datasv;
a->datasv = b->datasv;
b->datasv = t;
a->data = (a->datasv?SvPV((SV*)a->datasv,n_a):NULL);
b->data = (b->datasv?SvPV((SV*)b->datasv,n_a):NULL);
}
/* Recurse everywhere and set progenitor */
void pdl_family_setprogenitor(pdl *what,pdl *progenitor,pdl_trans *notthis)
{
PDL_DECL_CHILDLOOP(what)
pdl_trans *t; int i;
what->progenitor = progenitor;
PDL_START_CHILDLOOP(what)
t = PDL_CHILDLOOP_THISCHILD(what);
if(t!= notthis && (t->flags & PDL_ITRANS_DO_DATAFLOW_B)) {
for(i=t->vtable->nparents; i<t->vtable->npdls; i++) {
pdl_family_setprogenitor(t->pdls[i],progenitor,notthis);
}
}
PDL_END_CHILDLOOP(what)
}
/* Because make_physdims(mutateto) calls our make_physdims again,
need to set !dimschanged */
static void family_redodims(pdl_trans *tr)
{
int i;
pdl_family_trans *tr2 = (pdl_family_trans *)tr;
pdl_identity_redodims(tr);
tr2->pdls[1]->state &= ~(PDL_PARENTDIMSCHANGED | PDL_PARENTREPRCHANGED);
if(tr2->mutateto != tr2->pdls[1]) {
pdl_make_physdims(tr2->mutatefrom);
pdl_make_physdims(tr2->mutateto);
}
pdl__ensure_transdims(tr2->realtrans);
}
static void family_readdata(pdl_trans *tr)
{
pdl_family_trans *tr2 = (pdl_family_trans *)tr;
PDLDEBUG_f(printf("Family_readdata %d -> %d (%d)\n",tr->pdls[0],tr->pdls[1],tr));
pdl_identity_readdata(tr);
if(tr2->mutateto != tr2->pdls[1]) {
tr2->pdls[1]->state &= ~PDL_ANYCHANGED; /* Avoid infinite loops */
pdl_make_physical(tr2->mutateto);
}
pdl__ensure_trans(tr2->realtrans,PDL_PARENTDATACHANGED);
if(tr2->mutateto != tr2->pdls[1]) {
pdl_changed(tr2->mutateto, PDL_PARENTDATACHANGED,0);
}
PDLDEBUG_f(printf("Family_readdata_exit %d -> %d (%d)\n",tr->pdls[0],tr->pdls[1],tr));
}
static char fam_flags[] = {0,0};
static pdl_transvtable familyvtable = {
PDL_FUNCTION, 0, 1, 2,
fam_flags,
family_redodims,
family_readdata,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
sizeof(pdl_family_trans),
"Family virtual trans"
};
pdl *pdl_family_clone2now(pdl *from)
{
pdl *new_parent;
pdl_trans *ntrans;
int i;
pdl *newpdl;
if(from->future_me) return from->future_me;
/* Create new pdl */
newpdl = pdl_hard_copy(from);
newpdl->state |= PDL_PARENTDATACHANGED
| PDL_PARENTDIMSCHANGED; /* Mutator -> it's changed */
newpdl->living_for |= PDL_LIVINGFOR_FAMILY_NEWMUTATED;
/* Es ist vollbracht */
from->future_me = newpdl;
pdl__xchghashes(from,newpdl);
/* Am I progenitor */
if(from->progenitor == from) {
return newpdl; /* Do nothing */
} else {
/* If not, does my parent have a future thing? */
if(!from->trans->pdls[0]->future_me) {
pdl_family_clone2now(from->trans->pdls[0]);
}
new_parent = from->trans->pdls[0]->future_me;
if(!from->trans->vtable->copy) {
die("Cannot copy source transformation!!!\n");
}
ntrans = from->trans->vtable->copy(from->trans);
/* Set the new transformation for us */
for(i=0; i<ntrans->vtable->npdls; i++) {
if(ntrans->pdls[i] == from->trans->pdls[0]) {
pdl_set_trans_childtrans(new_parent,
ntrans, i);
}
}
for(i=0; i<ntrans->vtable->npdls; i++) {
if(ntrans->pdls[i] == from) {
pdl_set_trans_parenttrans(newpdl,
ntrans, i);
}
}
}
return newpdl;
}
void pdl_family_create(pdl *from,pdl_trans *trans,int ind1,int ind2)
{
pdl *cur = from;
pdl *to;
pdl *progfrom; pdl *progto; /* Progenitors */
pdl_family_trans *famtrans;
/* 1. find the progenitor of "from". Only 1-parent transformations
* allowed for now */
while(1) {
if(!cur->trans) break;
if(!(cur->trans->flags & PDL_ITRANS_DO_DATAFLOW_B)) break;
if(cur->trans->vtable->nparents != 1) {
die("Cannot mutate a pdl begotten from more than one progenitors\n");
}
if(cur->progenitor || cur->future_me) {
die("Mutating the mutated! Internal error!\n");
}
cur = cur->trans->pdls[0];
}
progfrom = cur;
/* 2. Go to all the reversible children and mark them
* and set their progenitor */
pdl_family_setprogenitor(progfrom,progfrom,trans);
/* 3. Make the clones */
progto = pdl_family_clone2now(progfrom);
progto->living_for |= PDL_LIVINGFOR_FAMILY_NEWPROGENITOR;
to = pdl_family_clone2now(from);
to->living_for |= PDL_LIVINGFOR_FAMILY_NEWMUTATED;
/* 4. Attach the clone transformation to the correct things */
famtrans = malloc(sizeof(pdl_family_trans));
PDL_TR_SETMAGIC(famtrans);
famtrans->flags = 0;
famtrans->vtable = &familyvtable;
famtrans->freeproc = NULL;
famtrans->realtrans = trans;
trans->flags |= PDL_ITRANS_FORFAMILY;
famtrans->mutateto = to;
famtrans->mutatefrom = from;
pdl_set_trans_childtrans(progfrom,(pdl_trans *)famtrans,0);
pdl_set_trans_parenttrans(progto,(pdl_trans *)famtrans,1);
famtrans->flags &= ~PDL_ITRANS_DO_DATAFLOW_B;
/* 5. Set the child transformation to do horrible things */
if(ind1>=0)
trans->pdls[ind1] = from;
trans->pdls[ind2] = to;
}
/*=============================================================*/
/* XXX Copied from Slices.xs */
static void pdl_identity_redodims(pdl_trans *__tr) {
int __dim;
pdl_identity_trans *__priv = (pdl_identity_trans *)
__tr;
pdl *__it = __tr->pdls[1];
pdl *__parent = __tr->pdls[0];
{
pdl_reallocdims(__it,__parent->ndims);
for(__dim=0; __dim<__it->ndims; __dim++) { __it->dims[__dim] = __parent->dims[__dim]; }
pdl_setdims_careful(__it);
;}
__it->threadids[0] = __it->ndims;
}
static void pdl_identity_readdata(pdl_trans *__tr) {
int __dim;
pdl_identity_trans *__priv = (pdl_identity_trans *)
__tr;
pdl *__it = __tr->pdls[1];
pdl *__parent = __tr->pdls[0];
{
{int *__myinds = pdl_malloc(sizeof(int)*__it->ndims);
int *__parentinds = pdl_malloc(sizeof(int)*__parent->ndims);
int __parentoffs; int __myoffs=0;
int __stop = 0; int __ind;
for(__ind = 0; __ind < __it->ndims; __ind++)
__myinds[__ind] = 0;
while(!__stop) {
for(__ind=0; __ind<__parent->ndims; __ind++)
__parentinds[__ind] = __myinds[__ind];;__parentoffs=0;
for(__ind=0; __ind<__parent->ndims; __ind++) {
__parentoffs += __parent->dimincs[__ind]*__parentinds[__ind];
}
pdl_put_offs(__it, __myoffs,
pdl_get_offs(__parent, __parentoffs));
__myoffs++;
__stop=1;
for(__ind=0; __ind < __it->ndims; __ind++) {
__myinds[__ind]++;
if(__myinds[__ind] >= __it->dims[__ind]) {
__myinds[__ind] = 0;
} else {__stop = 0; break;}
}
}
/* free(__myinds); */
}
}
}