/**************************************************************************/
/*** SCICALC Scientific Calculator for Windows 3.00.12                  ***/
/*** By Kraig Brockschmidt, Microsoft Co-op, Contractor, 1988-1989      ***/
/*** (c)1989 Microsoft Corporation.  All Rights Reserved.               ***/
/***                                                                    ***/
/*** scifunc.c                                                          ***/
/***                                                                    ***/
/*** Functions contained:                                               ***/
/***    SciCalcFunctions--do sin, cos, tan, com, log, ln, rec, fac, etc.***/
/***    DisplayError--Error display driver.                             ***/
/***                                                                    ***/
/*** Functions called:                                                  ***/
/***    SciCalcFunctions call DisplayError.                             ***/
/***                                                                    ***/
/*** Last modification. Fri  05-Jan-1990.                               ***/
/***                                                                    ***/
/*** -by- Amit Chatterjee. [amitc]  05-Jan-1990.                                                      ***/
/*** Calc did not have a floating point exception signal handler. This  ***/
/*** would cause CALC to be forced to exit on a FP exception as that's  ***/
/*** the default.                                                                                                                                                  ***/
/*** The signal handler is defined in here, in SCIMAIN.C we hook the    ***/
/*** the signal.                                                                                                                                    ***/
/***                                                                    ***/
/*** -by- Amit Chatterjee. [amitc] 14-Dec-1989                                                   ***/
/*** The REC function will not depend on the bInv flag. It used to ret  ***/
/*** a random number when the bInv flag was set.                                                 ***/
/***                                                                    ***/
/*** -by- Amit Chatterjee.      [amitc] 08-Dec-1989                                                   ***/
/*** Did a minor bug fix. The EnableToggles routine now sets the focus  ***/
/*** back to the main window before disabling HEX,DEC etc.. Without this***/
/*** the window with the focus would get disable and cause MOVE to not  ***/
/*** work right.                                                                                                                ***/
/***                                                                    ***/
/**************************************************************************/

#include "scicalc.h"
//#include "float.h"

extern HNUMOBJ     ghnoLastNum;
extern BOOL        bError;
extern TCHAR       *rgpsz[CSTRINGS];
INT                gnPendingError ;

/* Routines for more complex mathematical functions/error checking.       */

VOID  APIENTRY SciCalcFunctions (PHNUMOBJ phnoNum, DWORD wOp)
{
    try
    {
        switch (wOp)
        {
            case IDC_CHOP:
                if (bInv)
                {
                    // fractional portion
                    fracrat( phnoNum );
                }
                else
                {
                    // integer portion
                    intrat( phnoNum );
                }
                return;

            /* Return complement.                                             */
            case IDC_COM:
                NumObjNot( phnoNum );
                return;


            case IDC_PERCENT:
                {
                    DECLARE_HNUMOBJ( hno );
                    DECLARE_HNUMOBJ( hno100 );

                    try
                    {
                        NumObjAssign( &hno, ghnoLastNum );
                        NumObjSetIntValue( &hno100, 100 );

                        divrat( &hno, hno100 );

                        NumObjDestroy( &hno100 );

                        mulrat( phnoNum, hno );

                        NumObjDestroy( &hno );
                    }
                    catch ( DWORD nErrCode )
                    {
                        if ( hno != NULL )
                            NumObjDestroy( &hno );
                        if ( hno100 != NULL ) 
                            NumObjDestroy( &hno100 );
                        throw nErrCode;
                    }
                    return;
                }

            case IDC_SIN: /* Sine; normal, hyperbolic, arc, and archyperbolic     */
                if (F_INTMATH())
                {
                    MessageBeep(0);
                    return;
                }

                if(bInv)
                {
                    if (bHyp)
                    {
                        asinhrat( phnoNum );
                    }
                    else
                    {
                        asinanglerat( phnoNum, nDecMode );
                    }
                }
                else
                {
                    if (bHyp)
                    {
                        // hyperbolic sine
                        sinhrat( phnoNum );
                    }
                    else
                    {
                        NumObjSin( phnoNum );
                    }
                }
                return;

            case IDC_COS: /* Cosine, follows convention of sine function.         */
                if (F_INTMATH())
                {
                    MessageBeep(0);
                    return;
                }

                if(bInv)
                {
                    if (bHyp)
                    {
                        acoshrat( phnoNum );
                    }
                    else
                    {
                        acosanglerat( phnoNum, nDecMode );
                    }
                }
                else
                {
                    if (bHyp)
                        coshrat( phnoNum );
                    else
                    {
                        // cos()
                        NumObjCos( phnoNum );
                    }
                }
                return;

            case IDC_TAN: /* Same as sine and cosine.                             */
                if (F_INTMATH())
                {
                    MessageBeep(0);
                    return;
                }

                if(bInv)
                {
                    if (bHyp)
                    {
                        atanhrat( phnoNum );
                    }
                    else
                    {
                        atananglerat( phnoNum, nDecMode );
                    }
                }
                else
                {
                    if (bHyp)
                        tanhrat( phnoNum );
                    else
                    {
                        // Get the answer
                        NumObjTan( phnoNum );
                    }
                }
                return;

            case IDC_REC: /* Reciprocal.                                          */
                NumObjInvert( phnoNum );
                return;

            case IDC_SQR: /* Square and square root.                              */
            case IDC_SQRT:
                if(bInv || nCalc)
                {
                    rootrat( phnoNum, HNO_TWO );
                }
                else
                {
                    ratpowlong( phnoNum, 2 );
                }
                return;

            case IDC_CUB: /* Cubing and cube root functions.                      */
                if(bInv) {
                    DECLARE_HNUMOBJ( hno );

                    // REVIEW: if constants like 3 are going to be used repeatedly, it will be
                    // much quicker to define them once and then keep around the definition.
                    try
                    {
                        NumObjAssign( &hno, HNO_ONE );
                        addrat( &hno, HNO_TWO );

                        rootrat( phnoNum, hno );

                        NumObjDestroy( &hno );
                    }
                    catch ( DWORD nErrCode )
                    {
                        if ( hno != NULL )
                            NumObjDestroy( &hno );

                        throw nErrCode;
                    }
                }
                else {
                    /* Cube it, you dig?       */
                    ratpowlong( phnoNum, 3 );
                }
                return;

            case IDC_LOG: /* Functions for common and natural log.                */
            case IDC_LN:
                if(bInv)
                {
                    /* Check maximum for exponentiation for 10ü and eü.       */
                    if (wOp==IDC_LOG) /* Do exponentiation.                       */
                        NumObjAntiLog10( phnoNum ); // 10ü.
                    else
                        exprat( phnoNum );  // eü.
                }
                else
                {
                    // ratpak checks for valid range and throws error code if needed
                    if (wOp==IDC_LOG)
                        log10rat( phnoNum );
                    else
                        lograt( phnoNum );

                    // REVIEW: Is conversion of epsilon still needed?
                    NumObjCvtEpsilonToZero( phnoNum );
                }
                return;

            case IDC_FAC: /* Calculate factorial.  Inverse is ineffective.        */
                factrat( phnoNum );
                return;

            case IDC_DMS:
                {
                    if (F_INTMATH())
                    {
                        MessageBeep(0);
                    } 
                    else 
                    {
                        DECLARE_HNUMOBJ(hnoMin);
                        DECLARE_HNUMOBJ(hnoSec);
                        DECLARE_HNUMOBJ(hnoShft);

                        try
                        {
                            NumObjSetIntValue( &hnoShft, bInv ? 100 : 60 );

                            NumObjAssign( &hnoMin, *phnoNum );
                            intrat( phnoNum );

                            subrat( &hnoMin, *phnoNum );
                            mulrat( &hnoMin, hnoShft );
                            NumObjAssign( &hnoSec, hnoMin );
                            intrat( &hnoMin );

                            subrat( &hnoSec, hnoMin );
                            mulrat( &hnoSec, hnoShft );

                            //
                            // *phnoNum == degrees, hnoMin == minutes, hnoSec == seconds
                            //

                            NumObjSetIntValue( &hnoShft, bInv ? 60 : 100 );
                            divrat( &hnoSec, hnoShft );
                            addrat( &hnoMin, hnoSec );

                            divrat( &hnoMin, hnoShft );
                            addrat( phnoNum, hnoMin );

                            NumObjDestroy( &hnoShft );
                            NumObjDestroy( &hnoMin );
                            NumObjDestroy( &hnoSec );
                        }
                        catch ( DWORD nErrCode )
                        {
                            if ( hnoShft != NULL )
                                NumObjDestroy( &hnoShft );
                            if ( hnoMin != NULL )
                                NumObjDestroy( &hnoMin );
                            if ( hnoSec != NULL )
                                NumObjDestroy( &hnoSec );
                            throw nErrCode;
                        }
                    }
                    return;
                }
        }   // end switch( nOp )
    }
    catch( DWORD nErrCode )
    {
        DisplayError( nErrCode );
    }

    return;
}



/* Routine to display error messages and set bError flag.  Errors are     */
/* called with DisplayError (n), where n is a INT   between 0 and 5.      */

VOID  APIENTRY DisplayError (INT   nError)
{
    SetDisplayText(g_hwndDlg, rgpsz[IDS_ERRORS+nError]);
    bError=TRUE; /* Set error flag.  Only cleared with CLEAR or CENTR.    */
    
    /* save the pending error */
    gnPendingError = nError ;
    
    return;
}

