示例#1
0
文件: tprod_mex.c 项目: wpli/dialog
/*
  This function computes the generalised matrix products.  Basicially it
  works by spliting the input X and Y matrices into 2 *virtual* sub-matrices,
  one for the "outer" product dimensions (x/y rest) (over which the cartesian
  product is computed) and one for the "inner" product matrices (over which
  the inner product - or multiply accumulate - is computed).  Once these 2
  matrices have been computed the result is simply an outer product in
  tprod and inner product in macc */
void mexFunction(const int nlhs, mxArray *plhs[], 
                 const int nrhs, const mxArray *prhs[]) {
  char msgTxt[256];
  int i, j, maccnd=0, seqnd=0, BLKSZ=DEFAULTBLKSZ, indtype, retVal;
  const int xarg=0, ydimspecarg=3;
  int xdimspecarg=0, yarg=0; /* possibly other way round? */
  bool useMATLAB=true;
  int callType=0;
  MxInfo xinfo, yinfo, zinfo;
  MxInfo xmacc, ymacc, zrest, xrest, yrest;
  int znd, xnidx=0, ynidx=0;
  int *x2yIdx=0;
  if (nrhs < 4 || nrhs >6)	 ERROR("tprod: Incorrect number of inputs.");
  if (nlhs > 1)	 ERROR("tprod: Too many output arguments.");
  if ( mxGetNumberOfDimensions(prhs[ydimspecarg]) > 2 )
	 ERROR("tprod: ydimspec must be a vector");
  /* parse the tprod options list */
  if( nrhs >= 5 && mxIsChar(prhs[4]) ) {
	 char *opnmStr=mxArrayToString(prhs[4]);
	 for (i=0 ; opnmStr[i] != 0; i++ ) {
		switch ( opnmStr[i] ) {
		case 'm': case 'M':   useMATLAB=false;  break;
		case 'n': case 'N':   callType=1;    break; /* new call type */
		case 'o': case 'O':   callType=-1;   break; /* old call type */
		default: 
		  WARNING("tprod: Unrecognised tprod option");
		}
	 }
	 mxFree(opnmStr); opnmStr=0;
  }

  if ( nrhs==6 && mxGetNumberOfElements(prhs[5])==1 ){
	 BLKSZ=(int)mxGetScalar(prhs[5]);
  }

  /* Get X */
  xinfo = mkmxInfo(prhs[xarg],0);
  /* remove trailing singlenton dimensions from x and y */
  for( i=xinfo.nd; i>1; i-- ) if ( xinfo.sz[i-1]!=1 ) break;  xinfo.nd=i;

  /*----------------------------------------------------------------------*/
  /* Identify the calling convention used */
  if ( callType == 0 ) {

	 /* Try and identify the calling convention used, i.e. 
		 X,Y,xdimspec,ydimspec, or X,xdimspec,Y,ydimspec (deprecated) */
	 if ( mxGetNumberOfDimensions(prhs[1])==2 &&		
			( (mxGetN(prhs[1])==1 && mxGetM(prhs[1])>=xinfo.nd) /* Xdimspec OK */
			  || (mxGetN(prhs[1])>=xinfo.nd && mxGetM(prhs[1])==1) ) ) {
		yarg  = 2; /* start by trying new call type */
		int ynd = mxGetNumberOfDimensions(prhs[yarg]);
		const int *ysz = mxGetDimensions(prhs[yarg]); /* size of poss Y*/
		for( i=ynd; i>1; i-- ) if ( ysz[i-1]!=1 ) break; ynd=i; 
		if( mxGetNumberOfElements(prhs[ydimspecarg]) >= ynd ){/*Ydimspec OK*/
		  callType = 1 ;  /* new call type */
		}
	 }
	 if( mxGetNumberOfDimensions(prhs[2])==2 &&
		  ((mxGetN(prhs[2])==1 && mxGetM(prhs[2])>=xinfo.nd)/* xdimspec OK */
			|| (mxGetN(prhs[2])>=xinfo.nd && mxGetM(prhs[2])==1) ) ) {
		/* Consitent so far, check the ydimspec is OK */
		yarg  = 1; /* start by trying new call type */
		int ynd = mxGetNumberOfDimensions(prhs[yarg]);
		const int *ysz = mxGetDimensions(prhs[yarg]); /* size of poss Y*/
		for( i=ynd; i>1; i-- ) if ( ysz[i-1]!=1 ) break; ynd=i; 
		if ( mxGetNumberOfElements(prhs[ydimspecarg]) >= ynd ) {
		  
		  if ( callType == 0 ) { /* argument 3 is CONSISTENT, and 2 *WASN'T* */
			 callType = -1; 
			 
		  } else { /* argument 2 consistent ALSO */
			 /* of one of them matches exactly and the other doesn't */
			 int xnd = mxGetNumberOfDimensions(prhs[xarg]); /* num input X dims */
			 if ( xnd==2 && xinfo.nd == 1 ) xnd=1; /* col vec is special case*/
			 if ( xnd == mxGetNumberOfElements(prhs[1]) /* 1 matches *exactly* */
					&& xnd != mxGetNumberOfElements(prhs[2]) ) { /* 2 doesn't */
				callType = 1;
			 } else if( xnd == mxGetNumberOfElements(prhs[2])/* 2 *exact* match */
							&& xnd != mxGetNumberOfElements(prhs[1]) ){/* 1 doesn't */
				callType = -1;
			 } else { /* neither or both match exactly */
				callType = 1;
				WARNING("tprod: Could not unambigiously determine calling convention, tprod(X,xdimspec,Y,ydimspec) assumed. Use 'n' or 'o' to force new/old convention.");
			 }
		  }
		}
	 } 
  }
  switch ( callType ) {
  case 1:  xdimspecarg=1; yarg=2; break;/* new type: X,xdimspec,Y,xdimspec */
  case -1: xdimspecarg=2; yarg=1; break;/* old type: X,Y,xdimspec,xdimspec */
  default: ERROR("tprod: Couldnt identify calling convention.");
  }

  /* Now we know where the Y is we can get it too! */
  yinfo = mkmxInfo(prhs[yarg],0); 
  /* empty set as input for y means make it copy of x */
  if ( yinfo.numel==0 && yinfo.nd==2 && yinfo.sz[0]==0 && yinfo.sz[1]==0 ) { 
	 yinfo=copymxInfo(xinfo); 
  }
  /* remove trailing singlenton dimensions from x and y */
  for( i=yinfo.nd; i>1; i-- ) if ( yinfo.sz[i-1]!=1 ) break;  yinfo.nd=i;

  /* check the types are what we can use */
  if ( !(xinfo.dtype == DOUBLE_DTYPE || xinfo.dtype == SINGLE_DTYPE ) ){
	 ERROR("tprod: X type unsuppored: only double, single");
  }
  if ( !(yinfo.dtype == DOUBLE_DTYPE || yinfo.dtype == SINGLE_DTYPE ) ){
	 ERROR("tprod: Y type unsuppored: only double, single");
  }
     
  /*-------------------------------------------------------------------------
   * Initialise x2yIdx which maps from input dimensions to the type of output
	* we want.  The format of x2yIdx is:
	*   [Xdimlabels Ydimlabels] 
	* so it reflects the order of the dimensions in the output Z.
   * The value and sign of the integer label tells us what type of
   * operation to perform on this dimension.
	*    0   this is a squeezed dim which must be singlenton
	*   -ve  this is the index in X/Y of the matching dim to be accumulated
	*   +ve  this number is the position of the dimension at this location in
   *        the output matrix.
	*-------------------------------------------------------------------------*/
  znd=0; /* xnidx, ynidx; */
  double *xip, *yip;
  /* fill in the x2yIdx for the new type of indicies */
  maccnd=0;
  xnidx=mxGetNumberOfElements(prhs[xdimspecarg]);
  if( xnidx<xinfo.nd ) ERROR("tprod:Less X indicies than dimensions");
  ynidx=mxGetNumberOfElements(prhs[ydimspecarg]);
  if( ynidx<yinfo.nd ) ERROR("tprod:Less Y indicies than dimensions");

  x2yIdx=(int*)mxCalloc(xnidx+ynidx,sizeof(int));  	 
  xip=mxGetPr(prhs[xdimspecarg]); yip=mxGetPr(prhs[ydimspecarg]);

  /* find the max value of xip, this is num output dims */
  /* also check for non multiple instances of acc dims labels */
  znd=MAX((int)xip[0],0);
  for( i=1; i<xnidx; i++){
	 if ( xip[i] < .0 ) {
		for( j=0; j<i; j++)
		  if(xip[i]==xip[j]) ERROR("tprod: Duplicate x-dim label");
	 } else if ( xip[i] > .0 ) {
		znd=MAX(znd,(int)xip[i]); /* find the largest target dim */
	 } else if ( sz(xinfo,i)!=1 ) 
		ERROR("tprod: Ignored dims *MUST* have size 1");
  }
  /* same for yip */
  /* but also check that we always have matching x label to accumulate */
  znd=MAX(znd,(int)yip[0]); 
  for( i=1; i<ynidx; i++){
	 if ( yip[i] < .0 ) {
		for( j=0; j<i; j++)
		  if(yip[i]==yip[j]) ERROR("tprod: Duplicate y-dim label");
		for( j=0; j<xnidx; j++) if( yip[i]==xip[j] ) break;
		if( j==xnidx ) {
		  sprintf(msgTxt,"tprod: Couldn't find a matching negative x-label for the y-label=%d",(int)yip[i]);
		  ERROR(msgTxt);
		}
	 } else if ( yip[i] > .0 ) {
		znd=MAX(znd,(int)yip[i]); /* find the largest target dim */
	 } else if ( sz(yinfo,i)!=1 ) 
		ERROR("tprod: Ignored dims *MUST* have size 1");
  }

  /* compute the x->y mapping */
  for( i=0; i<xnidx; i++){
	 if ( xip[i] < .0 ) {
		/* search for the matching y */
		for( j=0; j<ynidx; j++) {
		  if ( yip[j]==xip[i] ) {
			 x2yIdx[i]=-(j+1);    x2yIdx[xnidx+j]=-(i+1);    maccnd++;
			 break;
		  }
		}
		if ( x2yIdx[i]==0 )
		  ERROR("tprod: Couldn't find a matching y-idx for the x");
		if( sz(xinfo,i) != sz(yinfo,j)) /* check sizes match too */
		  ERROR("tprod: Matched dims must have the same size!");
	 } else { /* just copy it through */
		x2yIdx[i]=(int)xip[i];
		/* search for the matching y, & check sizes match */
		for( j=0; j<ynidx && yip[j]!=xip[i]; j++);
		if ( j<ynidx ){ /* sequential dimension */
		  if ( sz(xinfo,i) != sz(yinfo,j) )
			 ERROR("tprod: Matched dims must have the same size!");
		  if ( sz(xinfo,i)!=1 ) { /* only *really* sequ if >1 size strides */
			 seqnd++; 
		  } 
		}
	 }
  }
  /* now set the y parts -- for the non-set non-accumulated dimensions */ 
  for( i=0; i<ynidx; i++){ 
	 if( yip[i] > .0 ) { x2yIdx[i+xnidx]=(int)yip[i]; }
  }
  /*   } */
  znd=MAX(znd,1); /* ensure at least 1 output dim */
  maccnd=MAX(maccnd,1); /* ensure at least 1 macc dim */
  
  /* compute the mxInfo for the accumulated and rest sub-matrices */  
  xmacc = mkemptymxInfo(maccnd);
  ymacc = mkemptymxInfo(maccnd);
  /* N.B. xrest.sz holds the	size of the combined x and y rest matrix */
  xrest = mkemptymxInfo(znd); 
  yrest = mkemptymxInfo(znd);

  initrestmaccmxInfo(znd, xinfo, yinfo, x2yIdx, xnidx, ynidx,
							&xrest, &yrest, &xmacc, &ymacc);

  /* compute the size of the output matrix */
  zinfo=initzmxInfo(znd, xinfo, yinfo, x2yIdx, xnidx, ynidx); 

  /* optimise/standardize the query so its the way tprod wants it */
  zrest = copymxInfo(zinfo);
  optimisetprodQuery(&zrest, &xrest, &yrest, &xmacc, &ymacc);  
  if ( xrest.rp != xinfo.rp ) { /* swap xinfo/yinfo if needed */
	 MxInfo tmp = xinfo; xinfo=yinfo; yinfo=tmp;
  }

  /* Now do the actuall work to compute the result */  
  if ( yinfo.numel==0 || xinfo.numel== 0 ) { /* deal with null inputs */
	 WARNING("tprod: Empty matrix input!");
	 /* return an empty matrix */
	 plhs[0]=mxCreateNumericArray(zinfo.nd,zinfo.sz,mxDOUBLE_CLASS,
											(xinfo.ip==0&&yinfo.ip==0)?mxREAL:mxCOMPLEX);
	 	 
  } else if ( useMATLAB && /* allowed */
		 seqnd==0 &&                     /* no sequential dims */
		 xmacc.nd <= 1 &&            /* at most 1 macc dim */
		 xrest.nd <= 2 &&            /* at most 2 output dims */
		 (xrest.nd<= 1 ||            /* 1 from X */
		  ((xrest.stride[0]==0) || (stride(xrest,1)==0))) && 
 		 (yrest.nd<= 1 ||            /* 1 from Y */
		  ((yrest.stride[0]==0) || (stride(yrest,1)==0))) &&
				  (xmacc.numel*(2+(xinfo.ip==0)+(yinfo.ip==0)) < MATLABMACCTHRESHOLDSIZE ) /* not tooo big! */
				  ){ 
	 /* Phew! we can use matlab! */
	 if ( xrest.stride[0]>0 ) { /* x comes before y in output */
		plhs[0]=MATLAB_mm(zinfo,xinfo,yinfo,xrest,yrest,
								xmacc,ymacc);
	 } else { /* y comes before x in output, reverse order of inputs */
		plhs[0]=MATLAB_mm(zinfo,yinfo,xinfo,yrest,xrest,
								ymacc,xmacc);
	 }
  } else {

	 /* otherwise do it ourselves */
	 /* create the data for the z matrix and set its pointer */
	 plhs[0]=mxCreateNumericArray(zinfo.nd,zinfo.sz,zinfo.dtype,
											(xinfo.ip==0&&yinfo.ip==0)?mxREAL:mxCOMPLEX);
	 zinfo.rp = mxGetPr(plhs[0]); zrest.rp=zinfo.rp;
	 zinfo.ip = mxGetPi(plhs[0]); zrest.ip=zinfo.ip;

	 /* call tprod to do the real work */
	 /* do the (appropriately typed) operation */
	 if(        xrest.dtype==DOUBLE_DTYPE && yrest.dtype==DOUBLE_DTYPE ) {/*dd*/
		retVal= ddtprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else if( xrest.dtype==DOUBLE_DTYPE && yrest.dtype==SINGLE_DTYPE ) {/*ds*/
		retVal= dstprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else if( xrest.dtype==SINGLE_DTYPE && yrest.dtype==DOUBLE_DTYPE ) {/*sd*/
		retVal= sdtprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);
 
	 } else if( xrest.dtype==SINGLE_DTYPE && yrest.dtype==SINGLE_DTYPE ){/*ss*/
		retVal= sstprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else {
 		ERROR("tprod: Inputs of unsupported type: only double/single");
		
	 }
	 /* check for errors */
	 switch ( retVal ) {
	 case ZTYPEMISMATCH : 
		ERROR("tprod: Z is of unsupported type"); break;
	 case XTYPEMISMATCH :
		ERROR("tprod: X is of unsupported type"); break;
	 case YTYPEMISMATCH :
		ERROR("tprod: Y is of unsupported type"); break;
	 case INTYPEMISMATCH :
		ERROR("tprod: input real/complex mix unsupported"); break;
	 case OTHERERROR :
		ERROR("tprod: Something else went wrong, sorry!"); break;
	 default: ;
	 }
  }
  
  /* ensure the output has the size we want */
  if ( plhs[0] ) mxSetDimensions(plhs[0],zinfo.sz,zinfo.nd);
  
  /* free up all the memory we've allocated */
  /* N.B. not clear we need to do this from the matlab web-site should happen
	  automatically */
  delmxInfo(&xinfo);delmxInfo(&yinfo);delmxInfo(&zinfo);
  delmxInfo(&xmacc);  delmxInfo(&ymacc);
  delmxInfo(&xrest);  delmxInfo(&yrest);
  FREE(x2yIdx);
}
示例#2
0
/*
  This function computes the generalised matrix products.  Basicially it
  works by spliting the input X and Y matrices into 2 *virtual* sub-matrices,
  one for the "outer" product dimensions (x/y rest) (over which the cartesian
  product is computed) and one for the "inner" product matrices (over which
  the inner product - or multiply accumulate - is computed).  Once these 2
  matrices have been computed the result is simply an outer product in
  tprod and inner product in macc */
void mexFunction(const int nlhs, mxArray *plhs[], 
                 const int nrhs, const mxArray *prhs[]) {
  int i, maccnd=0, seqnd=0, BLKSZ=DEFAULTBLKSZ, err=OK; 
  const int xargi=0, ydimspecarg=3;
  int xdimspecarg=0, yargi=0; /* possibly other way round? */
  bool useMATLAB=true;
  int callType=0;
  MxInfo xinfo, yinfo, zinfo;
  MxInfo xmacc, ymacc, zrest, xrest, yrest;
  int znd, xnidx=0, ynidx=0;
  int *x2yIdx=0;
  if (nrhs < 4 || nrhs >6)	 { ERROR("tprod: Incorrect number of inputs."); err=OTHERERROR; }
  if (nlhs > 1) { ERROR("tprod: Too many output arguments."); err=OTHERERROR; }
  if ( mxGetNumberOfDimensions(prhs[ydimspecarg]) > 2 ){
	 ERROR("tprod: ydimspec must be a vector"); err=OTHERERROR; }
  /* parse the tprod options list */
  if( nrhs >= 5 && mxIsChar(prhs[4]) ) {
	 char *opnmStr=mxArrayToString(prhs[4]);
	 for (i=0 ; opnmStr[i] != 0; i++ ) {
		switch ( opnmStr[i] ) {
		case 'm': case 'M':   useMATLAB=false;  break;
		case 'n': case 'N':   callType=1;    break; /* new call type */
		case 'o': case 'O':   callType=-1;   break; /* old call type */
		default: 
		  WARNING("tprod: Unrecognised tprod option");
		}
	 }
	 mxFree(opnmStr); opnmStr=0;
  }

  if ( nrhs==6 && mxGetNumberOfElements(prhs[5])==1 ){
	 BLKSZ=(int)mxGetScalar(prhs[5]);
  }

  /* Get X */
  xinfo = mkmxInfoMxArray(prhs[xargi],0);
  /* remove trailing singlenton dimensions from x and y */
  for( i=xinfo.nd; i>1; i-- ) if ( xinfo.sz[i-1]!=1 ) break;  xinfo.nd=i;

  /*----------------------------------------------------------------------*/
  /* Identify the calling convention used */
  if ( callType == 0 ) {

	 /* Try and identify the calling convention used, i.e. 
		 X,Y,xdimspec,ydimspec, or X,xdimspec,Y,ydimspec (deprecated) */
	 if ( mxGetNumberOfDimensions(prhs[1])==2 &&		
			( (mxGetN(prhs[1])==1 && mxGetM(prhs[1])>=xinfo.nd) /* Xdimspec OK */
			  || (mxGetN(prhs[1])>=xinfo.nd && mxGetM(prhs[1])==1) ) ) {
		int ynd = 0; 
		const int *ysz = 0;
		yargi = 2; /* start by trying new call type */
		ynd=mxGetNumberOfDimensions(prhs[yargi]);
		ysz = mxGetDimensions(prhs[yargi]); /* size of poss Y*/
		for( i=ynd; i>1; i-- ) if ( ysz[i-1]!=1 ) break; ynd=i; 
		if( mxGetNumberOfElements(prhs[ydimspecarg]) >= ynd ){/*Ydimspec OK*/
		  callType = 1 ;  /* new call type */
		}
	 }
	 if( mxGetNumberOfDimensions(prhs[2])==2 &&
		  ((mxGetN(prhs[2])==1 && mxGetM(prhs[2])>=xinfo.nd)/* xdimspec OK */
			|| (mxGetN(prhs[2])>=xinfo.nd && mxGetM(prhs[2])==1) ) ) {
		/* Consitent so far, check the ydimspec is OK */
		int ynd = 0; 
		const int *ysz = 0;
		yargi  = 1; /* start by trying new call type */
		ynd=mxGetNumberOfDimensions(prhs[yargi]);
		ysz = mxGetDimensions(prhs[yargi]); /* size of poss Y*/
		for( i=ynd; i>1; i-- ) if ( ysz[i-1]!=1 ) break; ynd=i; 
		if ( mxGetNumberOfElements(prhs[ydimspecarg]) >= ynd ) {
		  
		  if ( callType == 0 ) { /* argument 3 is CONSISTENT, and 2 *WASN'T* */
			 callType = -1; 
			 
		  } else { /* argument 2 consistent ALSO */
			 /* of one of them matches exactly and the other doesn't */
			 int xnd = mxGetNumberOfDimensions(prhs[xargi]); /* num input X dims */
			 if ( xnd==2 && xinfo.nd == 1 ) xnd=1; /* col vec is special case*/
			 if ( xnd == mxGetNumberOfElements(prhs[1]) /* 1 matches *exactly* */
					&& xnd != mxGetNumberOfElements(prhs[2]) ) { /* 2 doesn't */
				callType = 1;
			 } else if( xnd == mxGetNumberOfElements(prhs[2])/* 2 *exact* match */
							&& xnd != mxGetNumberOfElements(prhs[1]) ){/* 1 doesn't */
				callType = -1;
			 } else { /* neither or both match exactly */
				callType = 1;
				WARNING("tprod: Could not unambigiously determine calling convention, tprod(X,xdimspec,Y,ydimspec) assumed. Use 'n' or 'o' to force new/old convention.");
			 }
		  }
		}
	 } 
  }
  switch ( callType ) {
  case 1:  xdimspecarg=1; yargi=2; break;/* new type: X,xdimspec,Y,xdimspec */
  case -1: xdimspecarg=2; yargi=1; break;/* old type: X,Y,xdimspec,xdimspec */
  default: ERROR("tprod: Couldnt identify calling convention."); err=OTHERERROR;
  }

  /* Now we know where the Y is we can get it too! */
  yinfo = mkmxInfoMxArray(prhs[yargi],0); 
  /* empty set as input for y means make it copy of x */
  if ( yinfo.numel==0 && yinfo.nd==2 && yinfo.sz[0]==0 && yinfo.sz[1]==0 ) { 
	 yinfo=copymxInfo(xinfo); 
  }
  /* remove trailing singlenton dimensions from x and y */
  for( i=yinfo.nd; i>1; i-- ) if ( yinfo.sz[i-1]!=1 ) break;  yinfo.nd=i;

  /* check the types are what we can use */
  if ( !(xinfo.dtype == DOUBLE_DTYPE || xinfo.dtype == SINGLE_DTYPE ) ){
	 ERROR("tprod: X type unsuppored: only full double/single"); err=UNSUPPORTEDINPUTS;
  }
  if ( mxIsSparse(prhs[xargi]) ){
	 ERROR("tprod: X is sparse, only full double/single supported"); err=UNSUPPORTEDINPUTS;
  }
  if ( !(yinfo.dtype == DOUBLE_DTYPE || yinfo.dtype == SINGLE_DTYPE ) ){
	 ERROR("tprod: Y type unsuppored: only double, single"); err=UNSUPPORTEDINPUTS;
  }
  if ( mxIsSparse(prhs[yargi]) ){
	 ERROR("tprod: X is sparse, only full double/single supported"); err=UNSUPPORTEDINPUTS;
  }
       
  /* fill in the x2yIdx for the new type of indicies */
  maccnd=0;
  xnidx=mxGetNumberOfElements(prhs[xdimspecarg]);
  ynidx=mxGetNumberOfElements(prhs[ydimspecarg]);
  err = compx2yIdx_dd(xinfo,xnidx,mxGetPr(prhs[xdimspecarg]), 
								 yinfo,ynidx,mxGetPr(prhs[ydimspecarg]),
								 &x2yIdx,&znd,&maccnd,&seqnd);
  if ( err != 0 ) { /* FREE and return */
	 delmxInfo(&xinfo);  delmxInfo(&yinfo);
	 if ( x2yIdx != 0 ) FREE(x2yIdx); 
	 return ; 
  }

  #ifdef LOGGING  
  FILE *fd = fopen("/tmp/tprod.log","a");
  if( fd==0 ){WARNING("Couldn't open log file /tmp/tprod.log\n"); fd=stderr;}
  fprintf(fd,"tprod( ");
  printMxInfoSummary(fd,xinfo);fprintf(fd," ,[");
  for(i=0; i<xnidx; i++) fprintf(fd,"%d ", (int)mxGetPr(prhs[xdimspecarg])[i]);
  fprintf(fd,"] , ");
  printMxInfoSummary(fd,yinfo);fprintf(fd," ,[");  
  for(i=0; i<ynidx; i++) fprintf(fd,"%d ", (int)mxGetPr(prhs[ydimspecarg])[i]);
  fprintf(fd,"] )\n"); 
  if ( fd!=stderr ) fclose(fd);
  #endif

  /* compute the mxInfo for the accumulated and rest sub-matrices */  
  xmacc = mkemptymxInfo(maccnd);
  ymacc = mkemptymxInfo(maccnd);
  /* N.B. xrest.sz holds the	size of the combined x and y rest matrix */
  xrest = mkemptymxInfo(znd); 
  yrest = mkemptymxInfo(znd);

  err = initrestmaccmxInfo(znd, xinfo, yinfo, 
										x2yIdx, xnidx, ynidx,
										&xrest, &yrest, &xmacc, &ymacc);
  if ( err != 0 ) { /* FREE and return */
	 delmxInfo(&xinfo);  delmxInfo(&yinfo);
	 delmxInfo(&xmacc);  delmxInfo(&ymacc);
	 delmxInfo(&xrest);  delmxInfo(&yrest);
	 FREE(x2yIdx);
	 return; 
  }

  /* compute the size of the output matrix */
  zinfo=initzmxInfo(znd, xinfo, yinfo, x2yIdx, xnidx, ynidx); 

  /* optimise/standardize the query so its the way tprod wants it */
  zrest = copymxInfo(zinfo);
  err =  optimisetprodQuery(&zrest, &xrest, &yrest, 
										 &xmacc, &ymacc);  
  if ( err != OK ) { /* free everything and return */
	 delmxInfo(&xinfo);  delmxInfo(&yinfo); delmxInfo(&zinfo);
	 delmxInfo(&xmacc);  delmxInfo(&ymacc);
	 delmxInfo(&xrest);  delmxInfo(&yrest); delmxInfo(&zrest);
	 FREE(x2yIdx);
	 return; 
  }
  
  if ( xrest.rp != xinfo.rp ) { /* swap xinfo/yinfo if needed */
	 MxInfo tmp = xinfo; xinfo=yinfo; yinfo=tmp;
  }

  /* Now do the actuall work to compute the result */  
  if ( yinfo.numel==0 || xinfo.numel== 0 ) { /* deal with null inputs */
	 WARNING("tprod: Empty matrix input!");
	 /* return an empty matrix */
	 plhs[0]=mxCreateNumericArray(zinfo.nd,zinfo.sz,mxDOUBLE_CLASS,
											(xinfo.ip==0&&yinfo.ip==0)?mxREAL:mxCOMPLEX);
	 	 
  } else if ( useMATLAB && /* allowed */
		 seqnd==0 &&                     /* no sequential dims */
		 xmacc.nd <= 1 &&            /* at most 1 macc dim */
		 xrest.nd <= 2 &&            /* at most 2 output dims */
		 (xrest.nd<= 1 ||            /* 1 from X */
		  ((xrest.stride[0]==0) || (stride(xrest,1)==0))) && 
 		 (yrest.nd<= 1 ||            /* 1 from Y */
		  ((yrest.stride[0]==0) || (stride(yrest,1)==0))) &&
				  (xmacc.numel*(2+(xinfo.ip==0)+(yinfo.ip==0)) < MATLABMACCTHRESHOLDSIZE ) /* not tooo big! */
				  ){ 
	 /* Phew! we can use matlab! */
	 if ( xrest.stride[0]>0 ) { /* x comes before y in output */
		plhs[0]=MATLAB_mm(zinfo,xinfo,yinfo,xrest,yrest,
								xmacc,ymacc);
	 } else { /* y comes before x in output, reverse order of inputs */
		plhs[0]=MATLAB_mm(zinfo,yinfo,xinfo,yrest,xrest,
								ymacc,xmacc);
	 }
  } else {

	 /* otherwise do it ourselves */
	 /* create the data for the z matrix and set its pointer */
	 plhs[0]=mxCreateNumericArray(zinfo.nd,zinfo.sz,zinfo.dtype,
											(xinfo.ip==0&&yinfo.ip==0)?mxREAL:mxCOMPLEX);
	 zinfo.rp = mxGetPr(plhs[0]); zrest.rp=zinfo.rp;
	 zinfo.ip = mxGetPi(plhs[0]); zrest.ip=zinfo.ip;

	 /* call tprod to do the real work */
	 /* do the (appropriately typed) operation */
	 if(        xrest.dtype==DOUBLE_DTYPE && yrest.dtype==DOUBLE_DTYPE ) {/*dd*/
		err= ddtprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else if( xrest.dtype==DOUBLE_DTYPE && yrest.dtype==SINGLE_DTYPE ) {/*ds*/
		err= dstprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else if( xrest.dtype==SINGLE_DTYPE && yrest.dtype==DOUBLE_DTYPE ) {/*sd*/
		err= sdtprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);
 
	 } else if( xrest.dtype==SINGLE_DTYPE && yrest.dtype==SINGLE_DTYPE ){/*ss*/
		err= sstprod(zrest,xrest,yrest,xmacc,ymacc,BLKSZ);

	 } else {
		err= UNSUPPORTEDINPUTS;
		
	 }
	 /* check for errors */
	 switch ( err ) {
	 case ZTYPEMISMATCH : 
		ERROR("tprod: Z is of unsupported type"); break;
	 case XTYPEMISMATCH :
		ERROR("tprod: X is of unsupported type"); break;
	 case YTYPEMISMATCH :
		ERROR("tprod: Y is of unsupported type"); break;
	 case INTYPEMISMATCH :
		ERROR("tprod: input real/complex mix unsupported"); break;
	 case OTHERERROR :
		ERROR("tprod: Something else went wrong, sorry!"); break;
	 case UNSUPPORTEDINPUTS :
 		ERROR("tprod: Inputs of unsupported type: only double/single"); break;
	 default: ;
	 }
  }
  
  /* ensure the output has the size we want */
  if ( err==OK && plhs[0] ) mxSetDimensions(plhs[0],zinfo.sz,zinfo.nd);
  
  /* free up all the memory we've allocated */
  /* N.B. not clear we need to do this from the matlab web-site should happen
	  automatically */
  delmxInfo(&xinfo);delmxInfo(&yinfo);delmxInfo(&zinfo);
  delmxInfo(&xmacc);  delmxInfo(&ymacc);
  delmxInfo(&xrest);  delmxInfo(&yrest);
  FREE(x2yIdx);
}