/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd and Vincent Hayward.	    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	makePosition.c
 *
 * DESCRIPTION:	Routines for creating and freeing RCCL position equations.
 * 
 * AUTHOR/DATE:	John LLoyd, McGill, Summer 1988
 *		Original version: Vincent Hayward, 1983
 *
 *---------------------------------------------------------------------------
/*---------------------------------------------------------------------------

	The RCCL position equation is implemented as a circular linked
	list that looks somthing like this:


		t6Term			  toolTerm
		   |			     |
		 __V__        _____        __V__        _____
	+------>|_____+----->|_____+----->|_____+----->|_____+----+
	| +-----+_____|<-----+_____|<-----+_____|<-----+_____|<-+ |
	| |	|__+__|      |__+__|      |__+__|      |__+__|	| |
	| |	   |	        |            |            |	| |
	| |	   V            V            V            V	| |
	| |	dummy T6    tool trans   tool trans     fixed   | |
	| |      trans        no. 1        no. 2        trans   | |
	| |							| |
       	| |				 coordTerm 		| |
	| |				     |			| |
	| |	 _____        _____        __V__       		| |
	| +---->|_____+----->|_____+----->|_____+---------------+ |
	+-------+_____|<-----+_____|<-----+_____|<----------------+
		|__+__|      |__+__|      |__+__|      
		   |	        |            | 
		   V            V            V
	      coord trans  coord trans  coord trans
		 no. 1        no. 2        no. 3

	Each node in the list contains a pointer to preceeding and
	succeding nodes, and a pointer to the transform. The nodes
	also contain information fields describing which side of the
	defining equation the transform is located on, and whether this
	is the same side as the T6 transform.

	Each position equation is represented put into the canonocial
	form
			T6 TOOL = COORD P

	where TOOL and COORD are components that can contain several
	transforms, of which some may be time varying, and P is a constant
	transform. Any of these components may be absent, as long as there are
	enough to form a valid position equation.

	The components are delimited by special pointers within the
	position structure:

	"t6Term" points to the term containing the dummy T6 transform.
	"toolTerm" delimits the last transform of the TOOL component.
	"ccordTerm" delimits the last transform of the COORD component.
	"startTerm" points to the left most transform in the definition

	The P term is present if toolTerm->next != coordTerm.

---------------------------------------------------------------------------*/

#include <varargs.h>

#include "../h/cdefs.h"
#include "../h/rcclDefs.h"
#include "../h/errorCodes.h"

#include "../rccl/transform.h"
#include "../rccl/rcclAutoAbort.h"
#include "../rccl/rcclErrorMsgs.h"

#include "../rci/hashTable.h"

#if TEST
extern resetNameBuf();
static char *allocNameSpace();
#endif

#define MAX_TERMS	100

#if !(defined(VXWORKS_CFG) && defined(TEST))
/* Dummy locations pointed to by reserved arguments */
static TRSF _EQ;
static TRSF _TL;
static TRSF_DESC _T6;

/* Reserved arguments for denoting parts of the position equation */
TRSF *T6 = (TRSF*)&_T6;
TRSF *TRANS = (TRSF*)&_T6;
TRSF *EQ = &_EQ;
TRSF *TL = &_TL;
#endif
static initDone = 0;

/* Test to see if transform if bound to a function or not */
#define CONSTANT(t)	(TRSF_GETFXN(t->trsf) == constTrsf) 

extern char *strcpy();

/* Diagnostic messages */

static char *t6TwiceMsg		= "%s -- 'T6/TRANS' specified twice\n";
static char *eqTwiceMsg		= "%s -- 'EQ' specified twice\n";
static char *noLhsMsg		= "%s -- no left-hand-side for equation\n";
static char *noEndMsg		= "%s -- bad TRSF ptr or no TL termination\n";
static char *maxTermsMsg	= "%s -- max. no. of terms allowed is %d\n";
static char *noEqMsg		= "%s -- no 'EQ' specified\n";
static char *noRhsMsg		= "%s -- no right-hand-side given\n";
static char *noT6Msg		= "%s -- no t6 specified\n";
static char *noToolTrsfMsg	= "%s -- tool trsf not part of eqn.\n";
static char *noCoordMsg		= "%s -- eqn. has only T6 and tool parts\n";

static combineTerms();

static doinit()
{
	/* Called once per program execution to set things up */

	TRSF_DESC *tr = TRSF_GETDESC(T6);
	tr->next = NULL;
	tr->prev = NULL;
	tr->source = tr;
	tr->parent = tr;
	tr->fxn = varbTrsf;
	tr->arg = 0;
	tr->mark = 0;
	tr->manipOwner = 0;
	tr->taskOwner = 0;	
	tr->cpu = UNDEF;		/* because T6 is a dummy transform */
	tr->timestamp = 0;
	tr->magic = 0;			/* because T6 is a dummy transform */
	identTrsf (&tr->trsf);
	initDone = 1;
}

POS *makePosition (va_alist)
va_dcl
{
	/* Basic RCCL primitive to build a position data structure. */

	char *fxnName = "makePosition()";

	char *name;
	va_list ap;
	
	TRSF *tp;		/* pointer to input argument transforms */
	TRSF *TLp = NULL;	/* set when TL argument is found */
	TRSF *t6p = NULL;	/* set when T6 argument is found */

	POS *pos;		/* returned position equation */
	POS_TERM *terms;	/* start of mem. area containing pos terms */
	POS_TERM *coordt;	/* tmp pointer used to find coord term */
	POS_TERM *termp;	/* tmp term pointer */

	int termCount;		/* Total number of terms */
	int leftSide = 1;	/* start with left side of EQN */
	int t6OnLeftSide = 1;	/* until we find otherwise */

	int leftCount = 0;	/* No. of terms on LHS. */
	int rightCount = 0;	/* No. of terms on RHS. */
	int k;

	if (!planningLevelCheck(fxnName))
	 { return (NULL);
	 }
	MASK_RCI_AUTO_ABORT();

	if (!initDone)
	 { doinit();
	 }

	va_start(ap);
	name = va_arg(ap, char*);

       /* Make one pass through the equation, determine how many terms
	  here are, and check syntax */

	for (tp = va_arg(ap, TRSF*); 
	     tp != TL && tp != NULL && (leftCount + rightCount) <= MAX_TERMS; 
	     tp = va_arg(ap, TRSF*) )
	 { if (tp == T6)
	    { if (t6p)
	       { errorMsg (ESyntax, t6TwiceMsg, fxnName);
		 RETURN_ERROR (NULL);
	       }
	      else
	       { t6p = tp;
		 if (leftSide)
		  { leftCount++;
		  }
		 else
		  { rightCount++;
		    t6OnLeftSide = 0;
		  }
	       }
	    }
	   else if (tp == EQ)
	    { if (!leftSide)
	       { errorMsg (ESyntax, eqTwiceMsg, fxnName);
		 RETURN_ERROR (NULL);
	       }
	      else if (leftCount == 0)
	       { errorMsg (ESyntax, noLhsMsg, fxnName);
		 RETURN_ERROR (NULL);
	       }
	      else
	       { leftSide = 0;
	       }
	    }
	   else
	    { if (TRSF_GETDESC(tp)->magic != TRSF_MAGIC)
	       { errorMsg (EDescriptor, noEndMsg, fxnName);
		 RETURN_ERROR (NULL);
	       }
	      if (leftSide)
	       { leftCount++;
	       }
	      else
	       { rightCount++;
	       }
	    }
	 }
	if ((termCount = leftCount + rightCount) > MAX_TERMS)
	 { errorMsg (ETooMany, maxTermsMsg, fxnName, MAX_TERMS);
	   RETURN_ERROR (NULL);
	 }
	else
	 { if (tp == TL)
	    { TLp = va_arg(ap, TRSF*);
	    }
	 }
	va_end(ap);

	if (leftSide)
	 { errorMsg (ESyntax, noEqMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
	else if (rightCount == 0)
	 { errorMsg (ESyntax, noRhsMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
#if 0
	else if (!t6p)
	 { errorMsg (ESyntax, noT6Msg, fxnName);
	   RETURN_ERROR (NULL);
	 }
#endif

	/* Now allocate memory for the position structure and its terms.

	   The memory is allocated as one big block, with the POS 
	   memory coming first and the term memory coming second
	 */

	if ((pos = (POS*)allocMemId (name, POS_HASH_ID, 
				       sizeof(POS_TERM)*termCount 
				     + sizeof(POS), UNDEF)) == NULL)
	 { errorMsg (getErrorCode(0), rcclNullMsg, fxnName);
	   RETURN_ERROR (NULL);
	 }
	terms = (POS_TERM*)&pos[1];

	/* Go through and link all the terms into a circular linked list */

	for (k=0; k<termCount; k++)
	 { if (k < termCount-1)
	    { terms[k].next = &terms[k+1];
	    }
	   else
	    { terms[k].next = &terms[0];
	    }
	   if (k > 0)
	    { terms[k].prev = &terms[k-1];
	    }
	   else
	    { terms[k].prev = &terms[termCount-1];
	    }
	 }
	
	/* Now make another pass through the argument list and
	   instantiate all the terms in the equation */

	va_start(ap);
	va_arg(ap, char*);

	k = 0;
	pos->startTerm = &terms[k];
	for (tp=va_arg(ap,TRSF*); tp!=EQ; tp=va_arg(ap,TRSF*))
	 { terms[k].trsf = tp;
	   terms[k].t6Side = (t6p ? t6OnLeftSide : UNDEF);
	   terms[k].leftSide = 1;
	   k++;
	 }
	k = termCount-1;
	for (tp=va_arg(ap,TRSF*); tp!=TL && tp!=NULL; tp=va_arg(ap,TRSF*))
	 { terms[k].trsf = tp;
	   terms[k].t6Side = (t6p ? !t6OnLeftSide : UNDEF);
	   terms[k].leftSide = 0;
	   k--;
	 }
	va_end(ap);

	/* Now tie up loose ends: if there IS a T6 term, go through 
	   and find it, and also set up the tool term pointer */

	if (t6p)
	 { for (termp = terms; termp->trsf != T6; termp = termp->next)
	      ;
	   pos->t6Term = termp;
	   pos->toolTerm = NULL;
	   if (TLp)
	    { do
	       { if (termp->trsf == TLp)
		  { pos->toolTerm = termp;
		    break;
		  }
		 termp = (pos->t6Term->leftSide ? termp->next : termp->prev);
	       }
	      while (termp != pos->t6Term);
	    }
	   else
	    { pos->toolTerm = pos->t6Term;
	    }
	   if (pos->toolTerm == NULL)
	    { errorMsg (ESyntax, noToolTrsfMsg, fxnName);
	      freeMem (pos);
	      RETURN_ERROR (NULL);
	    }
	   if (pos->t6Term->leftSide)
	    { if (pos->toolTerm->next == pos->t6Term)
	       { errorMsg (ESyntax, noCoordMsg, fxnName);
		 freeMem (pos);
		 RETURN_ERROR (NULL);
	       }
	    }
	   else
	    { if (pos->toolTerm->prev == pos->t6Term)
	       { errorMsg (ESyntax, noCoordMsg, fxnName);
		 freeMem (pos);
		 RETURN_ERROR (NULL);
	       }
	    }
	 }
	else
	 { pos->t6Term = NULL;
	   pos->toolTerm = NULL;
	 }

	pos->coordfn = UNDEF;
	pos->toolfn = UNDEF;
	pos->numTerms = termCount;

	pos->global = YES;
	pos->addr = pos;

	RETURN_NORMAL (pos);
}

numOptimizedTerms (pos, shiftSet)
POS *pos;
int shiftSet;
{
	/* computes the number of terms of position structure will have
	   if all constant transforms are multiplied together. This is
	   used by the move request function movereq() when building the
	   local copy of the position equation for a particular motion
	   request.

	   If 'shiftSet' is true, that means the optimized equation will
	   also have to insert an additional offset transform at the location
	   adjacent to the tool term. If the transform already there
	   is constant, then this offset transform can be multiplied in
	   and the term count stays the same. If it is not constant, then
	   the terms count goes up by 1 because the offset transform will
	   have to be explicitly added.
	*/

	POS_TERM *term;
	POS_TERM *coordTerm;
	int termCount;
	int previousConstant;
	int t6OnLeft;

	if (!pos->t6Term)
	 { return (0);		/* XXX -- not likely to call this routine */
	 }

	termCount = 1;		/* for t6 */

	t6OnLeft = pos->t6Term->leftSide;
	coordTerm = t6OnLeft ? pos->toolTerm->next : pos->toolTerm->prev;

	/* count up terms due to tool, if any */

	previousConstant = 0;
	
	term = t6OnLeft ? pos->t6Term->next : pos->t6Term->prev;
	for (; term!=coordTerm; term=(t6OnLeft ? term->next : term->prev))
	 { if (CONSTANT(term))
	    { if (!previousConstant)
	       { termCount++;
	       }
	      previousConstant = 1;
	    }
	   else
	    { termCount++;
	      previousConstant = 0;
	    }
	 }
	
	/* count up terms due to coord part */

	previousConstant = 0;
	term = t6OnLeft ? pos->t6Term->prev : pos->t6Term->next;
	for (; term!=pos->toolTerm; term=(t6OnLeft ? term->prev : term->next))
	 { if (CONSTANT(term))
	    { if (!previousConstant)
	       { termCount++;
	       }
	      previousConstant = 1;
	    }
	   else
	    { termCount++;
	      previousConstant = 0;
	    }
	 }

	if (shiftSet && !CONSTANT(coordTerm))
	 { termCount++;
	 }

	return (termCount);
}

#if TEST
/* The TEST program uses the names of the optimized terms (which
   are normally set to NULL). */
#define ASSIGNTR(x,y,c)	\
	*(x)->trsf = *(y)->trsf; \
	TRSF_GETDESC((x)->trsf)->fxn = (c); \
	TRSF_GETDESC((x)->trsf)->source = TRSF_GETDESC((y)->trsf)->source; \
	TRSF_GETDESC((x)->trsf)->name = TRSF_GETDESC((y)->trsf)->name;
#else
#define ASSIGNTR(x,y,c) \
	*(x)->trsf = *(y)->trsf; \
	TRSF_GETDESC((x)->trsf)->fxn = (c); \
	TRSF_GETDESC((x)->trsf)->source = TRSF_GETDESC((y)->trsf)->source;
#endif


#if TEST
char *makeNewName(n1, n2, t1_leftSide, t2_leftSide, direction)
char *n1;
char *n2;
int t1_leftSide;
int t2_leftSide;
int direction;
{
	/* Used by the test program to create a new name for a
	   combined transform in the equation. The name is the 
	   juxtiposition of the names of the two component
	   trasforms. This allows us (in the TEST program) to
	   easily read off exactly what each optimized transform
	   is made of.
	*/

	int baseLength;
	char *newName;
	char *allocNameSpace();

	if (n1[0] == '(' && n1[strlen(n1)-1] == ')')
	 { n1++;
	   n1[strlen(n1)-1] = '\0';
	 }
	if (n2[0] == '(' && n2[strlen(n2)-1] == ')')
	 { n2++;
	   n2[strlen(n2)-1] = '\0';
	 }

	baseLength = strlen(n1) + strlen(n2) + 6;

	if (t1_leftSide == t2_leftSide)
	 { newName = allocNameSpace (baseLength);
	   if (t1_leftSide == direction)
	    { sprintf (newName, "(%s %s)", n1, n2);
	    }
	   else
	    { sprintf (newName, "(%s %s)", n2, n1);
	    }
	 }
	else
	 { newName = allocNameSpace (baseLength+1);
	   if (t1_leftSide == direction)
	    { sprintf (newName, "(%s %s*)", n1, n2);
	    }
	   else
	    { sprintf (newName, "(%s* %s)", n2, n1);
	    }
	 }
	return (newName);
}
#endif

makeOptimizedPosition (buf, oldp, shift, termCount)
void *buf;
POS *oldp;
TRSF *shift;
int termCount;
{
	/* Take a position equation "oldp" and build an optimized version
	   of it, in buf, where all of the constant transforms are 
	   multiplied together. This is used by "movereq()" to build
	   an optimized position equation to send down to the trajectory
	   generator. 

	   Note that the number of terms must be supplied: this is
	   computed beforehand with the routine numOptimizedTerms().

	   If 'shift' is true, then it points to an offset transform that
	   should be added to the final equation. This transform is added
	   next to the tool frame. If the transform there is already constant,
	   then it may be multiplies in. Otherwise, it has to be added as
	   an additional transform. This feature is used to implement the
	   displacement transform for the RCCL 'distance()' primitive.
	*/
	   
	POS *newp;
	POS_TERM *oldterm;
	POS_TERM *newterm;
	POS_TERM *coordTerm;
	POS_TERM *startTerm;
	POS_TERM *terms;
	TRSF_DESC *trsfs;
	int checkCount, k;
	int previousConstant = 0;
	int t6OnLeft;

	newp = (POS *)buf;
	terms = (POS_TERM*)((char*)buf + sizeof(POS));
	trsfs = (TRSF_DESC*)((char*)buf + sizeof(POS) 
					+ sizeof(POS_TERM)*termCount);

	t6OnLeft = oldp->t6Term->leftSide;
	coordTerm = t6OnLeft ? oldp->toolTerm->next : oldp->toolTerm->prev;

	newp->toolfn = 0;
	newp->coordfn = 0;
	startTerm = NULL;

	/* go through the buffer provided and build the link-list structure
	   of the position terms. We can do this in advance because we have
	   precomputed the number of optimized terms.
	*/

	for (k=0; k<termCount; k++)
	 { if (k < termCount-1)
	    { terms[k].next = &terms[k+1];
	    }
	   else
	    { terms[k].next = &terms[0];
	    }
	   if (k > 0)
	    { terms[k].prev = &terms[k-1];
	    }
	   else
	    { terms[k].prev = &terms[termCount-1];
	    }
	   bzero(&trsfs[k], sizeof(TRSF_DESC));	/* safety */
	   terms[k].trsf = (TRSF*)&trsfs[k];
	 }

	/* -- set up the t6 term -- */

	newterm = newp->t6Term = terms;
	oldterm = oldp->t6Term;
	ASSIGNTR(newterm, oldterm, varbTrsf);

	newterm->t6Side = 1;
	newterm->leftSide = oldterm->leftSide;
	if (oldterm == oldp->startTerm)
	 { startTerm = newterm;
	 }

	checkCount = 1;
	previousConstant = 0;

	/* Set up the part of the equation which coressponds to the TOOL */

	for (oldterm = t6OnLeft ? oldterm->next : oldterm->prev; 
	     oldterm != coordTerm;
	     oldterm = t6OnLeft ? oldterm->next : oldterm->prev)
	 { if (CONSTANT(oldterm))
	    { if (!previousConstant)
	       { newterm = t6OnLeft ? newterm->next : newterm->prev;
		 ASSIGNTR(newterm, oldterm, constTrsf);
		 newterm->t6Side = oldterm->t6Side;
		 newterm->leftSide = oldterm->leftSide;
		 checkCount++;
	       }
	      else
	       { combineTerms (newterm, oldterm, t6OnLeft);
	       }
	      previousConstant = 1;
	    }
	   else
	    { newterm = t6OnLeft ? newterm->next : newterm->prev;
	      ASSIGNTR(newterm, oldterm, varbTrsf);
	      newterm->t6Side = oldterm->t6Side;
	      newterm->leftSide = oldterm->leftSide;
	      newp->toolfn = 1;
	      checkCount++;
	      previousConstant = 0;
	    }
	   if (oldterm == oldp->startTerm)
	    { startTerm = newterm;
	    }
	 }

	newp->toolTerm = newterm;

	/* Set up the part of the equation which coressponds to the COORD */

	previousConstant = 0;
	newterm = newp->t6Term;

	for (oldterm = t6OnLeft ? oldp->t6Term->prev : oldp->t6Term->next;
	     oldterm != oldp->toolTerm;
	     oldterm = t6OnLeft ? oldterm->prev : oldterm->next)
	 { if (CONSTANT(oldterm))
	    { if (!previousConstant)
	       { newterm = t6OnLeft ? newterm->prev : newterm->next;
		 ASSIGNTR (newterm, oldterm, constTrsf);
		 newterm->t6Side = oldterm->t6Side;
		 newterm->leftSide = oldterm->leftSide;
		 checkCount++;
	       }
	      else
	       { combineTerms (newterm, oldterm, !t6OnLeft);
	       }
	      previousConstant = 1;
	    }
	   else
	    { newterm = t6OnLeft ? newterm->prev : newterm->next;
	      ASSIGNTR (newterm, oldterm, varbTrsf);
	      newterm->t6Side = oldterm->t6Side;
	      newterm->leftSide = oldterm->leftSide;
	      newp->coordfn = 1;
	      checkCount++;
	      previousConstant = 0;
	    }
	   if (oldterm == oldp->startTerm)
	    { startTerm = newterm;
	    }
	 }

	/* newterm now points to the last COORD term */

	if (CONSTANT(newterm))
	 { if (shift)
	    { /* fold shift transform in */
	      if (newterm->t6Side)
	       { multLiTrsf (newterm->trsf, shift, newterm->trsf);
	       }
	      else
	       { multTrsf (newterm->trsf, newterm->trsf, shift);
	       }
#if TEST	
	       { char *np = TRSF_GETDESC(newterm->trsf)->name;
		 char *newName;

		 if (np[0] == '(' && np[strlen(np)-1] == ')')
		  { np++;
		    np[strlen(np)-1] = '\0';
		  }
		 newName = allocNameSpace (strlen(np) + strlen("shift") + 5);
		 if (newterm->t6Side)
		  { sprintf (newName, "(shift* %s)", np);
		  }
		 else
		  { sprintf (newName, "(%s shift)", np);
		  }
		 TRSF_GETDESC(newterm->trsf)->name = newName;
	       }
#endif
	    }
	 }
	else
	 { if (shift)
	    { TRSF_DESC *tdesc;

	      newterm = t6OnLeft ? newterm->prev : newterm->next;
	      checkCount++;
	      tdesc = TRSF_GETDESC(newterm->trsf);
	      tdesc->source = (TRSF_DESC*)-1; /* make sure isn't referenced!*/
	      tdesc->fxn = constTrsf;

	      if ((t6OnLeft &&
		   (newterm->next==newp->t6Term || !newterm->next->t6Side))||
		  (!t6OnLeft &&
		   (newterm->prev==newp->t6Term || !newterm->prev->t6Side)))
	       { newterm->t6Side = NO;
		 newterm->leftSide = !(newp->t6Term->leftSide);
		 *newterm->trsf = *shift;
#if TEST
		 tdesc->name = "shift";
#endif
	       }
	      else
	       { newterm->t6Side = YES;
		 newterm->leftSide = (newp->t6Term->leftSide);
		 invertTrsf (newterm->trsf, shift);
#if TEST
		 tdesc->name = "shift*";
#endif
	       }
	    }
	 }

	newp->numTerms = termCount;

	if (checkCount != termCount)
	 { 
fatalMsg (-1, "makeOptimizedPosition() -- term no. mismatch (%d vs. %d)\n",
	    checkCount, termCount);
	 }
	if (startTerm == NULL)
	 { 
fatalMsg (-1, "makeOptimizedPosition() -- start term not initialized\n");
	 }
	if (!startTerm->leftSide)
	 { newp->startTerm = startTerm->next;
	 }
	else
	 { newp->startTerm = startTerm;
	 }
}

static combineTerms (t1, t2, direction)
POS_TERM *t1;
POS_TERM *t2;
int direction;
{
	/* Utility function used by the 'makeOptimizedPosition()' to
	   combine constant terms */

#if TEST
	TRSF_GETDESC(t1->trsf)->name = 
		makeNewName (TRSF_GETDESC(t1->trsf)->name,
			     TRSF_GETDESC(t2->trsf)->name,
			     t1->leftSide, t2->leftSide, direction);
#endif
	if (t1->leftSide == t2->leftSide)
	 { if (t1->leftSide == direction)
	    { multTrsf (t1->trsf, t1->trsf, t2->trsf);
	    }
	   else
	    { multTrsf (t1->trsf, t2->trsf, t1->trsf);
	    }
	 }
	else
	 { if (t1->leftSide == direction)
	    { multRiTrsf (t1->trsf, t1->trsf, t2->trsf);
	    }
	   else
	    { multLiTrsf (t1->trsf, t2->trsf, t1->trsf);
	    }
	 }
}

TRSF *bindPosition (p, t)
POS *p;
TRSF *t;
{
	/* binds a position p to a transform t */

	TRSF *old = p->t6Term->trsf;
	p->t6Term->trsf = T6;
	return(old);	
}

printPosition (p, values)
POS *p;
int values;
{	
	/* Goes through a position equation and prints out the names
	   of all the terms. If 'values' is true, then the values
	   of all the transforms are printed as well.
	 */

	POS_TERM *t;
	int k;

       	for (t = p->startTerm; t->leftSide; t = t->next)
	 { printf ("%s ", 
	     TRSF_GETDESC(t->trsf)->source == TRSF_GETDESC(T6) ? "T6" :
	     TRSF_GETDESC(t->trsf)->name == NULL ? "TEMP" :
	     TRSF_GETDESC(t->trsf)->name);
	   fflush (stdout);
	   if (t == p->toolTerm)
	    { printf ("(TL) ");
	      fflush (stdout);
	    }
	   if (t->next == p->startTerm)
	    { break;
	    }
	 }
	printf ("= ");
	for (t = p->startTerm->prev; !t->leftSide; t = t->prev)
	 { printf ("%s ",
		   TRSF_GETDESC(t->trsf)->source == TRSF_GETDESC(T6) ? "T6" :
		   TRSF_GETDESC(t->trsf)->name == NULL ? "TEMP" :
		   TRSF_GETDESC(t->trsf)->name);
	   fflush (stdout);
	   if (t == p->toolTerm)
	    { printf ("(TL) ");
	      fflush (stdout);
	    }
	   if (t == p->startTerm)
	    { break;
	    }
	 }
	putchar ('\n');
	printf ("toolfn = %s  coordfn = %s\n",
		 p->toolfn == UNDEF ? "UNDEF" :
		 p->toolfn == 0 ? "NO" : "YES",
		 p->coordfn == UNDEF ? "UNDEF" :
		 p->coordfn == 0 ? "NO" : "YES");

	if (values)
	 { 
	   for (t = p->startTerm; t->leftSide; t = t->next)
	    { TRSF_DESC *td = TRSF_GETDESC(t->trsf);

	      if (td->source != TRSF_GETDESC(T6))
	       { printf("%s:\n", td->name == NULL ? "TEMP" : td->name);
		 printTrsf ("%m\n", t->trsf);
	       }
	      if (t->next == p->startTerm)
	       { break;
	       }
	    }
	   for (t = p->startTerm->prev; !t->leftSide; t = t->prev)
	    { TRSF_DESC *td = TRSF_GETDESC(t->trsf);

	      if (td->source != TRSF_GETDESC(T6))
	       { printf("%s:\n", td->name == NULL ? "TEMP" : td->name);
		 printTrsf ("%m\n", t->trsf);
	       }
	      if (t == p->startTerm)
	       { break;
	       }
	    }
	 }
}

#if TEST
char nameBuf[0x1000];
char *nbp;

resetNameBuf ()
{
	nbp = nameBuf;
}

static char *allocNameSpace (n)
int n;
{
	char *mem = nbp;
	nbp += n;
	return (mem);
}
#endif


