/****************************************************************************
 *
 * $Id: pdl.c,v 1.5 2004/06/08 22:10:02 dburke Exp $
 *
 * pdl.c
 *   PDL support for Inline::SLang (at least the utility functions,
 *   since some PDL-specific code will appear in other files)
 *
 ****************************************************************************/

#include "util.h"
#include "pdl.h"

/* Should only ever be compiled if I_SL_HAVE_PDL is 1 so do not need to check */

/*
 * access the PDL internals
 * - this is essentially the output of
 *     use PDL::Core::Dev; print &PDL_AUTO_INCLUDE();
 */
Core* PDL;   /* Structure holds core C functions */
SV* CoreSV;  /* Gets pointer to perl var holding core structure */

/*
 * initialize the pointers that will allow us to call PDL functions 
 * - this is called from the BOOT section of the XS code
 *   we do it this way so that the PDL-related variables can be
 *   localised to this file (which now seems a bit pointless
 *   as they're no longer static)
 */
void initialize_pdl_core( void ) {

  /*
   * a 'use PDL::LiteF;' in SLang.pm would be simpler...
   * - using a flags setting of 0 (ie rather than PERL_LOADMOD_NOIMPORT)
   *   causes problems on OS-X and Linux [but not Solaris].
   *   I find the perl docs on this somewhat opaque; this was the
   *   first flag I guessed at using other than 0...
   */
  load_module( PERL_LOADMOD_NOIMPORT, newSVpv("PDL::Lite",0), NULL );

  /* 
   * this code fragment is essentially the output of:
   *    use PDL::Core::Dev; print &PDL_BOOT();
   * minus the require_pv line and the aTHX_ defines
   */
  CoreSV = perl_get_sv("PDL::SHARE",FALSE);
  if( NULL == CoreSV )
    Perl_croak(aTHX_ "The Inline::SLang module requires the PDL::Core module, which was not found");
  PDL = INT2PTR(Core*,SvIV( CoreSV ));
  if ( PDL_CORE_VERSION != PDL->Version )
    Perl_croak(aTHX_ "The Inline::SLang module needs to be recompiled against the latest installed PDL");

} /* initialize_pdl_core() */

/* Used by sl2pl.c - convert a S-Lang array into a piddle */

/*
 * see 'pdldoc API' for explanation of what's going on here
 *
 * SV *sl2pl_array_pdl( SLang_Array_Type * )
 *   convert the S-Lang array into a piddle
 *
 * void pl2sl_array_pdl( SV * )
 *   convert the piddle into a S-Lang array and
 *   push this array onto the S-Lang stack
 */

SV *
sl2pl_array_pdl( SLang_Array_Type *at ) {

  PDL_Long dims[SLARRAY_MAX_DIMS];
  pdl  *out;
  SV *sv;
  size_t dsize;
  int i;

  /*
   * copy over the dims
   * (we reverse them since PDL and S-Lang use different
   *  array-access schemes)
   */
  Printf( ("*** converting S-Lang array tp Piddle: ndims=%d [", at->num_dims) );
  for ( i = 0; i < at->num_dims; i++ ) {
    dims[at->num_dims-1-i] = at->dims[i];
    Printf( (" %d", at->dims[i]) );
  }
  Printf( ("] dtype=%d", at->data_type) );

  /* should we check for failure? */
  out = PDL->pdlnew();
  PDL->setdims( out, dims, at->num_dims );
#include "topdl.h"

  /*
   * copy the memory from the array since I don't know when S-Lang may delete it
   * (would be quicker to just point to it but that leads to memory-managment issues)
   *
   * It is not entirely clear to me from the S-Lang docs whether I can safely
   * access the data field of the array directly.
   */
  PDL->allocdata( out );
  (void) memcpy( out->data, at->data, (size_t) at->num_elements * dsize );

  /* covert the piddle into a 'SV *' */
  sv = sv_newmortal();
  PDL->SetSV_PDL( sv, out );
  SvREFCNT_inc( sv );
  return sv;

} /* sl2pl_array_pdl() */

void
pl2sl_array_pdl( SV *item ) {

  int dims[SLARRAY_MAX_DIMS];
  SLang_Array_Type *at;
  pdl *pdl;
  SLtype otype;
  size_t dsize;
  int i;

  /*
   * we have a piddle. I appear to have to call PDL->make_physdims()
   * on it (eg if it is a slice of another piddle), but is that 
   * all, or should I call PDL.make_physvaffine() instead?
   *
   * If we do have a piddle that is just a transformation of another
   * one then I 'cheat' and make it physical; in that way we can use
   * memcpy() to copy the data across rather than process the
   * transformation ourselves. It would be nice if there were a function
   * in the PDL API which would copy the contents of a "virtual" piddle
   * into a contiguous block of memory. Maybe there is one?
   *
   */
  pdl = PDL->SvPDLV(item);
  PDL->make_physdims(pdl);
  /* PDL->make_physvaffine(pdl); - do not need this since call make_physical() below */

  if ( pdl->ndims > SLARRAY_MAX_DIMS )
    croak( "Error: max number of dimensions for a S-Lang array is %d",
	   SLARRAY_MAX_DIMS );
  
  if ( pdl->ndims == 0 )
    croak( "Error: S-Lang does not allow a 0d array - perhaps should promote to 1d or convert to a scalar?" );
  
  /*
   * as in sl2pl_array_pdl() we need to reverse the dimensions
   */
  Printf( ("*** converting Piddle to S-Lang: ndims=%d [", pdl->ndims) );
  for ( i = 0; i < pdl->ndims; i++ ) {
    dims[pdl->ndims-1-i] = pdl->dims[i];
    Printf( (" %d", dims[i]) );
  }
  Printf( ("] dtype=%d", pdl->datatype) );

#include "toslang.h"

  Printf( (" -> %s\n", SLclass_get_datatype_name(otype)) );
  at = SLang_create_array( otype, 0, NULL, dims, pdl->ndims );
  if ( at == NULL )
    croak( "Error: Unable to create a S-Lang array of %ld elements",
	   pdl->nvals );

  /* copy over the data */
  if ( pdl->trans ) {
    /*
     * hack to make things easier for us; ensure that pdl->data contains
     * the actual data
     */
    Printf( ("*** NOTE: calling PDL->make_physical") );
    PDL->make_physical(pdl);
  }
  (void) memcpy( at->data, pdl->data, (size_t) pdl->nvals * dsize );

  /* stick array on the stack */
  (void) SLang_push_array (at, 1);

} /* pl2sl_array_pdl() */

/* pdl.c */
