Native/C

계산기 (삼각함수 완료 bugfix)

aucd29 2013. 10. 2. 18:51
/***************************************************************************
*
*        Date        : 2005-05-03
*        Copyright    : aucd29
*        E-mail        : aucd29@daum.net
*
*        열라 짜증나게 만드는 계산기
*
***************************************************************************/

// 중위 -> 후위로 그리고 과정 보여주고 계산값 보여주고
// -10*----5                                        ok
// -10*-(-9.1--(--0.8--0.1))                        ok
// -10*-(-9.1(--0.8--0.1))                            ok
// -10*-cos(--sin(--45--45)/2-sin(30))                ok
// -10*-(-9.1--(--0.8--0.1))                        ok
// -10*-(-9.1(--0.8--0.1))                            ok

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <ctype.h>

//
// defintion
//
#define MAX_STACK_SIZE 100
#define MAX_EXPR_SIZE 100
typedef enum{
    lparen, rparen, plus, minus, times, divide, mod, eos, operand, blank, minus_val, _cos, _tan, _sin
}precedence;

//
// variant value
//
int stack[MAX_STACK_SIZE];
double dbl_stack[MAX_STACK_SIZE];
char expr[MAX_EXPR_SIZE];

char post_expr[MAX_EXPR_SIZE];
int post_i=0;
int rtnM;

static int isp[] = {0, 19, 12, 12, 13, 13, 13, 0, 0, 0, 0, 14, 14, 14};
static int icp[] = {20, 19, 12, 12, 13, 13, 13, 0, 0, 0, 0, 14, 14, 14};

//
// function
//
void add(int*, int);
char put_token(int n);
double eval(void);
int get_token(char *, int*, int*);
int get_token2(char* , int*);
void postfix(void);
void getline(char *);
void print_token(int);
int rmstack(int *);
double pop(int* top);
void SCTInput(int);
int is_tfunc(char*, int*);
double ConvertRadian(double);
int is_tfunc2(char*, int*);
void InsertPostFix(int token2);
void InsertMinus(void);

int main(int argc, char *argv[])
{
    //
    // 중위 표기식 입력 받고
    //
    getline(expr);

    //
    // 중위식을 후위식으로 바꾸고
    //
    postfix();

    //
    // 바뀐 표기식을 출력
    //
    printf("==== PostFix ===\n%s\n================\n",post_expr);

    //
    // 후위 표기식인걸 계산하자.
    //
    printf("%lf",eval());
    printf("\n");

    return 0;
}

//
// 배열에 주소에 내용을 입력 받는다.
//
void getline(char *s)
{
    int c, i;

    for(i=0;(c=getchar()) !='\n'; ++i)
        if(c!=' ' && MAX_EXPR_SIZE>i) *s++ = c;
    s = '\0';
}

//
// 중위 표기를 후위표기로 변환한다.
//
void postfix(void)
{
    char symbol;
    int token;
    int token2=0;
    int n = 0;
    int top = 0;        // insert eos
    stack[0] = eos;

    int x=0;
    while((token = get_token(&symbol, &n, &top)) != eos)
    {
        // printf("### %d : token %d, N: %d ###\n", ++x, token, n);
        if(token == operand)
        {
            post_expr[post_i++] = symbol;
            while((token2 = get_token(&symbol, &n, &top)) == operand)
                post_expr[post_i++] = symbol;
            --n;
            
            if(expr[n]=='(' && isdigit(expr[n-1])) add(&top,times); // 숫자뒤 가로 일 경우 곱하기를 하자.
            post_expr[post_i++] = ' ';
        }
        else if(token == rparen)
        {
            token2 = rmstack(&top);
            if(token2 != lparen) InsertPostFix(token2);

            token2 = rmstack(&top);
            if(token2 != lparen) InsertPostFix(token2);
        }
        else
        {
            // symbol 의 isp가 token의 icp보다 크거나 작으면 symbol을 제거하고 출력한다.
            while(isp[stack[top]] >= icp[token])
            {
                token2 = rmstack(&top);
                InsertPostFix(token2);
            }
            add(&top, token);
        }
        
    }

    while((token = (precedence)rmstack(&top)) != eos)
        if(token != lparen) InsertPostFix(token);

    post_expr[--post_i] = '\0';
    printf("\n");
}

void InsertPostFix(int token2)
{
    if(token2 == _cos || token2 == _sin || token2 == _tan)
        SCTInput(token2);
    else
        post_expr[post_i++] = put_token(token2);
    
    post_expr[post_i++] = ' ';
}

void SCTInput(int token2)
{
    if(token2 == _cos)
    {
        post_expr[post_i++] = 'c';
        post_expr[post_i++] = 'o';
        post_expr[post_i++] = 's';
    }
    else if(token2 == _sin)
    {
        post_expr[post_i++] = 's';
        post_expr[post_i++] = 'i';
        post_expr[post_i++] = 'n';
    }
    else if(token2 == _tan)
    {
        post_expr[post_i++] = 't';
        post_expr[post_i++] = 'a';
        post_expr[post_i++] = 'n';
    }
}

int CountMark(int *n)
{
    int i;
    for(i=0;'-'==expr[(*n)++];++i);

    return i;
}

void InsertMinus(void)
{
    post_expr[post_i++] = '-';
    post_expr[post_i++] = '1';
    post_expr[post_i++] = ' ';
}

int get_token(char* symbol, int* n, int* top)
{
    int tmpN=0;
    int is_tFunc=0;

    *symbol = expr[(*n)++];
    switch(*symbol)
    {
    case '-':
        if(*n==1)                            // 첫번째가 음수면 음수숫자이다.
        {
            if(isdigit(expr[*n]))    
                return operand;
            else
            {
                tmpN = *n;                        // 카운트 세기 전의 N값
                rtnM = CountMark(n);            // 여기서 n이 증가한다.
            
                if(rtnM % 2)
                {
                    *symbol = expr[*n-1];        // -10*----5 일경우 5를 리턴
                    if(*symbol == '(')
                        return false;
                    else
                        return operand;
                }
                else
                {
                    if(expr[*n-1] == '(')
                    {
                        InsertMinus();
                        add(top,times);
                    }
                    else
                    {
                        --*n;
                        return operand;
                    }
                }
            }
        }
        ////////////////////////////////////////////
        else if(expr[*n] == '-')            // 음수가 연속으로 들어간경우 최소 2개
        {
            tmpN = *n;                     // 카운트 세기 전의 N값
            rtnM = CountMark(n);            // 여기서 n이 증가한다.

            //printf(" === %d === ",rtnM);

            //
            // 홀수가 들어오면 양수로 바뀐다...
            //
            if(rtnM % 2)
            {
                // 앞부분이 숫자가 아닌경우                
                if(!isdigit(expr[tmpN-2]))
                {
                    *symbol = expr[*n-1]; // -10*----5 일경우 5를 리턴

                    if(*symbol == 's' || *symbol == 'c' || *symbol == 't')
                    {
                        if((is_tFunc = is_tfunc(symbol, n)))
                        {
                            ++*n;
                            return is_tFunc;
                        }
                        else
                            return false;
                    }
                    else if(*symbol == '(')
                    {
                        add(top,plus);
                        return false;
                    }
                            
                    return operand;
                }
                else
                {
                    add(top,plus);    // +로 바뀐다...
                    //--*n;

                    // 리턴 값을 안먹는다 ㅡ.ㅡ;
                    // 원랜 return plus;였음...
                    return false;
                }
            }
            else
            {
                if(isdigit(expr[tmpN-2]))        // 연속된 음수 앞이 숫자가 아닌 경우
                {
                    add(top,minus);

                    // 리턴 값을 안먹는다 ㅡ.ㅡ;
                    return false;
                }
                else
                {
                    *symbol = expr[*n-1]; // -10*----5 일경우 5를 리턴

                    if(*symbol == 's' || *symbol == 'c' || *symbol == 't')
                    {
                        InsertMinus();
                        add(top,times);
                        --*n;
                        return false;
                    }
                    else
                    {
                        --*n;
                        return operand;
                    }
                }
            }
        }
        ////////////////////////////////////////
        else if(expr[*n]=='(')
        {
            InsertMinus();
            add(top,times);
            ++*n;
            return false;
        }
        /////////////////////////////////////////
        else if((expr[*n]=='c' || expr[*n]=='t' || expr[*n]=='s') && !isdigit(expr[*n-2]))
        {
            InsertMinus();
            add(top,times);
            return false;
        }
        ///////////////////////////////////////
        else if(!isdigit(expr[*n-2]))        // 음수 앞이 숫자가 아닌경우
        {
            return operand;
        }
        //////////////////////////////////////
        else                                // 일반적인 빼기
        {
            return minus;
        }
    case '(':    return lparen;
    case ')':    return rparen;
    case '+':    return plus;
    case '/':    return divide;
    case '*':    return times;
    case '%':    return mod;
    case '\0':    return eos;
    default:

        if((is_tFunc=is_tfunc(symbol, n)))
            return is_tFunc;
        else
            return operand;
    }
}

int is_tfunc(char* symbol, int* n)
{
    if(*symbol == 's' && expr[*n] == 'i' && expr[*n+1] == 'n')
    {
        *n+=2;
        return _sin;
    }
    else if(*symbol == 'c' && expr[*n] == 'o' && expr[*n+1] == 's')
    {
        *n+=2;
        return _cos;
    }
    else if(*symbol == 't' && expr[*n] == 'a' && expr[*n+1] == 'n')
    {
        *n+=2;
        return _tan;
    }

    return false;
}

int is_tfunc2(char* symbol, int* n)
{
    if(*symbol == 's' && post_expr[*n] == 'i' && post_expr[*n+1] == 'n')
    {
        *n+=2;
        return _sin;
    }
    else if(*symbol == 'c' && post_expr[*n] == 'o' && post_expr[*n+1] == 's')
    {
        *n+=2;
        return _cos;
    }
    else if(*symbol == 't' && post_expr[*n] == 'a' && post_expr[*n+1] == 'n')
    {
        *n+=2;
        return _tan;
    }

    return false;
}


int get_token2(char* symbol, int* n)
{
    int is_tFunc = 0;
    *symbol = post_expr[(*n)++];

    if(*symbol == 'c' || *symbol == 's' || *symbol == 't')
        if((is_tFunc=is_tfunc2(symbol, n)))
            return is_tFunc;

    switch(*symbol)
    {
    case '-':
        if(*n==1)
            return operand;
        else if(isdigit(post_expr[*n]))
        {
            return minus_val;
        }
        else
        {
            return minus;
        }
    case '(':    return lparen;
    case ')':    return rparen;
    case '+':    return plus;
    case '/':    return divide;
    case '*':    return times;
    case '%':    return mod;
    case '\0':     return eos;
    case ' ':    return blank;
    default:     return operand;
    }
}

void print_token(int n)
{
    printf("%c ",put_token(n));
}

char put_token(int n)
{
    switch(n)
    {
    case minus:     return '-';
    case lparen:    return '(';
    case rparen:    return ')';
    case plus:     return '+';
    case divide:    return '/';
    case times:     return '*';
    case mod:     return '%';
    case eos:     return '\0';
    default:        return '\0';
    }
}

int rmstack(int* top)
{
    return stack[(*top)--];
}

double pop(int* top)
{
    return dbl_stack[(*top)--];
}

void add(int* top, int token)
{    
    stack[++*top] = token;
}

//
// overloading
//
void add(int* top, double token)
{
    dbl_stack[++*top] = token;
}

double eval(void)
{
    int token;
    char symbol;
    double op1, op2;
    int n = 0;
    int top = -1;
    char szLine[100];
    int cnt=0;

    while((token = get_token2(&symbol, &n)) != eos)
    {
        if(token == blank) continue;

        if(token == minus_val || token == operand)
        {
            szLine[cnt++] = symbol;
            while((token = get_token2(&symbol, &n)) != blank)
                szLine[cnt++] = symbol;
        
            szLine[cnt] = '\0';
            add(&top, atof(szLine));
            cnt = 0;
        }
        else if(token == _sin || token == _cos || token == _tan)
        {
            op1 = ConvertRadian(pop(&top));
                        
            switch(token)
            {
            case _sin: add(&top, sin(op1)); break;
            case _cos: add(&top, cos(op1)); break;
            case _tan: add(&top, tan(op1)); break;
            }        
        }
        else
        {
            op2 = pop(&top); // delete stack
            op1 = pop(&top);

            switch(token)
            {            
            case plus:
                add(&top, op1+op2);
                break;
            case minus :
                add(&top, op1 - op2);
                break;
            case times :
                add(&top, op1 * op2);
                break;
            case divide:
                add(&top, op1 / op2);
                break;
            case mod :
                add(&top, (int)op1 % (int)op2);
                break;
            }
        }
    }

    return pop(&top);    // return result
}

double ConvertRadian(double d)
{
    return (d*3.141592/180);
}