/* This file is to be included in Levmar.xs * * John Lapeyre */ #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "pdl.h" #include "pdlcore.h" #include static Core* PDL; /* Structure holds core C functions */ static SV* CoreSV; /* Gets pointer to perl var holding core structure */ // following line useless, i guess // typedef void (*levmarfunc)( double *, double *, int, int, void * ) ; typedef void (*DelMagic)(pdl *, int param); static void delete_levmar_pdls(pdl* p, int param); static void default_magic(pdl *p, int pa) { p->data = 0; } static pdl* pdl_wrap(void *data, int datatype, PDL_Long dims[], int ndims, DelMagic delete_magic, int delparam); static SV *getref_pdl(pdl *it); /* We use a struct holding data for the wrapper functions to * perl fit code, LEVFUNC, and JLEVFUNC. liblevmar uses fit * functions whose the last argument is a pointer to * unpecified data. When fitting data it is normally used * for the ordinate data (t). We pass a DFP struct instead. * These functions are all re-entrant, i hope. The DFP * struct holds pdls which are created once at the start of * a fit and destroyed when it is finished. Data is not put * in the pdls here. The fit routines send different arrays * on different calls, so the pdl pointer to the data must * be set in the wrapper functions at each call. t is an * exception-- it is set by the user just once. levmar calls * DFP_check() (below) which either sets t, or points the * DFP *dat directly to t, if the wrapper functions are not * used (ie for pure C fit functions). */ // Don't remember why i called it DFP typedef struct { SV * p_sv; // sv to pass directly to perl fit subs SV * d_sv; SV * x_sv; SV * t_sv; pdl * p_pdl; // the pdl pointer pdl * d_pdl; pdl * x_pdl; pdl * t_pdl; SV* perl_fit_func; // perl refs to user's fit and jac functions SV* perl_jac_func; int datatype; } DFP; /* moved to xs // called from sub levmar DFP * _DFP_create () { DFP * dat; dat = (DFP *)malloc(sizeof( DFP )); if ( NULL == dat ) { fprintf(stderr, "Can't allocate storage for dat in DFP_create\n"); exit(1); } return dat; } */ /* // called from sub levmar int _DFP_free (DFP *dat) { if ( dat ) { free(dat); } } */ /* moved into xs void DFP_set_perl_funcs ( DFP *dat, SV* perl_fit_func, SV* perl_jac_func ) { if ( dat == NULL ) { fprintf(stderr, "DFP_set_perl_funcs got null struct\n"); exit(1); } dat->perl_fit_func = perl_fit_func; dat->perl_jac_func = perl_jac_func; } */ void DFP_create_pdls( DFP *dat, int data_type, int m, int n, int nt) { pdl *p_pdl, *x_pdl, *d_pdl, *t_pdl; SV *p_sv, *x_sv, *d_sv, *t_sv; PDL_Long mf_dims[] = {m}; int num_mf_dims = 1; PDL_Long mjac_dims[] = {m,n}; // for jacobian 'd' variable int num_mjac_dims = 2; PDL_Long n_dims[] = {n}; int num_n_dims = 1; PDL_Long nt_dims[] = {nt}; int num_nt_dims = 1; // create pdls, but with no data; p_pdl = pdl_wrap(NULL, data_type, mf_dims, num_mf_dims, delete_levmar_pdls, 0); d_pdl = pdl_wrap(NULL, data_type, mjac_dims, num_mjac_dims, delete_levmar_pdls, 0); x_pdl = pdl_wrap(NULL, data_type, n_dims, num_n_dims, delete_levmar_pdls, 0); t_pdl = pdl_wrap(NULL, data_type, nt_dims, num_nt_dims, delete_levmar_pdls, 0); p_sv = getref_pdl(p_pdl); d_sv = getref_pdl(d_pdl); x_sv = getref_pdl(x_pdl); t_sv = getref_pdl(t_pdl); // otherwise we get a memory leak sv_2mortal(p_sv); sv_2mortal(d_sv); sv_2mortal(x_sv); sv_2mortal(t_sv); dat->p_pdl = p_pdl; dat->d_pdl = d_pdl; dat->t_pdl = t_pdl; dat->x_pdl = x_pdl; dat->p_sv = p_sv; dat->d_sv = d_sv; dat->t_sv = t_sv; dat->x_sv = x_sv; } /* If dat is null then no DFP struct was created because we * are using a C function and it will expect t as the last * argument (and dat is passed as the last argument). If * dat is non-null, it will hold a struct with all the pdls * and we must put t in its proper place. */ void DFP_check(DFP **dat, int data_type, int m, int n, int nt, void *t ) { if ( *dat != NULL) { DFP_create_pdls( *dat, data_type, m, n, nt); (*dat)->t_pdl->data = t; } else { // Pure C routine that don't need this wrapper, expect t as last arg *dat = (DFP *)t; // cast in case compiler complains. } } // Code from PDL::API docs. For creating a pdl and giving it // data storage allocated elswhere. static pdl* pdl_wrap(void *data, int datatype, PDL_Long dims[], int ndims, DelMagic delete_magic, int delparam) { pdl* npdl = PDL->pdlnew(); /* get the empty container */ PDL->setdims(npdl,dims,ndims); /* set dims */ npdl->datatype = datatype; /* and data type */ npdl->data = data; /* point it to your data */ /* make sure the core doesn't meddle with your data */ npdl->state |= PDL_DONTTOUCHDATA | PDL_ALLOCATED; if (delete_magic != NULL) { PDL->add_deletedata_magic(npdl, delete_magic, delparam); } else PDL->add_deletedata_magic(npdl, default_magic, 0); return npdl; } // Don't free data, it is allocated inside liblevmar static void delete_levmar_pdls(pdl* p, int param) { if (p->data) { // free(p->data); } else { } p->data = 0; } /* Had to cut and paste from pdlcore.c * Is this routine somehow otherwise available ? * This returns SV *ref, giving access to the * pdl *it as a perl scalar. */ static SV *getref_pdl(pdl *it) { SV *newref; if(!it->sv) { SV *ref; HV *stash = gv_stashpv("PDL",TRUE); SV *psv = newSViv(PTR2IV(it)); it->sv = psv; newref = newRV_noinc(it->sv); (void)sv_bless(newref,stash); } else { newref = newRV_inc(it->sv); SvAMAGIC_on(newref); } return newref; } //----------------------------------------------------- // Modified Function from pdl gsl interp code // It follows the perlembed doc example closely void LEVFUNC(double *p, double *x, int m, int n, DFP *dat) { int count; dSP; dat->p_pdl->data = p; dat->x_pdl->data = x; ENTER; SAVETMPS; PUSHMARK(SP); // Dont make mortal, they come from outside this routine XPUSHs(dat->p_sv); XPUSHs(dat->x_sv); XPUSHs(dat->t_sv); PUTBACK; count=call_sv(dat->perl_fit_func,G_SCALAR); SPAGAIN; if (count!=1) croak("error calling perl function\n"); PUTBACK; FREETMPS; LEAVE; } void JLEVFUNC(double *p, double *d, int m, int n, DFP *dat) { int count; dat->p_pdl->data = p; dat->d_pdl->data = d; dSP; ENTER; SAVETMPS; PUSHMARK(SP); XPUSHs(dat->p_sv); XPUSHs(dat->d_sv); XPUSHs(dat->t_sv); PUTBACK; count=call_sv(dat->perl_jac_func,G_SCALAR); SPAGAIN; if (count!=1) croak("error calling perl function\n"); PUTBACK; FREETMPS; LEAVE; }