/*****************************************************************************
*   "Irit" - the 3d (not only polygonal) solid modeller.		     *
*									     *
* Written by:  Gershon Elber				Ver 0.2, Mar. 1990   *
******************************************************************************
*   Module to handle overloaded operators for the input parser module.	     *
* Note this module should be accessed by Input Parser only (InptPrsr.c).     *
*****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include "program.h"
#include "allocate.h"
#include "attribut.h"
#include "booleang.h"
#include "freeform.h"
#include "geomat3d.h"
#include "inptprsg.h"
#include "inptprsl.h"
#include "objects.h"
#include "overload.h"

/* The following table help to decide if the operand are legal for the given */
/* operator. 5 entries for PLUS, MINUS, MULT, DIV, POWER. Each entry is a    */
/* square matrix of number of object types by number of object types:	     */

static IritExprType OverLoadDiadicTable[5][11][11] =
/*    POLY_EXPR	 NUMERIC_EXPR  POINT_EXPR    VECTOR_EXPR  PLANE_EXPR  MATRIX_EXPR  CURVE_EXPR  SURFACE_EXPR   STRING_EXPR  OLST_EXPR    CTLPT_EXPR */
{ { /* PLUS */
    { POLY_EXPR, NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NUMERIC_EXPR, NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       STRING_EXPR, NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    MATRIX_EXPR, NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,	   CURVE_EXPR, NO_EXPR,       NO_EXPR,     NO_EXPR,     CURVE_EXPR },
    { POLY_EXPR, NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   STRING_EXPR,  NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       STRING_EXPR, NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     OLST_EXPR,   NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     CURVE_EXPR, NO_EXPR,       NO_EXPR,     NO_EXPR,     CURVE_EXPR } },

  { /* MINUS */
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NUMERIC_EXPR, NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    MATRIX_EXPR, NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,	   NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR } },

  { /* MULT */
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    POLY_EXPR,   NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NUMERIC_EXPR, POINT_EXPR,   VECTOR_EXPR, NO_EXPR,    MATRIX_EXPR, NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 POINT_EXPR,   NUMERIC_EXPR, NUMERIC_EXPR,NO_EXPR,    POINT_EXPR,  NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 VECTOR_EXPR,  NUMERIC_EXPR, NUMERIC_EXPR,NO_EXPR,    VECTOR_EXPR, NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { POLY_EXPR, MATRIX_EXPR,  POINT_EXPR,   VECTOR_EXPR, NO_EXPR,    MATRIX_EXPR, CURVE_EXPR, SURFACE_EXPR,  NO_EXPR,     OLST_EXPR,   NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    CURVE_EXPR,  NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    SURFACE_EXPR,NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    OLST_EXPR,   NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR } },

  { /* DIV */
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NUMERIC_EXPR, NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,	   NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { POLY_EXPR, NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,      NO_EXPR,     NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR } },

  { /* POWER */
    { POLY_EXPR, NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NUMERIC_EXPR, NO_EXPR,	     NO_EXPR,	  NO_EXPR,    MATRIX_EXPR, NO_EXPR,    NO_EXPR,       STRING_EXPR, NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      VECTOR_EXPR,  VECTOR_EXPR, NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 MATRIX_EXPR,  NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,	 NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,	   NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { POLY_EXPR, NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    POLY_EXPR,     NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   STRING_EXPR,  NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       STRING_EXPR, NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR },
    { NO_EXPR,   NO_EXPR,      NO_EXPR,	     NO_EXPR,	  NO_EXPR,    NO_EXPR,     NO_EXPR,    NO_EXPR,       NO_EXPR,     NO_EXPR,     NO_EXPR } },
};

/* The following table help to decide if the operand are legal for the given */
/* operator. 1 entry for UNARMINUS. Each entry is a linear vector of length  */
/* of number of object types:						     */

static IritExprType OverLoadMonadicTable[1][11] =
/*  POLY_EXPR  NUMERIC_EXPR  POINT_EXPR  VECTOR_EXPR  PLANE_EXPR  MATRIX_EXPR  CURVE_EXPR  SURFACE_EXPR  STRING_EXPR  OLST_EXPR  CTLPT_EXPR */
{ /* UNARMINUS */
  { POLY_EXPR, NUMERIC_EXPR, POINT_EXPR, VECTOR_EXPR, PLANE_EXPR, MATRIX_EXPR, CURVE_EXPR, SURFACE_EXPR, NO_EXPR,     NO_EXPR,   NO_EXPR }
};

static int OverLoadTypeCheckAux(int Operator, IritExprType Right,
				IritExprType Left, IritExprType *Result);
static IPObjectStruct *ConvSrfToPolys(ParseTree *Srf);

/*****************************************************************************
*   Routine to perform type checking on the overloaded operators. Returns    *
* TRUE if legal, and set Result to returned object type.		     *
*   Allows multiple types (I.e. VECTOR_EXPR | MATRIX_EXPR is legal input).   *
*   Note output Result may be multiple types as well is a such case.	     *
*****************************************************************************/
int OverLoadTypeCheck(int Operator, IritExprType Right, IritExprType Left,
							IritExprType *Result)
{
    int i, j, NumRightTypes, NumLeftTypes;
    IritExprType TmpResult;

    /* Compute how many types are feasible here (input). */
    for (i = 1, NumRightTypes = 0; i < ERROR_EXPR; i <<= 1)
	NumRightTypes += (i & Right) != 0;
    for (i = 1, NumLeftTypes = 0; i < ERROR_EXPR; i <<= 1)
	NumLeftTypes += (i & Left) != 0;

    if (NumLeftTypes < 1 || NumRightTypes < 1)
	return FALSE;
    else if (NumLeftTypes == 1 && NumRightTypes == 1)
	return OverLoadTypeCheckAux(Operator, Right, Left, Result);
    else {
	/* More than one type in the input - compute union of the output     */
    	/* types and return the union.					     */
	*Result = 0;
    	for (i = 1; i < ERROR_EXPR; i <<= 1)
	    if ((i & Right) != 0)
		for (j = 1; j < ERROR_EXPR; j <<= 1)
		    if ((j & Left) != 0)
			if (OverLoadTypeCheckAux(Operator,
						 (IritExprType) i,
						 (IritExprType) j,
						 &TmpResult))
			    *Result |= TmpResult;

	return *Result != 0;
    }
}

/*****************************************************************************
*   Routine to perform type checking on the overloaded operators. Returns    *
* TRUE if legal, and set Result to returned object type.		     *
*   Assumes a single type (I.e. VECTOR_EXPR | MATRIX_EXPR is illegal input). *
*****************************************************************************/
static int OverLoadTypeCheckAux(int Operator, IritExprType Right,
				IritExprType Left, IritExprType *Result)
{
    int i, IRight, ILeft;

    for (i = 1, IRight = 0; i < Right; i <<= 1, IRight++);
    for (i = 1, ILeft  = 0; i < Left;  i <<= 1, ILeft++);

    switch (Operator) {
	case PLUS:
	case MINUS:
	case MULT:
	case DIV:
	case POWER:
	    *Result = OverLoadDiadicTable[Operator - OPERATORS_OFFSET]
					 [IRight][ILeft];
	    return *Result != NO_EXPR;
	case UNARMINUS:
	    *Result = OverLoadMonadicTable[0][IRight];
	    return *Result != NO_EXPR;
	default:
	    IritFatalError("OverLoadTypeCheck: undefined operator");
    }
    return FALSE;				    /* Makes warning silent. */
}

/*****************************************************************************
*   Routine to evaluate	an monadic or diadic expression. It is assumed the   *
* two operands are valid for the given expression - a test which can be made *
* using OverLoadTypeCheck routine (see above). returned pointer to node with *
* the result, NULL in case of error (should not happen actually).	     *
* Root holds the simple sub-expression (Root has the operator). PObj holds   *
* all the currently defined objects available in the system. IError & CError *
* will be set to relevant error if was one.				     *
*****************************************************************************/
ParseTree *OverLoadEvalOper(ParseTree *Root, ParseTree *TempR,
		ParseTree *TempL, InptPrsrEvalErrType *IError, char *CError)
{
    int i,
	OperReversed = FALSE;
    char *Str;
    RealType R;
    ParseTree *Temp,
	*RetVal = Root;
    IPObjectStruct *TempLObj, *TempRObj;

    switch (Root -> NodeKind) {		      /* Dies if undefined operator. */
	case PLUS:
	case MINUS:
	case MULT:
	case DIV:
	case POWER:
	    if (TempR == NULL ||
		TempL == NULL ||
		TempR -> PObj == NULL ||
		TempL -> PObj == NULL)
		return NULL;					   /* Error! */
	    break;

	case UNARMINUS:
	    if (TempR == NULL || TempR -> PObj == NULL)
		return NULL;					   /* Error! */
	    break;

	default:
	    IritFatalError("OverLoadEvalOper: Undefined operator");
    }

    /* Make TempL be bigger, so we need handle less cases. */
    if (Root -> NodeKind != UNARMINUS &&
	((int) TempR -> PObj -> ObjType) > ((int) TempL -> PObj -> ObjType)) {
	Temp = TempR;
	TempR = TempL;
	TempL = Temp;

	OperReversed = TRUE;
    }

    switch (Root -> NodeKind) {
	case PLUS:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root -> PObj = GenNUMValObject(TempL -> PObj -> U.R +
					       TempR -> PObj -> U.R);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_STR_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_STRING, NULL);
		Str = Root -> PObj -> U.Str;
		if (OperReversed) {
		    sprintf(Str, "%d", (int) (TempR -> PObj -> U.R));
		    strncpy(&Str[strlen(Str)],
			    TempL -> PObj -> U.Str, LINE_LEN - 1);
		}
		else {
		    strncpy(Str, TempL -> PObj -> U.Str, LINE_LEN - 1);
		    sprintf(&Str[strlen(Str)], "%d",
			    (int) (TempR -> PObj -> U.R));
		}
	    }
	    else if (IS_PT_NODE(TempR) && IS_PT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_ADD(Root -> PObj -> U.Vec,
			TempL -> PObj -> U.Pt, TempR -> PObj -> U.Pt);
	    }
	    else if (IS_PT_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_ADD(Root -> PObj -> U.Vec,
			TempL -> PObj -> U.Vec, TempR -> PObj -> U.Pt);
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_ADD(Root -> PObj -> U.Vec,
			TempL -> PObj -> U.Vec, TempR -> PObj -> U.Vec);
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
                MatAddTwo4by4(*Root -> PObj -> U.Mat,
			      *TempL -> PObj -> U.Mat,
			      *TempR -> PObj -> U.Mat);
	    }
	    else if ((IS_POLY_NODE(TempR) || IS_SRF_NODE(TempR)) &&
		     (IS_POLY_NODE(TempL) || IS_SRF_NODE(TempL))) {
		TempLObj = ConvSrfToPolys(TempL);
		TempRObj = ConvSrfToPolys(TempR);
		Root -> PObj = BooleanOR(TempLObj, TempRObj);
		if (Root -> PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BOOLEAN_ERR;
		    UpdateCharError("Operator ", PLUS);
		    RetVal = NULL;
		}
	    }
	    else if (IS_STR_NODE(TempR) && IS_STR_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_STRING, NULL);
		Str = Root -> PObj -> U.Str;
		sprintf(Str, "%s%s", TempL -> PObj -> U.Str,
				     TempR -> PObj -> U.Str);
	    }
	    else if (IS_OLST_NODE(TempR) && IS_OLST_NODE(TempL)) {
	    	Root -> PObj = AppendLists(TempL -> PObj, TempR -> PObj);
	    }
	    else if ((IS_CTLPT_NODE(TempR) || IS_CRV_NODE(TempR)) &&
		     (IS_CTLPT_NODE(TempL) || IS_CRV_NODE(TempL))) {
		if (OperReversed)
		    Root -> PObj = MergeCurvesAndCtlPoints(TempR -> PObj,
							   TempL -> PObj);
		else
		    Root -> PObj = MergeCurvesAndCtlPoints(TempL -> PObj,
							   TempR -> PObj);
	    }
	    else
		RetVal = NULL;
	    break;
	case MINUS:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root -> PObj = GenNUMValObject(TempL -> PObj -> U.R -
					       TempR -> PObj -> U.R);
	    }
	    else if (IS_PT_NODE(TempR) && IS_PT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_SUB(Root -> PObj -> U.Vec,
		       TempL -> PObj -> U.Pt, TempR -> PObj -> U.Pt);
	    }
	    else if (IS_PT_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_SUB(Root -> PObj -> U.Vec,
		       TempL -> PObj -> U.Vec, TempR -> PObj -> U.Pt);
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_SUB(Root -> PObj -> U.Vec,
		       TempL -> PObj -> U.Vec, TempR -> PObj -> U.Vec);
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
                MatSubTwo4by4(*Root -> PObj -> U.Mat,
			      *TempL -> PObj -> U.Mat,
			      *TempR -> PObj -> U.Mat);
	    }
	    else if ((IS_POLY_NODE(TempR) || IS_SRF_NODE(TempR)) &&
		     (IS_POLY_NODE(TempL) || IS_SRF_NODE(TempL))) {
		TempLObj = ConvSrfToPolys(TempL);
		TempRObj = ConvSrfToPolys(TempR);
		Root -> PObj = BooleanSUB(TempLObj, TempRObj);
		if (Root -> PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BOOLEAN_ERR;
		    UpdateCharError("Operator ", MINUS);
		    RetVal = NULL;
		}
	    }
	    else
		RetVal = NULL;
	    break;
	case MULT:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {
		Root -> PObj = GenNUMValObject(TempL -> PObj -> U.R *
					       TempR -> PObj -> U.R);
	    }
	    else if (IS_PT_NODE(TempR) && IS_PT_NODE(TempL)) {
		Root -> PObj =
		    GenNUMValObject(DOT_PROD(TempL -> PObj -> U.Pt,
					     TempR -> PObj -> U.Pt));
	    }
	    else if (IS_PT_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj =
		    GenNUMValObject(DOT_PROD(TempL -> PObj -> U.Vec,
					     TempR -> PObj -> U.Pt));
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj =
		    GenNUMValObject(DOT_PROD(TempL -> PObj -> U.Vec,
					     TempR -> PObj -> U.Vec));
	    }
	    else if (IS_MAT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
                MatMultTwo4by4(*Root -> PObj -> U.Mat,
			       *TempL -> PObj -> U.Mat,
			       *TempR -> PObj -> U.Mat);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_PT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_POINT, NULL);
		PT_COPY(Root -> PObj -> U.Pt, TempL -> PObj -> U.Pt);
		PT_SCALE(Root -> PObj -> U.Pt, TempR -> PObj -> U.R);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_COPY(Root -> PObj -> U.Vec, TempL -> PObj -> U.Vec);
		PT_SCALE(Root -> PObj -> U.Vec, TempR -> PObj -> U.R);
	    }
	    else if (IS_NUM_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
		MatScale4by4(*Root -> PObj -> U.Mat,
			     *TempL -> PObj -> U.Mat,
			     &TempR -> PObj -> U.R);
	    }
	    else if (IS_POLY_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = GMTransformObject(TempR -> PObj,
					       *TempL -> PObj -> U.Mat);
	    }
	    else if ((IS_MAT_NODE(TempR) && IS_CRV_NODE(TempL)) ||
		     (IS_MAT_NODE(TempR) && IS_SRF_NODE(TempL)) ||
		     (IS_MAT_NODE(TempR) && IS_OLST_NODE(TempL))) {
		Root -> PObj = GMTransformObject(TempL -> PObj,
					       *TempR -> PObj -> U.Mat);
	    }
	    else if (IS_VEC_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		MatMultVecby4by4(Root -> PObj -> U.Vec,
				 TempR -> PObj -> U.Vec,
				 *TempL -> PObj -> U.Mat);
	    }
	    else if (IS_PT_NODE(TempR) && IS_MAT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_POINT, NULL);
		MatMultVecby4by4(Root -> PObj -> U.Pt,
				 TempR -> PObj -> U.Pt,
				 *TempL -> PObj -> U.Mat);
	    }
	    else if ((IS_POLY_NODE(TempR) || IS_SRF_NODE(TempR)) &&
		     (IS_POLY_NODE(TempL) || IS_SRF_NODE(TempL))) {
		TempLObj = ConvSrfToPolys(TempL);
		TempRObj = ConvSrfToPolys(TempR);
		Root -> PObj = BooleanAND(TempLObj, TempRObj);
		if (Root -> PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BOOLEAN_ERR;
		    UpdateCharError("Operator ", MULT);
		    RetVal = NULL;
		}
	    }
	    else
		RetVal = NULL;
	    break;
	case DIV:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {  /* Numeric div. */
		if (TempR -> PObj -> U.R != 0.0) {
		    Root -> PObj = GenNUMValObject(TempL -> PObj -> U.R /
						   TempR -> PObj -> U.R);
		}
		else {
		    *IError = IE_ERR_DIV_BY_ZERO;
		    strcpy(CError, "/");
		    RetVal = NULL;
		}
	    }
	    else if ((IS_POLY_NODE(TempR) || IS_SRF_NODE(TempR)) &&
		     (IS_POLY_NODE(TempL) || IS_SRF_NODE(TempL))) {
		TempLObj = ConvSrfToPolys(TempL);
		TempRObj = ConvSrfToPolys(TempR);
		Root -> PObj = BooleanCUT(TempLObj, TempRObj);
		if (Root -> PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BOOLEAN_ERR;
		    UpdateCharError("Operator ", DIV);
		    RetVal = NULL;
		}
	    }
	    else
		RetVal = NULL;
	    break;
	case POWER:
	    if (IS_NUM_NODE(TempR) && IS_NUM_NODE(TempL)) {/* Numeric power. */
		Root -> PObj = GenNUMValObject(pow(TempL -> PObj -> U.R,
						   TempR -> PObj -> U.R));
	    }
	    else if (IS_NUM_NODE(TempR) && IS_MAT_NODE(TempL)) {
		/* Power MUST be integer in this case. */
		i = (int) TempR -> PObj -> U.R;
		if (!APX_EQ(i, TempR -> PObj -> U.R) || i < -1) {
		    *IError = IE_ERR_MAT_POWER;
		    strcpy(CError, "^");
		    RetVal = NULL;
		}
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
		if (i == -1) {		     /* Generate the inverse matrix: */
		    if (!MatInverseMatrix(*TempL -> PObj -> U.Mat,
					  *Root -> PObj -> U.Mat)) {
			*IError = IE_ERR_MAT_POWER;
			strcpy(CError, "^");
			RetVal = NULL;
		    }
		}
		else {			      /* I must be positive integer. */
		    MatGenUnitMat(*Root -> PObj -> U.Mat);
		    while (i--)
		        MatMultTwo4by4(*Root -> PObj -> U.Mat,
				       *Root -> PObj -> U.Mat,
				       *TempL -> PObj -> U.Mat);
		}
	    }
	    else if (IS_NUM_NODE(TempR) && IS_STR_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_STRING, NULL);
		Str = Root -> PObj -> U.Str;
		if (OperReversed) {
		    sprintf(Str, GlblFloatFormat, TempR -> PObj -> U.R);
		    strcpy(&Str[strlen(Str)], TempL -> PObj -> U.Str);
		}
		else {
		    strcpy(Str, TempL -> PObj -> U.Str);
		    sprintf(&Str[strlen(Str)], GlblFloatFormat,
			    TempR -> PObj -> U.R);
		}
	    }
	    else if (IS_PT_NODE(TempR) && IS_PT_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		GMVecCrossProd(Root -> PObj ->U.Vec,
			       TempR -> PObj -> U.Pt,
			       TempL -> PObj -> U.Pt);
	    }
	    else if (IS_PT_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		GMVecCrossProd(Root -> PObj ->U.Vec,
			       TempR -> PObj -> U.Pt,
			       TempL -> PObj -> U.Vec);
	    }
	    else if (IS_VEC_NODE(TempR) && IS_VEC_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		GMVecCrossProd(Root -> PObj ->U.Vec,
			       TempR -> PObj -> U.Vec,
			       TempL -> PObj -> U.Vec);
	    }
	    else if ((IS_POLY_NODE(TempR) || IS_SRF_NODE(TempR)) &&
		     (IS_POLY_NODE(TempL) || IS_SRF_NODE(TempL))) {
		TempLObj = ConvSrfToPolys(TempL);
		TempRObj = ConvSrfToPolys(TempR);
		Root -> PObj = BooleanMERGE(TempLObj, TempRObj);
		if (Root -> PObj == NULL) {
		    IPGlblEvalError = IE_ERR_BOOLEAN_ERR;
		    UpdateCharError("Operator ", MINUS);
		    RetVal = NULL;
		}
	    }
	    else if (IS_STR_NODE(TempR) && IS_STR_NODE(TempL)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_STRING, NULL);
		Str = Root -> PObj -> U.Str;
		sprintf(Str, "%s%s", TempL -> PObj -> U.Str,
				     TempR -> PObj -> U.Str);
	    }
	    else
		RetVal = NULL;
	    break;
	case UNARMINUS:
	    if (IS_NUM_NODE(TempR)) {
		Root -> PObj = GenNUMValObject(-TempR -> PObj -> U.R);
	    }
	    else if (IS_PT_NODE(TempR)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_POINT, NULL);
		PT_COPY(Root -> PObj -> U.Pt, TempR -> PObj -> U.Pt);
		PT_SCALE(Root -> PObj -> U.Pt, -1.0);
	    }
	    else if (IS_VEC_NODE(TempR)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_VECTOR, NULL);
		PT_COPY(Root -> PObj -> U.Vec, TempR -> PObj -> U.Vec);
		PT_SCALE(Root -> PObj -> U.Vec, -1.0);
	    }
	    else if (IS_PLANE_NODE(TempR)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_PLANE, NULL);
		PLANE_COPY(Root -> PObj -> U.Plane, TempR -> PObj -> U.Plane);
		for (i = 0; i < 4; i++)
		    Root -> PObj -> U.Plane[i] = -Root -> PObj -> U.Plane[i];
	    }
	    else if (IS_MAT_NODE(TempR)) {
		Root -> PObj = IPAllocObject("", IP_OBJ_MATRIX, NULL);
		R = -1.0;
		MatScale4by4(*Root -> PObj -> U.Mat,
			     *TempR -> PObj -> U.Mat,
			     &R);
	    }
	    else if (IS_POLY_NODE(TempR)) {
		Root -> PObj = BooleanNEG(TempR -> PObj);
	    }
	    else if (IS_CRV_NODE(TempR)) {
		Root -> PObj = CurveReverse(TempR -> PObj);
	    }
	    else if (IS_SRF_NODE(TempR)) {
		Root -> PObj = SurfaceReverse(TempR -> PObj);
	    }
	    else
		RetVal = NULL;
	    break;
    }
    if (RetVal == NULL && *IError == IPE_NO_ERR) { /* Put general error msg: */
	*IError = IE_ERR_TYPE_MISMATCH;
	switch (Root -> NodeKind) {
	    case PLUS:
		strcpy(CError, "Operator +");
		break;
	    case MINUS:
		strcpy(CError, "Operator -");
		break;
	    case MULT:
		strcpy(CError, "Operator *");
		break;
	    case DIV:
		strcpy(CError, "Operator /");
		break;
	    case POWER:
		strcpy(CError, "Operator ^");
		break;
	    case UNARMINUS:
		strcpy(CError, "Operator (unary) -");
		break;
	}
    }

    return RetVal;
}

/*****************************************************************************
*   If given object is a surface, its polygonal representation object is     *
* returned instead. Otherwise the given pointer is returned as is.           *
*****************************************************************************/
static IPObjectStruct *ConvSrfToPolys(ParseTree *Srf)
{
    IPObjectStruct *PObjPolys;
    if (!IS_SRF_NODE(Srf))
	return Srf -> PObj;

    if ((PObjPolys = AttrGetObjectObjAttrib(Srf -> PObj, "_polygons"))
								== NULL) {
    	ComputeSurfacePolygons(Srf -> PObj);
    	PObjPolys = AttrGetObjectObjAttrib(Srf -> PObj, "_polygons");
    }

    return PObjPolys;
}
