/***(Calculator.m)*************************************************************
*H* Primary input processing logic for SciCalc                V1.1, 20-JUN-93 *
*C* V1.1 20-JUN-93 Version 1.1 update.	    	    	    	    	--MDM *
*C* V0.0 09-FEB-91 Initial version  	    	    	    	    	--MDM *
******************************************************************************/

#import <strings.h>  	/* Standard C string operator function prototypes */
#import <ctype.h>   	/* Standard C ASCII character code macros */
#import <math.h>	/* Standard C math library function prototypes */

#import <appkit/Application.h>	/* Appkit Application Class Definition */
#import <appkit/Button.h>	/* Appkit Button      Class Definition */
#import <appkit/Matrix.h>	/* Appkit Matrix      Class Definition */
#import <appkit/MenuCell.h>	/* Appkit MenuCell    Class Definition */
#import <appkit/Slider.h>	/* Appkit Slider      Class Definition */

#import "Calculator.h"	    	/* Interface for this object */
#import "LcdText.h" 	    	/* LcdText Class Definition */

#import "nxkeycodes.h"	    	/* NeXT keyboard keycode values */

void STR_BinaryToHex (char*, char*);
void STR_HexToBinary (char*, char*);
#ifdef DEBUG
void UTL_NxDumpEvent (NXEvent*, int, int, int);
#endif

#define ACCEPT_Digit	  0x00000001
#define ACCEPT_Operator	  0x00000002
#define ACCEPT_OpenParen  0x00000004
#define ACCEPT_CloseParen 0x00000008
#define ACCEPT_Function   0x00000010

#define BASE_Decimal	1
#define BASE_Hex    	2
#define BASE_Octal  	3
#define BASE_Binary 	4

#define FMODE_Evaluate	    1
#define FMODE_Operate	    2

#define MAXDGT_Decimal	    22
#define MAXDGT_HexLong	    8
#define MAXDGT_HexWord	    4
#define MAXDGT_HexByte	    2
#define MAXDGT_OctalLong    11
#define MAXDGT_OctalWord    6
#define MAXDGT_OctalByte    3
#define MAXDGT_BinaryLong   32
#define MAXDGT_BinaryWord   16
#define MAXDGT_BinaryByte   8

#define MCELL_minimum	0
#define MCELL_maximum	25

#define METRIC_Degrees	1
#define METRIC_Radians	2
#define METRIC_Grads	3

#define UNIT_Byte	1
#define UNIT_Word	2
#define UNIT_Longword	3
#define UNIT_Quadword	4

static Calculator *CVmyOnlyChild;   /* ID of Classes one object instance */


@implementation Calculator:Object


/******************************************************************************
***                   F A C T O R Y   M E T H O D S 	    	    	    ***
******************************************************************************/

/******************************************************************************
* METHOD:- init  	    	    	    	    	    	    	      *
*   Create and initialize a new instance of the Calculator class.    	      *
******************************************************************************/
- init

{   /* Local Variables */
    int ndex;

/* BEGIN:-init */	
self = [super init];	    /* create new instance of calculator */

/* Set the Class Variable to the ID of its only object */
CVmyOnlyChild = self;

/* Create Value & Operator stacks */
OVvalStack = [[ValueStack alloc] init];
OVopStack  = [[OperatorStack alloc] init];

/* Initialize Display & Value strings to be null terminated */
OVcalcDspStr[0] = OVcalcDspStr[0] = 0;
OVcalcMemDspStr[0] = 0;
for (ndex = MCELL_minimum; ndex <= MCELL_maximum; ndex++)
    OVcalcMemoryVal[ndex] = 0.0;

/* Initialize state variables */
OVcalcAcceptMode   = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
OVcalcBase         = BASE_Decimal;
OVcalcMaxDigits    = MAXDGT_Decimal;
OVcalcCEIndex	   = 0;
OVcalcEqualized    = NO;
OVcalcFrozen       = NO;
OVcalcFunctionMode = FMODE_Evaluate;
OVcalcHyperbolic   = NO;
OVcalcInverse      = NO;
OVcalcMetric	   = METRIC_Degrees;
OVcalcMemoryCell   = MCELL_minimum;
OVcalcParenBalance = 0;
OVcalcPrecsnVal    = 6;

return(self);
}/* END:-init */



/******************************************************************************
* METHOD:+ KeyBoardEquivalent 	    	    	    	    	    	      *
*   Receives messages from the Main  Window  object for each keyDown event it *
* receives.  This routine determines  which  key was pressed and decides if a *
* message should be sent to the Calculator object itself.   	    	      *
******************************************************************************/
+ KeyBoardEquivalent:(NXEvent *)event;

{   /* Local Variables */
    char  chrval;

/* BEGIN KeyBoardEquivalent */
#if 0
UTL_NxDumpEvent (event, 1, 1, 1);
#endif

if ( index("01234567890ABCDEFabcdef.",event->data.key.charCode) )
    {/* This keydown event corresponds to a digit button */
    chrval = event->data.key.charCode;
    if (islower(chrval))
    	chrval = toupper(chrval);

    [CVmyOnlyChild ProcessDigit:chrval];
    }

else if ( event->data.key.charCode == '+' )
    [CVmyOnlyChild ProcessOperator: OPCODE_Plus: "+"];

else if ( event->data.key.charCode == '-' )
    [CVmyOnlyChild ProcessOperator: OPCODE_Minus: "-"];

else if ( event->data.key.charCode == '/' )
    [CVmyOnlyChild ProcessOperator: OPCODE_Divide: "/"];

else if ( event->data.key.charCode == '*' )
    [CVmyOnlyChild ProcessOperator: OPCODE_Multiply: "*"];

else if ( event->data.key.charCode == '=' ||
    	  event->data.key.charCode == 13 ||
    	  ((event->flags & NX_NUMERICPADMASK) && event->data.key.charCode == 3) )
    [CVmyOnlyChild Equals:CVmyOnlyChild];

else if ( event->data.key.charCode == '~' ||
    	  event->data.key.charCode == '`' )
    [CVmyOnlyChild ChangeSign:CVmyOnlyChild];

else if ( event->data.key.charCode == '(' )
    [CVmyOnlyChild OpenParen:CVmyOnlyChild];

else if ( event->data.key.charCode == ')' )
    [CVmyOnlyChild CloseParen:CVmyOnlyChild];

else if ( event->data.key.charCode == 'P' || event->data.key.charCode == 'p' )
    [CVmyOnlyChild EnterPi:CVmyOnlyChild];

else if ( event->data.key.charCode == '[' )
    [CVmyOnlyChild MemoryStore:CVmyOnlyChild];

else if ( event->data.key.charCode == ']' )
    [CVmyOnlyChild MemoryRecall:CVmyOnlyChild];

else if ( event->data.key.charCode == '{' )
    [[CVmyOnlyChild setTag:-1] MemorySelect:CVmyOnlyChild];

else if ( event->data.key.charCode == '}' )
    [[CVmyOnlyChild setTag:1] MemorySelect:CVmyOnlyChild];

else if ( event->data.key.charCode == '\\' )
    [CVmyOnlyChild MemoryClear:CVmyOnlyChild];

else if ( event->data.key.charCode == '\033' )	    	/* ESC */
    [CVmyOnlyChild AllClear:CVmyOnlyChild];

else if ( event->data.key.charCode == '\177' )	    	/* Delete */
    [CVmyOnlyChild ClearEntry:CVmyOnlyChild];

else if ( event->data.key.charCode == '\005' ) 	    	/* Control-E */
    [CVmyOnlyChild ProcessFunction:OPCODE_Exp :"Exp:"];

else if ( (event->flags & NX_ALTERNATEMASK) &&	    	/* Alternate-E */
    	  (event->data.key.charCode == NX_CHRCODE_ALT_e ||
    	   event->data.key.charCode == NX_CHRCODE_ALT_E) )
    [CVmyOnlyChild ProcessFunction:OPCODE_Ln :"Ln:"];

else if ( (event->flags & NX_ALTERNATEMASK) &&	    	/* Alternate-F */
          (event->data.key.charCode == NX_CHRCODE_ALT_f ||
           event->data.key.charCode == NX_CHRCODE_ALT_F) )
    [CVmyOnlyChild ProcessFunction:OPCODE_Factorial :"X!:"];

else if ( event->data.key.charCode == '\014' )   	/* Control-L */
    [CVmyOnlyChild ProcessFunction:OPCODE_TenToX :"10^X:"];

else if ( (event->flags & NX_ALTERNATEMASK) &&	    	/* Alternate-L */
          (event->data.key.charCode == NX_CHRCODE_ALT_l ||
           event->data.key.charCode == NX_CHRCODE_ALT_L) )
    [CVmyOnlyChild ProcessFunction:OPCODE_Log :"LOG:"];

else if ( event->data.key.charCode == '\022' )          /* Control-R */
    [CVmyOnlyChild ProcessFunction:OPCODE_Recip :"1/X:"];

else if ( event->data.key.charCode == '\030' )          /* Control-X */
    [CVmyOnlyChild ProcessFunction:OPCODE_XSquared :"X^2:"];

else if ( (event->flags & NX_ALTERNATEMASK) &&	    	/* Alternate-X */
          (event->data.key.charCode == NX_CHRCODE_ALT_x ||
           event->data.key.charCode == NX_CHRCODE_ALT_X) )
    [CVmyOnlyChild ProcessFunction:OPCODE_SqrRoot :"SQRT:"];

else if ( event->data.key.charCode == '\031' )          /* Control-Y */
    [CVmyOnlyChild ProcessOperator:OPCODE_XToY :"X^Y"];

else if ( (event->flags & NX_ALTERNATEMASK) &&	    	/* Alternate-Y */
          (event->data.key.charCode == NX_CHRCODE_ALT_y ||
           event->data.key.charCode == NX_CHRCODE_ALT_Y) )
    [CVmyOnlyChild ProcessOperator:OPCODE_XrootY :"XrootY"];

else if ( (event->flags & NX_CONTROLMASK) && !(event->flags & NX_SHIFTMASK) )
    {/* Control down without shift key ::= Inverse:Off, Hyperbolic:Off */
    if ( event->data.key.charCode == '\003' )          	    	/* Ctl-c */
    	[CVmyOnlyChild ProcessFunction:OPCODE_Cos :"COS:"];
    else if ( event->data.key.charCode == '\023' )          	/* Ctl-s */
    	[CVmyOnlyChild ProcessFunction:OPCODE_Sin :"SIN:"];
    else if ( event->data.key.charCode == '\024' )              /* Ctl-t */
    	[CVmyOnlyChild ProcessFunction:OPCODE_Tan :"TAN:"];
    }

else if ( (event->flags & NX_CONTROLMASK) && (event->flags & NX_SHIFTMASK) )
    {/* Control down with shift key ::= Inverse:Off, Hyperbolic:On */
    if ( event->data.key.charCode == '\003' )          	    	/* Ctl-C */
    	[CVmyOnlyChild ProcessFunction:OPCODE_CosH :"COSH:"];
    else if ( event->data.key.charCode == '\023' )     	    	/* Ctl-S */
    	[CVmyOnlyChild ProcessFunction:OPCODE_SinH :"SINH:"];
    else if ( event->data.key.charCode == '\024' )     	    	/* Ctl-T */
    	[CVmyOnlyChild ProcessFunction:OPCODE_TanH :"TANH:"];
    }

else if ( (event->flags & NX_ALTERNATEMASK) && !(event->flags & NX_SHIFTMASK) )
    {/* Alternate down without shift key ::= Inverse:Off, Hyperbolic:On */
    if ( event->data.key.charCode == NX_CHRCODE_ALT_c )		/* Alt-c */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ACos :"ACOS:"];
    else if ( event->data.key.charCode == NX_CHRCODE_ALT_s )	/* Alt-s */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ASin :"ASIN:"];
    else if ( event->data.key.charCode == NX_CHRCODE_ALT_t )	/* Alt-t */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ATan :"ATAN:"];
    }

else if ( (event->flags & NX_ALTERNATEMASK) && (event->flags & NX_SHIFTMASK) )
    {/* Alternate down with shift key ::= Inverse:On, Hyperbolic:On */
    if ( event->data.key.charCode == NX_CHRCODE_ALT_C )		/* Alt-C */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ACosH :"ACOSH:"];
    else if ( event->data.key.charCode == NX_CHRCODE_ALT_S )	/* Alt-S */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ASinH :"ASINH:"];
    else if ( event->data.key.charCode == NX_CHRCODE_ALT_T )	/* Alt-T */
    	[CVmyOnlyChild ProcessFunction:OPCODE_ATanH :"ATANH:"];
    }
else
    return(NULL);

return(CVmyOnlyChild);
}/* END KeyBoardEquivalent */



/******************************************************************************
* METHOD:+ PasteExpression:Length:    	    	    	    	    	      *
*   This method accepts a string and attempts to process each character as if *
* were entered from the keyboard.   	    	    	    	    	      *
******************************************************************************/
+ PasteExpression

    /* Arguments */
    	  :(char*)  expString
    Length:(int)    expLength

{   /* Local Variables */
    char  chrval;
    int	  indx;

/* BEGIN+PasteExpression:Length: */
for ( indx = 0; indx < expLength; indx++ )
    {
    chrval = toupper(expString[indx]);
    if ( strchr("01234567890ABCDEF.", chrval) )
    	[CVmyOnlyChild ProcessDigit:chrval];

    else if ( chrval == '+' )
    	[CVmyOnlyChild ProcessOperator: OPCODE_Plus: "+"];

    else if ( chrval == '-' )
    	[CVmyOnlyChild ProcessOperator: OPCODE_Minus: "-"];

    else if ( chrval == '/' )
        [CVmyOnlyChild ProcessOperator: OPCODE_Divide: "/"];

    else if ( chrval == '*' )
    	[CVmyOnlyChild ProcessOperator: OPCODE_Multiply: "*"];

    else if ( chrval == '=' )
        [CVmyOnlyChild Equals:CVmyOnlyChild];

    else if ( chrval == '~' )
    	[CVmyOnlyChild ChangeSign:CVmyOnlyChild];

    else if ( chrval == '(' )
    	[CVmyOnlyChild OpenParen:CVmyOnlyChild];

    else if ( chrval == ')' )
        [CVmyOnlyChild CloseParen:CVmyOnlyChild];

    else if ( chrval == 'P' )
    	[CVmyOnlyChild EnterPi:CVmyOnlyChild];

    else if ( chrval == '[' )
        [CVmyOnlyChild MemoryStore:CVmyOnlyChild];

    else if ( chrval == ']' )
        [CVmyOnlyChild MemoryRecall:CVmyOnlyChild];

    else if ( chrval == '{' )
    	[[CVmyOnlyChild setTag:-1] MemorySelect:CVmyOnlyChild];

    else if ( chrval == '}' )
        [[CVmyOnlyChild setTag:1] MemorySelect:CVmyOnlyChild];

    else if ( chrval == '\\' )
        [CVmyOnlyChild MemoryClear:CVmyOnlyChild];

    else if ( chrval == '\033' )
        [CVmyOnlyChild AllClear:CVmyOnlyChild];

    else if ( chrval == '\177' )
        [CVmyOnlyChild ClearEntry:CVmyOnlyChild];
    }/*for*/

return(CVmyOnlyChild);
}/* END+PasteExpression:Length: */



/******************************************************************************
***                   I N S T A N C E   M E T H O D S 	    	    	    ***
******************************************************************************/

/******************************************************************************
* INSTANCE METHOD:- AdjustPrecision    	    	    	    	    	      *
*   This method adjusts the  precision  display  text  and  the slider limits *
* according to the current Base and Unit settings.  	    	    	      *
******************************************************************************/
- AdjustPrecision

{   /* Local Variables */
    char fmtstr[16];	/* Space to format precision label text */
    int  maxprc=0;	/* maximum precision for current Base/Unit choice */

/* BEGIN AdjustPrecision */
switch (OVcalcBase)
    {
    case BASE_Decimal:
    	maxprc = MAXDGT_Decimal - 2;
    	break;

    case BASE_Hex:
    	if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_HexLong;
    	if (OVcalcUnit == UNIT_Word)     maxprc = MAXDGT_HexWord;
    	if (OVcalcUnit == UNIT_Byte)     maxprc = MAXDGT_HexByte;
    	break;
    	
    case BASE_Octal:
    	if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_OctalLong;
    	if (OVcalcUnit == UNIT_Word)     maxprc = MAXDGT_OctalWord;
    	if (OVcalcUnit == UNIT_Byte)     maxprc = MAXDGT_OctalByte;
    	break;

    case BASE_Binary:
    	if (OVcalcUnit == UNIT_Longword) maxprc = MAXDGT_BinaryLong;
    	if (OVcalcUnit == UNIT_Word)     maxprc = MAXDGT_BinaryWord;
    	if (OVcalcUnit == UNIT_Byte)     maxprc = MAXDGT_BinaryByte;
    	break;
    }/*switch*/

if ( maxprc < OVcalcPrecsnVal )
    {/* Reset precision to fit within limits, modify label and slider */
    OVcalcPrecsnVal = maxprc;
    sprintf(fmtstr, "Precision: %2d", OVcalcPrecsnVal);
    [OVcalcPrecsnDsp setStringValue:fmtstr];
    [OVcalcPrecsnSldr setIntValue:OVcalcPrecsnVal];
    }

[OVcalcPrecsnSldr setMaxValue:(double)maxprc];
return self;
}/* END AdjustPrecision */



/******************************************************************************
* INSTANCE METHOD:- AllClear   	    	    	    	    	    	      *
*   This method handles the  actions  required when the user presses the "AC" *
* button.   	    	    	    	    	    	    	    	      *
******************************************************************************/
- AllClear:sender

{/* BEGIN AllClear */

/* Empty Operator and Value Stacks */
[OVopStack ClearStack];
[OVvalStack ClearStack];

/* Clear Display and Value Strings */
OVcalcDspStr[0] = OVcalcValStr[0] = 0;
OVcalcCEIndex	= 0;

/* Reset state variables to normalized values */
OVcalcAcceptMode   = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
OVcalcFrozen       = NO;
OVcalcEqualized    = NO;
OVcalcParenBalance = 0;

/* Set current display value to "0.0" */
[OVcalcDisplay setCurrentLine:"0.0"];

return self;
}/* END AllClear */



/******************************************************************************
* INSTANCE METHOD:- BitwiseAnd 	    	    	    	    	    	      *
*   Process the bitwise And operator button event.     	    	    	      *
******************************************************************************/
- BitwiseAnd:sender

{/* BEGIN BitwiseAnd */
[self ProcessOperator: OPCODE_And: "&"];
return self;
}/* END BitwiseAnd */



/******************************************************************************
* INSTANCE METHOD:- BitwiseLsh 	    	    	    	    	    	      *
*   Process the bitwise arithmetic Left-Shift function button event    	      *
******************************************************************************/
- BitwiseLsh:sender

{/* BEGIN BitwiseLsh */
[self ProcessFunction: OPCODE_LShift: "<<:"];
return self;
}/* END BitwiseLsh */



/******************************************************************************
* INSTANCE METHOD:- BitwiseNot 	    	    	    	    	    	      *
*   Process the bitwise Not function button event.     	    	    	      *
******************************************************************************/
- BitwiseNot:sender

{/* BEGIN BitwiseNot */
[self ProcessFunction: OPCODE_Not: "Not:"];
return self;
}/* END BitwiseNot */



/******************************************************************************
* INSTANCE METHOD:- BitwiseOr  	    	    	    	    	    	      *
*   Process the bitwise Or operator button event.     	    	    	      *
******************************************************************************/
- BitwiseOr:sender

{/* BEGIN BitwiseOr */
[self ProcessOperator: OPCODE_Or: "|"];
return self;
}/* END BitwiseOr */



/******************************************************************************
* INSTANCE METHOD:- BitwiseRsh 	    	    	    	    	    	      *
*   Process the bitwise arithmetic Right-Shift function button event   	      *
******************************************************************************/
- BitwiseRsh:sender

{/* BEGIN BitwiseRsh */
[self ProcessFunction: OPCODE_RShift: ">>:"];
return self;
}/* END BitwiseRsh */



/******************************************************************************
* INSTANCE METHOD:- BitwiseXor 	    	    	    	    	    	      *
*   Process the bitwise exclusive or operator button event.    	    	      *
******************************************************************************/
- BitwiseXor:sender

{/* BEGIN BitwiseXor */
[self ProcessOperator: OPCODE_Xor: "^"];
return self;
}/* END BitwiseXor */



/******************************************************************************
* INSTANCE METHOD:- ChangeBase	    	    	    	    	    	      *
*   Handle the actions needed  when  one  of  numeric  base radio buttons are *
* selected.  This function is  responsible  enabling  and disabling the digit *
* buttons appropriate for the  current  numeric base.  It is also responsible *
* for managing the UNIT  radio  box  according  to base.  In Decimal mode the *
* only Unit allowed is Quadword.  If the numeric base is Hex/Octal/Binary the *
* Unit may be Longword/Word/Byte.   When  transitioning  from Decimal to Hex/ *
* Octal/Binary the Longword/Word/Byte buttons are enabled and longword is set *
* as the default Unit size.   This routine also performs the task of changing *
* the currently entered value from the current Base/Unit to the new Base/Unit *
* selection.	    	    	    	    	   	    	    	      *
******************************************************************************/
- ChangeBase:sender

{   /* Local Variables */
    int	   col;
    double curval;
    int    oldbase;
    int	   row;
    int	   translate=0;	   /* Translate current value to new base? */

/* BEGIN ChangeBase */
if ( [self ValueEntered] )
    {/* A value has been entered, scan it under the current Base setting */
    translate = 1;
    [self ScanValue:OVcalcValStr :&curval];
    }
    
/* Save the current Base and extract the new base from the button tag */
oldbase    = OVcalcBase;
OVcalcBase = [sender selectedTag]; /* Get tag value from sending button */

/* Convert Unit radio box according to Base transition */
if ( oldbase != BASE_Decimal && OVcalcBase == BASE_Decimal )
    {/* Modify the unit radio box, allow only Quadword unit selection */
    [[OVcalcUnitMatrix cellAt:0 :0] setEnabled:NO];  /* Disable byte */
    [[OVcalcUnitMatrix cellAt:1 :0] setEnabled:NO];  /* Disable word */
    [[OVcalcUnitMatrix cellAt:0 :1] setEnabled:NO];  /* Disable longword */
    [[OVcalcUnitMatrix cellAt:1 :1] setEnabled:YES]; /* Enable quadword */
    [OVcalcUnitMatrix selectCellAt:1 :1];     	     /* Select quadword */
    
    /* Modify bitwise function button matrix: Disable [And], [Or], [Not] */
    /* [Xor], [Lsh], [Rsh]  	    	    	    	    	    	 */
    for (row = 0; row < 3; row++)
    	for (col = 0; col < 2; col++)
    	    [[OVcalcBitwiseMtx cellAt:row :col] setEnabled:NO];
    OVcalcUnit = UNIT_Quadword;
    }

else if ( oldbase == BASE_Decimal && OVcalcBase != BASE_Decimal )
    {/* Modify unit radio box, allow byte/word/longword select longword */
    [[OVcalcUnitMatrix cellAt:0 :0] setEnabled:YES];  /* Enable byte */
    [[OVcalcUnitMatrix cellAt:1 :0] setEnabled:YES];  /* Enable word */
    [[OVcalcUnitMatrix cellAt:0 :1] setEnabled:YES];  /* Enable longword */
    [[OVcalcUnitMatrix cellAt:1 :1] setEnabled:NO];   /* Disable quadword */
    [OVcalcUnitMatrix selectCellAt:0 :1];   	      /* Select Longword */

    /* Modify bitwise function button matrix: Enable [And], [Or], [Not] */
    /* [Xor], [Lsh], [Rsh]  	    	    	       	    	    	*/
    for (row = 0; row < 3; row++)
    	for (col = 0; col < 2; col++)
    	    [[OVcalcBitwiseMtx cellAt:row :col] setEnabled:YES];
    OVcalcUnit = UNIT_Longword;
    }


switch ( OVcalcBase )
    {
    case BASE_Decimal:
    	/* Disable buttons [A]-[F] */
    	for (row = 0; row < 2; row++)
    	    for (col = 0; col < 3; col++)
   	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
    	/* Enable buttons [0]-[9] & [.] */
    	for (row = 2; row < 5; row++)
    	    for (col = 0; col < 3; col++)
    	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];
        [OVcalcDigitPBtn setEnabled:YES];

    	OVcalcMaxDigits = MAXDGT_Decimal;
    	break;
    	    	
    case BASE_Hex:
    	/* Enable buttons [0]-[F] and disable [.] button */
    	for (row = 0; row < 5; row++)
    	    for (col = 0; col < 3; col++)
    	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];
        [OVcalcDigitPBtn setEnabled:NO];

    	if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_HexLong;
    	if (OVcalcUnit == UNIT_Word)     OVcalcMaxDigits = MAXDGT_HexWord;
    	if (OVcalcUnit == UNIT_Byte)     OVcalcMaxDigits = MAXDGT_HexByte;
    	break;
    	    	
    case BASE_Octal:
    	/* Disable buttons [8]-[F] & [.] */
    	for (row = 0; row < 2; row++)
    	    for (col = 0; col < 3; col++)
    	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
    	[[OVcalcDigitMtx cellAt:2 :1] setEnabled:NO];
    	[[OVcalcDigitMtx cellAt:2 :2] setEnabled:NO];
        [OVcalcDigitPBtn setEnabled:NO];
    	/* Enable buttons [0]-[7] ] */
    	[[OVcalcDigitMtx cellAt:2 :0] setEnabled:YES];
    	for (row = 3; row < 5; row++)
    	    for (col = 0; col < 3; col++)
    	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:YES];

    	if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_OctalLong;
    	if (OVcalcUnit == UNIT_Word)     OVcalcMaxDigits = MAXDGT_OctalWord;
    	if (OVcalcUnit == UNIT_Byte)     OVcalcMaxDigits = MAXDGT_OctalByte;
    	break;
    	    	
    case BASE_Binary:
    	/* Disable buttons [2]-[F] & [.] */
   	for (row = 0; row < 4; row++)
    	    for (col = 0; col < 3; col++)
    	    	[[OVcalcDigitMtx cellAt:row :col] setEnabled:NO];
    	[[OVcalcDigitMtx cellAt:4 :1] setEnabled:NO];
    	[[OVcalcDigitMtx cellAt:4 :2] setEnabled:NO];
        [OVcalcDigitPBtn setEnabled:NO];

    	/* Enable buttons [0]-[1] */
    	[[OVcalcDigitMtx cellAt:4 :0] setEnabled:YES];

    	if (OVcalcUnit == UNIT_Longword) OVcalcMaxDigits = MAXDGT_BinaryLong;
    	if (OVcalcUnit == UNIT_Word)     OVcalcMaxDigits = MAXDGT_BinaryWord;
    	if (OVcalcUnit == UNIT_Byte)     OVcalcMaxDigits = MAXDGT_BinaryByte;
    	break;
    }/*switch*/

/* Adjust precision slider for selected base */
[self AdjustPrecision];

if ( translate )
    {/* Clear current entry strings, reformat and redisplay */
    OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
    [self FormatValue:curval :OVcalcValStr];
    strcat (OVcalcDspStr, OVcalcValStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];
    }

/* Adjust memory display if need be */
if ( OVcalcMemDspStr[0] != 0 )
    {/* Memory is not empty, so reformat cell to new numeric base */
    [self FormatValue:OVcalcMemoryVal[OVcalcMemoryCell]  :OVcalcMemDspStr];
    [OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
    }

return self;
}/* END ChangeBase */



/******************************************************************************
* INSTANCE METHOD:- ChangeFunctionMode	     	    	    	    	      *
*   Handle the actions needed when Function Mode modifier is toggled.  	      *
******************************************************************************/
- ChangeFunctionMode:sender

{/* BEGIN ChangeFunctionMode */
if ( OVcalcFunctionMode == FMODE_Evaluate )
    {/* Change current function mode to Operate, replace menu button text */
    OVcalcFunctionMode = FMODE_Operate;
    [[sender selectedCell] setTitle:"Operate"];
    }

else if ( OVcalcFunctionMode == FMODE_Operate )
    {/* Change current function mode to Evaluate, replace menu button text */
    OVcalcFunctionMode = FMODE_Evaluate;
    [[sender selectedCell] setTitle:"Evaluate"];
    }

return(self);
}/* END ChangeFunctionMode */



/******************************************************************************
* METHOD:- ChangeHyperbolic	   	    	    	    	    	      *
*   Handle the actions needed when Hyperbolic modifier is toggled.   	      *
******************************************************************************/
- ChangeHyperbolic:sender

{/* BEGIN ChangeHyperbolic */
if ( [sender intValue] == 0 )
    /* Toggle button is in disabled state, turn off Hyperbolic modifier */
    OVcalcHyperbolic = NO;
else
    /* Toggle button is in enabled state, turn on Hyperbolic modifier */
    OVcalcHyperbolic = YES;

return(self);
}/* END ChangeHyperbolic */



/******************************************************************************
* METHOD:- ChangeInverse	   	    	    	    	    	      *
*   Handle the actions needed when Inverse modifier is toggled.   	      *
******************************************************************************/
- ChangeInverse:sender

{/* BEGIN ChangeInverse */
if ( [sender intValue] == 0 )
    /* Toggle button is in disabled state, turn off Inverse modifier */
    OVcalcInverse = NO;
else
    /* Toggle button is in enabled state, turn on Inverse modifier */
    OVcalcInverse = YES;
return(self);
}/* END ChangeInverse */



/******************************************************************************
* METHOD:- ChangeMetric	   	    	    	    	    	    	      *
*   Handle the actions needed when one of Metric radio buttons are selected.  *
******************************************************************************/
- ChangeMetric:sender

{/* BEGIN ChangeMetric */
OVcalcMetric = [sender selectedTag]; /* Get tag value from sending button */
return(self);
}/* END ChangeMetric */



/******************************************************************************
* METHOD:- ChangePrecison  	    	    	    	    	    	      *
*   This method  handles  messages  from  the output precision slider.  It is *
* responsible for updating  precision  display and current precision instance *
* variable. 	    	    	    	    	    	    	    	      *
******************************************************************************/
- ChangePrecision:sender;

{   /* Local Variables */
    char fmtstr[16];

/* BEGIN ChangePrecision */
OVcalcPrecsnVal = [sender intValue];
sprintf(fmtstr, "Precision: %2d", OVcalcPrecsnVal);
[OVcalcPrecsnDsp setStringValue:fmtstr];
return(self);
}/* END ChangePrecision */



/******************************************************************************
* METHOD:- ChangeSign	   	    	    	    	    	    	      *
*   Handle the actions needed  when the [+-] button is pressed.  If the first *
* character in the current value string is '-' the minus sign is stripped off *
* the value and entry portion of the  display string.  Otherwise a minus sign *
* is added to the value and entry portion of the display string.  The display *
* string is then redrawn.   	    	    	    	    	    	      *
******************************************************************************/
- ChangeSign:sender

{/* BEGIN ChangeSign */
if ( OVcalcValStr[0] == '-' )
    {/* Change negative value to positive value, copy over - sign */
    strcpy(OVcalcValStr,&OVcalcValStr[1]);
    strcpy(&OVcalcDspStr[OVcalcCEIndex], &OVcalcDspStr[OVcalcCEIndex+1]);
    }

else
    {/* Change positive value to negative, add - then append current value */
    strcpy(&OVcalcDspStr[OVcalcCEIndex],"-");
    strcpy(&OVcalcDspStr[OVcalcCEIndex+1],OVcalcValStr);
    strcpy(OVcalcValStr,&OVcalcDspStr[OVcalcCEIndex]);
    }

/* Redraw the current display string */
[OVcalcDisplay setCurrentLine:OVcalcDspStr];
return(self);
}/* END ChangeSign */



/******************************************************************************
* METHOD:- ChangeUnit	   	    	    	    	    	    	      *
*   Handle the actions needed when one of Unit radio buttons are selected.    *
******************************************************************************/
- ChangeUnit:sender

{/* BEGIN ChangeUnit */
OVcalcUnit = [sender selectedTag]; /* Get tag value from sending button */
if (OVcalcUnit == UNIT_Quadword)
    OVcalcMaxDigits = MAXDGT_Decimal;

else if (OVcalcUnit == UNIT_Longword)
    {
    if (OVcalcBase == BASE_Hex)    OVcalcMaxDigits = MAXDGT_HexLong;
    if (OVcalcBase == BASE_Octal)  OVcalcMaxDigits = MAXDGT_OctalLong;
    if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryLong;
    }

else if (OVcalcUnit == UNIT_Word)
    {
    if (OVcalcBase == BASE_Hex)    OVcalcMaxDigits = MAXDGT_HexWord;
    if (OVcalcBase == BASE_Octal)  OVcalcMaxDigits = MAXDGT_OctalWord;
    if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryWord;
    }

else if (OVcalcUnit == UNIT_Byte)
    {
    if (OVcalcBase == BASE_Hex)    OVcalcMaxDigits = MAXDGT_HexByte;
    if (OVcalcBase == BASE_Octal)  OVcalcMaxDigits = MAXDGT_OctalByte;
    if (OVcalcBase == BASE_Binary) OVcalcMaxDigits = MAXDGT_BinaryByte;
    }

/* Adjust precision slider for selected base */
[self AdjustPrecision];
return(self);
}/* END ChangeUnit */



/******************************************************************************
* METHOD:- ClearEntry	   	    	    	    	    	    	      *
*   Handle the actions needed  when the "CE" button is pressed.  If a partial *
* value has been entered then clear the Value string and delete the value off *
* the end of the display string and redisplay.	    	    	    	      *
******************************************************************************/
- ClearEntry:sender;

{/* BEGIN ClearEntry */
if ( [self ValueEntered] )
    {/* A partial value has been entered, erase it and redisplay */
    OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];
    }
return(self);
}/* END ClearEntry */



/******************************************************************************
* METHOD:- CloseParen	   	    	    	    	    	    	      *
*   Handle the actions needed when the ")" button is pressed.                 *
******************************************************************************/
- CloseParen:sender

{   /* Local Variables */
    double curval;
    int	   opcode;

/* BEGIN CloseParen */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_CloseParen) != 0 )
    {/* We are expecting a close paren, so attempt to reduce to open paren */

    /* With a valid Close Paren set new acceptance state */
    if (--OVcalcParenBalance > 0)
    	/* Open parens not balanced, accept Operator or CloseParen state */
    	OVcalcAcceptMode = ACCEPT_Operator | ACCEPT_CloseParen;
    else 
    	/* Open Parens are balanced, accept Operator only */
    	OVcalcAcceptMode = ACCEPT_Operator;

    /* Add operator symbol to display string and redisplay */
    strcat (OVcalcDspStr, ")");
    OVcalcCEIndex = strlen(OVcalcDspStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    if ( [self ValueEntered] )
    	{/* Close paren just after an operand, push onto value stack */
    	[self ScanValue:OVcalcValStr :&curval];
    	[OVvalStack Push:curval];
    	OVcalcValStr[0] = 0;
    	}

    /* Reduce expression between parens and remove open paren operator */
    [self ReduceExpression:OPCODE_CloseParen];
    [OVopStack Pop:&opcode];
#ifdef DEBUG
    if ( opcode != OPCODE_OpenParen )
    	{
    	printf("Paren Match Failure");
    	[OVopStack PrintStack];
    	[OVvalStack PrintStack];
    	}
#endif
    }

else
    /* Enter error state and display syntax error */
    [self SyntaxError:OPCODE_CloseParen :")"];

return self;
}/* END CloseParen */



/******************************************************************************
* METHOD:- Cosine		      	    	    	    	    	      *
*   Perform actions in  response  to  the COS button being pressed.  Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- Cosine:sender;

{/* BEGIN Cosine */
if ( OVcalcInverse )
    {/* The Inverse modifier is on, Opcode will be an ACOSx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be ACOSH */
    	[self ProcessFunction:OPCODE_ACosH :"ACOSH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be ACOS */
    	[self ProcessFunction:OPCODE_ACos :"ACOS:"];
    }    	

else
    {/* The Inverse modifier is off, Opcode will be an COSx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be COSH */
    	[self ProcessFunction:OPCODE_CosH :"COSH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be COS */
    	[self ProcessFunction:OPCODE_Cos :"COS:"];
    }
return(self);
}/* END Cosine */



/******************************************************************************
* METHOD:- Digit	    	    	    	    	    	    	      *
*   This method handles the button  press  events for all the numeric buttons *
* plus the period  button.   The  button  tag  value  is used to identify the *
* corresponding character for  the  button.   The ProcessDigit method is then *
* called with the corresponding character to parse the input.	    	      *
******************************************************************************/
- Digit:sender

{   /* Local Variables */
    static char	tag2chr[] = "0123456789ABCDEF.";
    int	        tagval;

/* BEGIN Digit */
/* Get the tag value from the sending button object */
tagval = [sender selectedTag];

/* Process the corresponding character value for the button pressed */
[self ProcessDigit:tag2chr[tagval]];
return self;
}/* END Digit */



/******************************************************************************
* METHOD:- Divide    	    	    	    	    	    	    	      *
*   Process the Divide operator button event. 	    	    	    	      *
******************************************************************************/
- Divide:sender

{/* BEGIN Divide */
[self ProcessOperator: OPCODE_Divide: "/"];
return self;
}/* END Divide */



/******************************************************************************
* METHOD:- EnterPi  	    	    	    	    	    	    	      *
*   This method handles the actions required when the user presses the [PI]   *
* button.  The Pi opcode is sent to the Function Processor routine. 	      *
******************************************************************************/
- EnterPi:sender

{/* BEGIN EnterPi */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 && OVcalcBase == BASE_Decimal)
    {/* We are expecting a digit to be entered and base is decimal */

    /* Replace current entry and value string with Pi approximation */
    strcpy(OVcalcValStr, "3.14159265358979323846");
    strcpy(&OVcalcDspStr[OVcalcCEIndex], OVcalcValStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    if ( OVcalcParenBalance > 0 )
    	OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
    else
    	OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
    }/*if*/

return self;
}/* END EnterPi */



/******************************************************************************
* METHOD:- Equals  	    	    	    	    	    	    	      *
*   This method handles the  actions  required  when the user presses the "=" *
* button.   	    	    	    	    	    	    	    	      *
******************************************************************************/
- Equals:sender

{   /* Local Variables */
    double curval;

/* BEGIN Equals */
if ( (OVcalcAcceptMode & ACCEPT_Operator) != 0 &&
     (OVcalcAcceptMode & ACCEPT_CloseParen) == 0 )
    {/* We are expecting an operator and parens are balanced, so the */
     /* = operator is valid at this time.                            */

    /* With a valid equal operator set new acceptance state */
    OVcalcAcceptMode = ACCEPT_Digit | ACCEPT_Operator | ACCEPT_Function |
    	    	       ACCEPT_OpenParen;

    if ( [self ValueEntered] )
    	{/* We have an Equals operator after an unprocessed value */
    	 /* Translate value string to binary and push onto value stack */
    	[self ScanValue:OVcalcValStr :&curval];
    	[OVvalStack Push:curval];

    	/* Clear value string for next entered value */
    	OVcalcValStr[0] = 0;
    	}

    /* "Equals" has lowest precedence, reduce expresion */
    [self ReduceExpression:OPCODE_Equals];

    /* Top of value stack now has results of expression, display it and */
    /* preload value string with result, and set state to Equalized 	*/
    [OVvalStack Pop:&curval];
    [self FormatValue:curval :OVcalcDspStr];
    [self FormatValue:curval :OVcalcValStr];
        
    [OVcalcDisplay newLine:self];
    [OVcalcDisplay setCurrentLine:" = "];
    [OVcalcDisplay appendCurrentLine:OVcalcValStr];
    [OVcalcDisplay newLine:self];
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    OVcalcCEIndex   = 0;
    OVcalcEqualized = YES;
    }

else
    [self SyntaxError: OPCODE_Equals: "="];

return self;
}/* END Equals */



/******************************************************************************
* METHOD:- Exponential		      	    	    	    	    	      *
*   Perform actions in  response  to  the E^X button being pressed.  Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- Exponential:sender;

{/* BEGIN Exponential */
if ( OVcalcInverse )
    /* The Inverse modifier is on, Opcode will be Ln */
    [self ProcessFunction:OPCODE_Ln :"Ln:"];

else
    /* The Inverse modifier is off, Opcode will be an E^X */
    [self ProcessFunction:OPCODE_Exp :"Exp:"];
return(self);
}/* END Exponential */



/******************************************************************************
* METHOD:- Factorial		      	    	    	    	    	      *
*   Perform actions in response to  the X! button being pressed.  Send opcode *
* to the Function Processor routine.   	    	    	    	    	      *
******************************************************************************/
- Factorial:sender;

{/* BEGIN Factorial */
[self ProcessFunction:OPCODE_Factorial :"X!:"];
return self;
}/* END Factorial */



/******************************************************************************
* METHOD:- Integer		      	    	    	    	    	      *
*   Perform actions in response to  the [Int] button being pressed.  Send the *
* opcode to the Function Processor routine.    	    	    	    	      *
******************************************************************************/
- Integer:sender;

{/* BEGIN Integer */
[self ProcessFunction:OPCODE_Integer :"Int:"];
return self;
}/* END Integer */



/******************************************************************************
* METHOD:- FormatValue	    	    	    	    	    	    	      *
*   This method accepts a single operand  value and formats into the supplied *
* string according to the current numeric base and operand unit size.	      *
******************************************************************************/
- FormatValue: (double)operand : (char*) fmtstr

{   /* Local Variables */
    char  byte;	    	    	/* Space to convert double operand to byte value */
    long  longword; 	/* Space to convert double operand to longword value */
    char  hexstr[16];	/* Input string for binary conversion */
    short word;	    	/* Space to convert double operand to word value */

/* BEGIN FormatValue */
if ( OVcalcBase == BASE_Decimal )
    sprintf(fmtstr, "%.*f", OVcalcPrecsnVal, operand);
    /*sprintf(fmtstr, "%.*g", OVcalcPrecsnVal,operand);*/

else if ( OVcalcBase == BASE_Hex )
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    longword = operand;
    	    sprintf(fmtstr, "%*.*lX", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned long)longword);
    	    break;
    	case UNIT_Word:
    	    word = operand;
    	    sprintf(fmtstr, "%*.*hX", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned short)word);
    	    break;
    	case UNIT_Byte:
    	    byte = operand;
    	    sprintf(fmtstr, "%*.*X", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned int)byte);
    	    break;
    	}/*switch*/

else if ( OVcalcBase == BASE_Octal )
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    longword = operand;
    	    sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned long)longword);
    	    break;
       	case UNIT_Word:
    	    word = operand;
    	    sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned short)word);
    	    break;
    	case UNIT_Byte:
    	    byte = operand;
    	    sprintf(fmtstr, "%*.*o", OVcalcPrecsnVal,OVcalcPrecsnVal,
    	    	    (unsigned char)byte);
    	    break;
    	}/*switch*/

else if ( OVcalcBase == BASE_Binary )
    {
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    longword = operand;
    	    sprintf(hexstr, "%8.8X", (unsigned long)longword);
    	    break;
       	case UNIT_Word:
    	    word = operand;
    	    sprintf(hexstr, "%4.4X", (unsigned short)word);
    	    break;
    	case UNIT_Byte:
    	    byte = operand;
    	    sprintf(hexstr, "%2.2X", (unsigned char)byte);
    	    break;
    	}/*switch*/
    STR_HexToBinary (hexstr,fmtstr);

    /* Adjust resultant value to current precision setting */
    longword = strlen(fmtstr);
    if ( index(fmtstr,'1') == NULL )
    	/* The string contains all zeroes, display precision length zero */
    	word = longword - OVcalcPrecsnVal;
    else
    	{/* Adjust value string for non-zero case */
    	word     = strlen(index(fmtstr, '1'));
    	if ( word < OVcalcPrecsnVal )
    	    /* Value shorter than precision, trim leading zeroes for proper pad */
    	    word = longword - OVcalcPrecsnVal;
    	else 
    	    /* Value longer than precision, trim all leading zeroes */
    	    word = longword - word;
    	}
    strcpy(fmtstr,fmtstr+word);
    }
return(self);
}/* END FormatValue */



/******************************************************************************
* METHOD:- MemoryClear    	    	    	    	    	    	      *
*   Process the [MC] button event.   This  routine clears the contents of the *
* current memory value string and its associated text window.	    	      *
******************************************************************************/
- MemoryClear:sender

{/* BEGIN MemoryClear */
OVcalcMemDspStr[0] = 0;
OVcalcMemoryVal[OVcalcMemoryCell]  = 0.0;
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];
return(self);
}/* END MemoryClear */



/******************************************************************************
* METHOD:- MemoryRecall    	    	    	    	    	    	      *
*   Process  the  [MR]  button  event.   This  routine  checks  to see if the *
* Calculator is in a state to accept  an  operand.  If it is the value in the *
* Memory string is entered as the new operand.	    	    	    	      *
******************************************************************************/
- MemoryRecall:sender

{/* BEGIN MemoryRecall */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 )
    {/* We are expecting a digit to be entered */

    /* Copy memory value string to current & display strings, redisplay */
    strcpy(OVcalcValStr, OVcalcMemDspStr);
    strcpy(&OVcalcDspStr[OVcalcCEIndex], OVcalcMemDspStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    if ( OVcalcParenBalance > 0 )
    	OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
    else
    	OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
    }/*if*/

return self;
}/* END MemoryRecall */



/******************************************************************************
* METHOD:- MemorySelect  	    	    	    	    	    	      *
*   This method handles messages from  the memory cell selection buttons.  It *
* is responsible for modifying the current memory cell index and updating the *
* memory cell label and display string.	    	    	    	    	      *
******************************************************************************/
- MemorySelect:sender;

{   /* Local Variables */
    char fmtstr[2];

/* BEGIN MemorySelect */
/* increment/decrement memory cell index according to selection button */
OVcalcMemoryCell += [sender selectedTag];
if ( OVcalcMemoryCell < MCELL_minimum )   OVcalcMemoryCell = MCELL_minimum;
if ( OVcalcMemoryCell > MCELL_maximum )   OVcalcMemoryCell = MCELL_maximum;

/* Set the memory cell label to correspond to the current cell index */
fmtstr[0] = 'A' + OVcalcMemoryCell;
fmtstr[1] = 0;
[OVcalcMemCellLabel setStringValue:fmtstr];

/* Set the memory value display to the cell string */
if ( OVcalcMemoryVal[OVcalcMemoryCell] == 0.0 )
    /* Use a null string instead of a formatted 0 */
    OVcalcMemDspStr[0] = 0;
else
    /* Format the value in current base and unit selection */    
    [self FormatValue:OVcalcMemoryVal[OVcalcMemoryCell]  :OVcalcMemDspStr];
[OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];

return(self);
}/* END MemorySelect */



/******************************************************************************
* METHOD:- MemoryStore    	    	    	    	    	    	      *
*   Process the [MS]  button  event.  A check is made to see if a valid value *
* currently exists in the current value  string.   If no valid value has been *
* entered the routine  returns  without  taking  any  action.   Otherwise the *
* current value string is copied to  memory value string, and the memory text *
* window is then updated with the new value.      	      	    	      *
******************************************************************************/
- MemoryStore:sender

{/* BEGIN MemoryStore */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( [self ValueEntered] )
    {/* A viable operand value has been entered or is available */

    /* Copy current value string & display Memory string in text window */
    strcpy (OVcalcMemDspStr, OVcalcValStr);
    [OVcalcMemoryDsp setStringValue:OVcalcMemDspStr];

    [self ScanValue:OVcalcMemDspStr :&OVcalcMemoryVal[OVcalcMemoryCell]];
    }

return self;
}/* END MemoryStore */



/******************************************************************************
* METHOD:- Minus    	    	    	    	    	    	    	      *
*   Process the Minus operator button event. 	    	    	    	      *
******************************************************************************/
- Minus:sender

{/* BEGIN Minus */
[self ProcessOperator: OPCODE_Minus: "-"];
return self;
}/* END Minus */



/******************************************************************************
* METHOD:- Modify    	    	    	    	    	    	    	      *
*   Process the Modify operator button event. 	    	    	    	      *
******************************************************************************/
- Modify:sender

{/* BEGIN Modify */
[self ProcessOperator: OPCODE_Modify: "%"];
return self;
}/* END Modify */



/******************************************************************************
* METHOD:- Multiply    	    	    	    	    	    	    	      *
*   Process the Multiply operator button event.	    	    	    	      *
******************************************************************************/
- Multiply:sender

{/* BEGIN Multiply */
[self ProcessOperator: OPCODE_Multiply: "*"];
return self;
}/* END Multiply */



/******************************************************************************
* METHOD:- OpenParen	   	    	    	    	    	    	      *
*   Handle the actions needed when the "(" button is pressed.                 *
******************************************************************************/
- OpenParen:sender

{/* BEGIN OpenParen */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_OpenParen) != 0 )
    {/* We are expecting an open paren */

    /* With a valid Open Paren set new acceptance state */
    OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;
    OVcalcParenBalance++;   	/* increment open paren count */

    if (OVcalcEqualized == YES)
    	{/* We need to wipeout preloaded equalized value and start a new one */
    	OVcalcDspStr[0] = OVcalcValStr[0] = 0;
    	OVcalcEqualized = NO;
    	}

    /* Add operator symbol to display string and redisplay */
    strcat (OVcalcDspStr, "(");
    OVcalcCEIndex = strlen(OVcalcDspStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    /* Push the new opcode on the operator stack */
    [OVopStack Push:OPCODE_OpenParen];
    }

else
    /* Enter error state and display syntax error */
    [self SyntaxError:OPCODE_OpenParen :"("];

return self;
}/* END OpenParen */



/******************************************************************************
* METHOD:- Plus	    	    	    	    	    	    	    	      *
*   Process the Plus operator button event. 	    	    	    	      *
******************************************************************************/
- Plus:sender

{/* BEGIN Plus */
[self ProcessOperator: OPCODE_Plus: "+"];
return self;
}/* END Plus */



/******************************************************************************
* METHOD:- ProcessDigit	    	    	    	    	    	    	      *
*   This method check  the  validity  of  each  digit  of an operand as it is *
* entered.   The  character  value of the new digit is supplied by the caller *
* and if acceptable is appended to the current Display and Value strings.     *
******************************************************************************/
- ProcessDigit:(char) dgtchr

{   /* Local Variables */
    char *inset;    /* Char pointer to locate dgtchr in base digit set */

/* BEGIN ProcessDigit */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_Digit) != 0 )
    {/* We are expecting a digit to be entered */

    /* Determine if digit valid for current number Base */
    if (OVcalcBase == BASE_Decimal)
    	inset = index("0123456789.",dgtchr);
    else if (OVcalcBase == BASE_Hex)
    	inset = index("0123456789ABCDEF",dgtchr);
    else if (OVcalcBase == BASE_Octal)
    	inset = index("01234567",dgtchr);
    else /* OVcalcBase == BASE_Binary */
    	inset = index("01",dgtchr);
    if (inset == NULL)
    	/* Entered digit is not proper set for base, ignore it */
    	return self;

    if (OVcalcEqualized == YES)
    	{/* We need to wipeout preloaded equalized value and start a new one */
    	OVcalcDspStr[0] = OVcalcValStr[0] = 0;
    	OVcalcEqualized = NO;
    	}

    /* Determine if we have exceeded maximum digit limit */
    if (OVcalcValStr[0] == '-' && strlen(OVcalcValStr) == OVcalcMaxDigits+1)
    	return self;
    else if (strlen(OVcalcValStr) == OVcalcMaxDigits)
    	return self;

    /* Append the digit character to the Value and Display strings */
    strncat (OVcalcValStr, &dgtchr, 1);
    strncat (OVcalcDspStr, &dgtchr, 1);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    if ( [self ValueEntered] )
    	{/* Viable operand has been formed, so an operator is now acceptable */
    	if ( OVcalcParenBalance > 0 )
    	    OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator|ACCEPT_CloseParen;
    	else
    	    OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_Operator;
    	}
    }

return self;
}/* END ProcessDigit */



/******************************************************************************
* METHOD:- ProcessFunction   	    	    	    	    	    	      *
*   Perform all common processing Function call type operator codes.          *
******************************************************************************/
- ProcessFunction: (int) opcode: (char *)symbol

{   /* Local Variables */
    double  curval;
    int     preced;

/* BEGIN ProcessFunction */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( OVcalcFunctionMode == FMODE_Evaluate )
    {/* Process the function within the context of the current expression */

    if ( (OVcalcAcceptMode & ACCEPT_Function) != 0 )
	{/* We are expecting a function call opcode */

	/* Set new acceptance mode after a valid operator */
	OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen;

	if (OVcalcEqualized == YES)
	    {/* We need to wipeout preloaded equalized value and start a new one */
	    OVcalcDspStr[0] = OVcalcValStr[0] = 0;
	    OVcalcEqualized = NO;
	    }

	/* Add operator symbol to display string and redisplay */
	strcat (OVcalcDspStr, symbol);
	OVcalcCEIndex = strlen(OVcalcDspStr);
	[OVcalcDisplay setCurrentLine:OVcalcDspStr];


	[OVopStack ComparePrecedence: opcode: &preced];
	if ( preced <= 0 )
	    /* OpCode has a lower  precedence  then the operator on the top of */
	    /* operator stack, reduce expresion until lower opcode is reached  */
	    [self ReduceExpression:opcode];

	/* Push the new opcode on the operator stack */
	[OVopStack Push:opcode];
	}

    else
    	/* Enter error state and display syntax error */
    	[self SyntaxError:opcode :symbol];
    }

else if ( OVcalcFunctionMode == FMODE_Operate )
    {/* Try to operate the function on the currently entered value */
    if ( [self ValueEntered] )
    	{/* Then there is a value to operate the function on */

    	/* Read the currently entered value and perform function on value */
    	[self ScanValue:OVcalcValStr :&curval];
    	curval = [self ProcessOpcode:opcode :curval :curval];

    	/* Clear current entry strings, reformat and redisplay */
    	OVcalcDspStr[OVcalcCEIndex] = OVcalcValStr[0] = 0;
    	[self FormatValue:curval :OVcalcValStr];
    	strcat (OVcalcDspStr, OVcalcValStr);
    	[OVcalcDisplay setCurrentLine:OVcalcDspStr];
    	}
    }

return self;
}/* END ProcessFunction */



/******************************************************************************
* INSTANCE METHOD:- ProcessOpcode   	    	    	    	    	      *
*   This method executes the operation indicated by the supplied opcode using *
* the supplied operands.  The result is returned to the caller via the retrun *
* value.    	    	    	    	    	    	    	    	      *
******************************************************************************/
- (double) ProcessOpcode :(int)opcode :(double)operand1 :(double)operand2

{   /* Local Variables */
    double  result;	    /* Temporary workspace for opcode evaluation */
    long    longword1;	    /* Bitwise Longword operand1 */
    long    longword2;	    /* Bitwise Longword operand2 */
    short   word1;	    /* Bitwise Word operand1 */
    short   word2;	    /* Bitwise Word operand2 */
    char    byte1;	    /* Bitwise Byte operand1 */
    char    byte2;	    /* Bitwise Byte operand2 */

/* BEGIN PerformOpcode */
switch (opcode)
    {
    case OPCODE_ACos:
	result = acos(operand1);
	if ( OVcalcMetric == METRIC_Degrees )
	    result *= (180.0/M_PI);
	if ( OVcalcMetric == METRIC_Grads )
	    result *= (200.0/M_PI);
    	return result;

    case OPCODE_ACosH:
    	return acosh(operand1);

    case OPCODE_And:
    	byte1 = word1 = longword1 = operand1;
    	byte2 = word2 = longword2 = operand2;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = (longword1 & longword2);
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = (word1 & word2);
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = (byte1 & byte2);
    	else
    	    result = NAN;
    	return result;

    case OPCODE_ASin:
	result = asin(operand1);
	if ( OVcalcMetric == METRIC_Degrees )
	    result *= (180.0/M_PI);
	if ( OVcalcMetric == METRIC_Grads )
	    result *= (200.0/M_PI);
    	return result;

    case OPCODE_ASinH:
    	return asinh(operand1);

    case OPCODE_ATan:
	result = atan(operand1);
	if ( OVcalcMetric == METRIC_Degrees )
	    result *= (180.0/M_PI);
	if ( OVcalcMetric == METRIC_Grads )
	    result *= (200.0/M_PI);
    	return result;

    case OPCODE_ATanH:
	return atanh(operand1);

    case OPCODE_Cos:
	if ( OVcalcMetric == METRIC_Degrees )
	    operand1 *= (M_PI/180.0);
	if ( OVcalcMetric == METRIC_Grads )
	    operand1 *= (M_PI/200.0);
    	return cos(operand1);

    case OPCODE_CosH:
	return cosh(operand1);

    case OPCODE_Divide:
	return (operand1 / operand2);

    case OPCODE_Exp:
    	return exp(operand1);

    case OPCODE_Factorial:
	if (operand1 < 0.0)
	    result = NAN;
	else
	    {/* Round to nearest integer */
	    operand1 = rint(operand1);
	    result   = operand2 = 1.0;
	    while (operand2 <= operand1)
		{
		result   *= operand2;
		operand2 += 1.0;
		}
	    }
    	return result;

    case OPCODE_Integer:
    	return rint(operand1);

    case OPCODE_Ln:
	return log(operand1);

    case OPCODE_Log:
	return log10(operand1);

    case OPCODE_LShift:
    	byte1 = word1 = longword1 = operand1;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = (longword1 << 1);
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = (word1 << 1);
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = (byte1 << 1);
    	else
    	    result = NAN;
    	return result;

    case OPCODE_Minus:
	return (operand1 - operand2);

    case OPCODE_Modify:
	result = floor(operand1/operand2);
	result = operand1 / operand2 - result;
	result = result * operand2;
    	return rint(result);

    case OPCODE_Multiply:
	return (operand1 * operand2);

    case OPCODE_Not:
    	byte1 = word1 = longword1 = operand1;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = ~longword1;
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = ~word1;
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = ~byte1;
    	else
    	    result = NAN;
    	return result;

    case OPCODE_Or:
    	byte1 = word1 = longword1 = operand1;
    	byte2 = word2 = longword2 = operand2;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = (longword1 | longword2);
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = (word1 | word2);
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = (byte1 | byte2);
    	else
    	    result = NAN;
    	return result;

    case OPCODE_Pi:
	return M_PI;

    case OPCODE_Plus:
	return (operand1 + operand2);

    case OPCODE_Recip:
	return ((double)1.0 / operand1);

    case OPCODE_RShift:
    	byte1 = word1 = longword1 = operand1;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = (longword1 >> 1);
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = (word1 >> 1);
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = (byte1 >> 1);
    	else
    	    result = NAN;
    	return result;

    case OPCODE_Sin:
	if ( OVcalcMetric == METRIC_Degrees )
	    operand1 *= (M_PI/180.0);
	if ( OVcalcMetric == METRIC_Grads )
	    operand1 *= (M_PI/200.0);
    	return sin(operand1);

    case OPCODE_SinH:
	return sinh(operand1);

    case OPCODE_SqrRoot:
	return sqrt(operand1);

    case OPCODE_Tan:
	if ( OVcalcMetric == METRIC_Degrees )
	    operand1 *= (M_PI/180.0);
	if ( OVcalcMetric == METRIC_Grads )
	    operand1 *= (M_PI/200.0);
	return tan(operand1);

    case OPCODE_TanH:
	return tanh(operand1);

    case OPCODE_TenToX:
	return pow((double) 10.0,operand1);

    case OPCODE_Xor:
    	byte1 = word1 = longword1 = operand1;
    	byte2 = word2 = longword2 = operand2;
    	if ( OVcalcUnit == UNIT_Longword )
    	    result = (longword1 ^ longword2);
    	else if ( OVcalcUnit == UNIT_Word )
    	    result = (word1 ^ word2);
    	else if ( OVcalcUnit == UNIT_Byte )
    	    result = (byte1 ^ byte2);
    	else
    	    result = NAN;
    	return result;

    case OPCODE_XrootY:
	return pow(operand2,1.0/operand1);

    case OPCODE_XSquared:
	return (operand1*operand1);

    case OPCODE_XToY:
	return pow(operand1,operand2);
    }/*switch*/

return NAN;
}/* END PerformOpcode */



/******************************************************************************
* METHOD:- ProcessOperator   	    	    	    	    	    	      *
*   Perform all common processing for all normal arithmetic operators.        *
******************************************************************************/
- ProcessOperator: (int) opcode: (char *)symbol

{   /* Local Variables */
    double curval;
    int    preced;

/* BEGIN ProcessOperator */
if ( OVcalcFrozen == YES )
    /* Calculator is in error state, ignore input until cleared */
    return self;

if ( (OVcalcAcceptMode & ACCEPT_Operator) != 0 )
    {/* We are expecting an operator */

    /* Set new acceptance mode after a valid operator */
    OVcalcAcceptMode = ACCEPT_Digit|ACCEPT_OpenParen|ACCEPT_Function;

    /* Add operator symbol to display string and redisplay */
    strcat (OVcalcDspStr, symbol);
    OVcalcCEIndex = strlen(OVcalcDspStr);
    [OVcalcDisplay setCurrentLine:OVcalcDspStr];

    if ( [self ValueEntered] )
        {/* We have an operator after an unprocessed value */
        OVcalcEqualized = NO;   /* Cancel expression equalization state */

        /* Translate value string to binary and push onto value stack */
        [self ScanValue:OVcalcValStr :&curval];
        [OVvalStack Push:curval];

    	/* Clear value string for next entered value */
    	OVcalcValStr[0] = 0;
    	}

    [OVopStack ComparePrecedence: opcode: &preced];
    if ( preced <= 0 )
    	/* OpCode has a lower  precedence  then the operator on the top of */
    	/* operator stack, reduce expresion until lower opcode is reached  */
    	[self ReduceExpression:opcode];

    /* Push the new opcode on the operator stack */
    [OVopStack Push:opcode];
    }

else
    /* Enter error state and display syntax error */
    [self SyntaxError:opcode :symbol];

return self;
}/* END ProcessOperator */



/******************************************************************************
* METHOD:- ReduceExpression  	    	    	    	    	    	      *
*   This method performs each of  the  operations on the Operator stack until *
* an operator of lesser precedence then the supplied operator is found on the *
* top of the stack. 	    	    	    	    	    	    	      *
******************************************************************************/
- ReduceExpression: (int) curopr

{   /* Local Variables */
    int	    noperands;
    double  operand1;
    double  operand2;
    int	    preced;
    int	    topopr;

/* BEGIN ReduceExpression */
[OVopStack ComparePrecedence: curopr: &preced];
while ( preced <= 0 )
    {/* The stack top opcode still has higher precedence then current opcode */

    /* Pop the opcode of the top of the stack and process it */
    [OVopStack Pop:&topopr];

    /* Retrieve the proper number of operands for this opcode */
    [OVopStack NumberOfOperands:topopr :&noperands];
    if ( noperands == 1 )
    	{
        [OVvalStack Pop:&operand1];
    	operand2 = operand1;
    	}
    else if ( noperands == 2 )
    	{
        [OVvalStack Pop:&operand2];
        [OVvalStack Pop:&operand1];
    	}

    /* Execute the opcode on the operands and stack result */
    [OVvalStack Push:[self ProcessOpcode:topopr :operand1 :operand2]];

    /* Compare current opcode against new Operator stack top */
    [OVopStack ComparePrecedence: curopr: &preced];
    }/*while*/

return self;
}/* END ReduceExpression */



/******************************************************************************
* METHOD:- Reciprocal		      	    	    	    	    	      *
*   Perform actions in response  to  the X^-1 button being pressed.  	      *
******************************************************************************/
- Reciprocal:sender;

{/* BEGIN Reciprocal */
[self ProcessFunction:OPCODE_Recip :"1/X:"];
return(self);
}/* END Reciprocal */



/******************************************************************************
* METHOD:- ScanValue	    	    	    	    	    	    	      *
*   This method accepts a string representing a formated value in the current *
* base and unit size.   This  routine  converts  the  formated  string to its *
* appropriate binary value. 	  	    	    	    	    	      *
******************************************************************************/
- ScanValue: (char*) fmtstr :(double*)operand

{   /* Local Variables */
    char  byte;	    	/* Space to convert double operand to byte value */
    long  longword; 	/* Space to convert double operand to longword value */
    char  hexstr[16];	/* Input string for binary conversion */
    short word;	    	/* Space to convert double operand to word value */

/* BEGIN ScanValue */
if ( OVcalcBase == BASE_Decimal )
    sscanf(fmtstr, "%lf", operand);

else if ( OVcalcBase == BASE_Hex )
    {
    sscanf(fmtstr, "%lx", (unsigned long*)&longword);
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    *operand = longword;
    	    break;
    	case UNIT_Word:
    	    *operand = word = longword;
    	    break;
    	case UNIT_Byte:
    	    *operand = byte = word = longword;
    	    break;
    	}/*switch*/
    }

else if ( OVcalcBase == BASE_Octal )
    {
    sscanf(fmtstr, "%o", (unsigned long*)&longword);
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    *operand = longword;
    	    break;
       	case UNIT_Word:
    	    *operand = word = longword;
    	    break;
    	case UNIT_Byte:
    	    *operand = byte = word = longword;
    	    break;
    	}/*switch*/
    }

else if ( OVcalcBase == BASE_Binary )
    {
    STR_BinaryToHex (fmtstr, hexstr);
    sscanf(hexstr, "%lX", (unsigned long*)&longword);
    switch ( OVcalcUnit )
    	{
    	case UNIT_Longword:
    	    *operand = longword;
    	    break;
    	case UNIT_Word:
    	    *operand = word = longword;
    	    break;
    	case UNIT_Byte:
    	    *operand = byte = word = longword;
    	    break;
    	}/*switch*/
    }
return(self);
}/* END ScanValue */



/******************************************************************************
* METHOD:- Sine		    	    	    	    	    	    	      *
*   Perform actions in  response  to  the SIN button being pressed.  Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- Sine:sender;

{/* BEGIN Sine */
if ( OVcalcInverse )
    {/* The Inverse modifier is on, Opcode will be an ASINx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be ASINH */
    	[self ProcessFunction:OPCODE_ASinH :"ASINH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be ASIN */
    	[self ProcessFunction:OPCODE_ASin :"ASIN:"];
    }    	

else
    {/* The Inverse modifier is off, Opcode will be an SINx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be SINH */
    	[self ProcessFunction:OPCODE_SinH :"SINH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be SIN */
    	[self ProcessFunction:OPCODE_Sin :"SIN:"];
    }
return(self);
}/* END Sine */



/******************************************************************************
* METHOD:- SyntaxError	    	    	    	    	    	    	      *
*   Display a syntax  error  message  and  set the Frozen state to true.  The *
* Frozen state will block further input until a Clear operation is performed. *
******************************************************************************/
- SyntaxError: (int) opcode: (char *) symbol;

{/* BEGIN SyntaxError */

/* Append syntax error message to display string and redisplay */
strcat (OVcalcDspStr, "<Syntax Error>");
[OVcalcDisplay setCurrentLine:OVcalcDspStr];

/* Set Frozen state to block further input procedures, until clear */
OVcalcFrozen = YES;

#ifdef DEBUG
 /* Dump stack states to standard output */
 printf("Syntax Error due to [%s] operator\n",symbol);
 printf("Value Stack:\n");
 [OVvalStack PrintStack];
 printf("\nOperator Stack:\n");
 [OVopStack PrintStack];
#endif

return self;
}/* END SyntaxError */



/******************************************************************************
* METHOD:- Tangent		    	    	    	    	    	      *
*   Perform actions in  response  to  the Tan button being pressed.  Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- Tangent:sender;

{/* BEGIN Tangent */
if ( OVcalcInverse )
    {/* The Inverse modifier is on, Opcode will be an ATANx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be ATANH */
    	[self ProcessFunction:OPCODE_ATanH :"ATANH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be ATAN */
    	[self ProcessFunction:OPCODE_ATan :"ATAN:"];
    }    	

else
    {/* The Inverse modifier is off, Opcode will be an TANx */
    if ( OVcalcHyperbolic )
    	/* Hyperbolic modifier on, Opcode will be TANH */
    	[self ProcessFunction:OPCODE_TanH :"TANH:"];
    else
    	/* Hyperbolic modifier off, Opcode will be TAN */
    	[self ProcessFunction:OPCODE_Tan :"TAN:"];
    }
return(self);
}/* END Tangent */



/******************************************************************************
* METHOD:- TenToTheX		      	    	    	    	    	      *
*   Perform actions in  response to  the 10^X button being pressed.  Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- TenToTheX:sender;

{/* BEGIN TenToTheX */
if ( OVcalcInverse )
    /* The Inverse modifier is on, Opcode will be Log */
    [self ProcessFunction:OPCODE_Log :"LOG:"];

else
    /* The Inverse modifier is off, Opcode will be an 10^X */
    [self ProcessFunction:OPCODE_TenToX :"10^X:"];
return(self);
}/* END TenToTheX */



/******************************************************************************
* METHOD: ValueEntered 	    	    	    	    	    	    	      *
*   Determine if the  current  value  string  has  enough characters in it to *
* constitute an operand.    	    	    	    	    	    	      *
******************************************************************************/
- ValueEntered

{/* BEGIN ValueEntered */
if ( strlen(OVcalcValStr) == 0 )
    return nil;

if ( OVcalcValStr[0] == '.' && strlen(OVcalcValStr) == 1 )
    return nil;

if ( OVcalcValStr[0] == '-')
    {
    if ( strlen(OVcalcValStr) == 1 )
    	return nil;
    if ( OVcalcValStr[1] == '.' && strlen(OVcalcValStr) == 2 )
    	return nil;
    }
return self;
}/* END ValueEntered */



/******************************************************************************
* METHOD:- XSquared		      	    	    	    	    	      *
*   Perform actions in  response to  the X^2 button being pressed.   Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Function *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- XSquared:sender;

{/* BEGIN XSquared */
if ( OVcalcInverse )
    /* The Inverse modifier is on, Opcode will be Square Root */
    [self ProcessFunction:OPCODE_SqrRoot :"SQRT:"];

else
    /* The Inverse modifier is off, Opcode will be an X^2 */
    [self ProcessFunction:OPCODE_XSquared :"X^2:"];
return(self);
}/* END XSquared */



/******************************************************************************
* METHOD:- XToTheY		      	    	    	    	    	      *
*   Perform actions in  response to  the X^Y button being pressed.   Based on *
* the current modifier flags  send  the  appropriate  opcode  to the Operator *
* Processor routine.	    	    	    	    	    	    	      *
******************************************************************************/
- XToTheY:sender;

{/* BEGIN XToTheY */
if ( OVcalcInverse )
    /* The Inverse modifier is on, Opcode will be XrootY */
    [self ProcessOperator:OPCODE_XrootY :"XrootY"];

else
    /* The Inverse modifier is off, Opcode will be an X^Y */
    [self ProcessOperator:OPCODE_XToY :"X^Y"];
return(self);
}/* END XToTheY */



/******************************************************************************
* METHOD:- setTag		      	    	    	    	    	      *
*   This method is used so that this object can pose as a Control object.     *
******************************************************************************/
- setTag:(int)anInt;
{/* BEGIN setTag */
OVtag = anInt;
return self;
}/* END setTag */



/******************************************************************************
* METHOD:- selectedTag		      	    	    	    	    	      *
*   This method is used so that this object can pose as a Control object.     *
******************************************************************************/
- (int) selectedTag
{/* BEGIN selectedTag */
return OVtag;
}/* END selectedTag */
@end
