/*
last modification time:
3.25.2004
ver 2.0
*/

#include "conio.h"
#include "stdio.h"
#include "stdlib.h"
#include "math.h"

#define SIZE 100
#define ARRAYSIZE 10

int     checkexp(char*);
int     checkparen(char* cp1 );
char*    findpunc(char* cp1 );
int     checkpunc(char* cp1 );    
double    asc2num(char* cp1, char* cp2);
double    seperatewords( char* expression );
char*    finddot(char* cp1, char* cp2);
char*    findmatch(char* cp1 );
double    calculate(double expcp[ARRAYSIZE], char puncs[ARRAYSIZE], char punc );

main(){
    char expression[SIZE+1];
    double results=0;
    
    window(1,1,80,25 );
    textbackground(BLUE);
    textcolor(WHITE);
    clrscr();

    window(1,1,80,5);
    textbackground(WHITE);
    textcolor(BLACK);
    clrscr();
    gotoxy(1,1);
    
    cprintf("this programme can solve an expression including ( ) + - * / ^ ;\r\n" );
    cprintf("if you wana use minus, use(0-xxx) or (.-xxx) to take place of it;\r\n" );
    cprintf("please enter your expressions; no space is expected;\r\n" );
    cprintf("key in 'q' to quit the programme;" );
    
    window(1,7,80,8);
    textbackground(BLUE);
    textcolor(WHITE);

  while(1){
        while(bioskey(1)==0);
        clrscr();
        gotoxy(22,7 );
        gets(expression );
        /*check if quit;*/
        if( *expression=='q' ){
            break;
        }
        
        /*check if expression is legal;*/
        if( !checkexp(expression) ){
            continue;
        }
        
        /*recursion*/
        results=seperatewords(expression);
        gotoxy(20,8);
        cprintf("   the result is: %lf\r  ",results);
        gotoxy(22,5);
    }  /*end while*/
}

/*codes below used to seperate words and use recursion to
solve each expression in "(" and ")" ;*/
double seperatewords(char* expression ){
    double expcp[ARRAYSIZE];
    char puncs[ARRAYSIZE]="";

    char* cp1=expression;
    char* cp2=expression;
    int i=0;
    double* results=NULL;
    
    while( i<ARRAYSIZE ){
        cp2=findpunc(cp1);
        if(*cp2=='(' ){
            expcp[i]=seperatewords(cp2+1 );
            cp2=findmatch(cp2)+1;
        }else if(*cp2==')' ){
            expcp[i]=asc2num(cp1,cp2);
            puncs[i]='\0';
            results=&expcp[i];
            break;
        }else{
            expcp[i]=asc2num(cp1,cp2);
        }
        if(*cp2!='\0'){
            if(*cp2==')' ){
                results=&expcp[i];
                break;
            }
            /*store punctuations;*/
            puncs[i]=*cp2++;
            i++;
            cp1=cp2;
        }else{
            /*end of expression;*/
            puncs[i]='\0';
            results=&expcp[i];
            break;
        }
    }
    if (i>=ARRAYSIZE ) {
        cprintf("operands are too many...\nonly calculate the front %d operands\n", ARRAYSIZE);
        puncs[i-1]='\0';
    }
    calculate(expcp, puncs, '^' );
    /* '*' and '/' have the same PRI
    * so they are associative, so it doesn't matter which is calculated first;
    * '+' and '-' is the same;
    */
    calculate(expcp, puncs, '/' );
    calculate(expcp, puncs, '*' );

    calculate(expcp, puncs, '+' );
    calculate(expcp, puncs, '-' );
    return *results;
}
char* findpunc(char* cp1){
    while( (*cp1>='0'&&*cp1<='9') || *cp1=='.' ){
        cp1++;
    }
    return cp1;
}
char* findmatch(char* cp1 ){
    int i=0;
    while(1 ){
        if(*cp1=='(' ) i++;
        else if(*cp1==')' ){
            i--;
            if(i==0) break;
        }
        cp1++;
    }
    return cp1;
}
/*codes below used to convert nums;*/
double asc2num(char* cp1, char* cp2){
    double num=0;
    double power=10,powernum=1;
    char* cptemp=NULL;
    char* cprem=NULL;
    
    /*because cp2 points to a punc , -- to point to a number;*/
    cprem=cptemp=finddot( cp1, cp2 );
    /*if exist,digitals in front of dot;*/
    if(cprem>cp1){
        cptemp--;
        for(;cp1<=cptemp;cptemp--){
            num+=(*cptemp-'0')*powernum;
            powernum*=power;
        }
    }
    /*if exist,digitals behind the dot;*/
    if(cprem<cp2){
        cptemp=cprem;
        power=.1;
        powernum=.1;
        cptemp++;
        for(;cptemp<cp2;cptemp++){
            num+=(*cptemp-'0')*powernum;
            powernum*=power;
        }
    }
    return num;
}
char* finddot(char* cp1, char* cp2){
    while( *cp1!='.' && cp1<cp2 ){
        cp1++;
    }
    return cp1;
}

/*punc is the punc you wana to calcute with;*/
double calculate(double expcp[ARRAYSIZE], char puncs[ARRAYSIZE], char punc ){
    /*compute multiplying and dividing;*/
    double* p_operandl=NULL;
    double* p_operandr=NULL;
    double numtemp=0;
    int        i=0;
    int        j=0;
    char*    cp1=puncs;
    char*    cp2;
    while(*cp1!='\0'){
        i=j;
        p_operandl=&expcp[i];
        p_operandr=&expcp[i];
        cp2=cp1+1;
        if(*cp1!='x'){
            p_operandr=&expcp[++i];
            while(*cp2=='x'){
                cp2++;
                i++;
                p_operandr=&expcp[i];
            }

            if(*cp1==punc){
                if (punc=='^' ) {
                    numtemp=pow(*p_operandl, *p_operandr );
                }else if(punc=='/' ){
                    if(*p_operandr==0){
                        cprintf("sign divider's right operand must not be 0!\n");
                        numtemp=0;
                        break;
                    }
                    numtemp= *p_operandl / *p_operandr;
                }else if (punc=='*' ) {
                    numtemp= *p_operandl * *p_operandr;
                }else if (punc=='+' ) {
                    numtemp= *p_operandl + *p_operandr;
                }else if (punc=='-' ) {
                    numtemp= *p_operandl - *p_operandr;
                }
            *cp1='x';
            *p_operandr=numtemp;
            }
        }
        cp1++;
        j++;
    }/*end while*/
    return numtemp;
}


/*codes below used to check exps;*/
int checkexp(char* expression){
    /*check if expression is legal;*/
    char* cp1=expression;
    
    /*first character must be one of the below;*/
    if( (*cp1<'0'||*cp1>'9')&&*cp1!='.'&&*cp1!='(' ){
        cprintf("the first figer must be digital!\n");
        return 0;
    }
    if(!checkparen(expression) ){
        cprintf("wrong expression.please check parenthesis.\n" );
        return 0;
    }
    
    for(; *cp1!='\0'; cp1++ ){
        if( (*cp1<'0'||*cp1>'9')&&*cp1!='.' ){
            if( *cp1!='+' && *cp1!='-' && *cp1!='/' && *cp1!='*' &&
                *cp1!='(' && *cp1!=')' && *cp1!='^' ){
                cprintf( "error figers...\n" );
                return 0;
            }
            if( !checkpunc(cp1) ){
                cprintf( "there must be digitals on both side of the punctuation\n" );
                return 0;
            }
        }
    }
    return 1;
}
/*check punctuations, including + - * / ;*/
int checkpunc(char* cp1){
    /*check if there is digital at both side of punctuations;*/
    
    if(*cp1!='(' && *cp1!=')' ){
        if( (*(cp1-1)<'0'||*(cp1-1)>'9')&&*(cp1-1)!='.'&&*(cp1-1)!='('&&*(cp1-1)!=')' ||
            (*(cp1+1)<'0'||*(cp1+1)>'9')&&*(cp1+1)!='.'&&*(cp1+1)!='('&&*(cp1+1)!=')' ){
            return 0;
        }
    }
    return 1;
}
/*check parentheses. those are '(' and ')' ;*/
int checkparen(char* cp1 ){
    int i=0;
    char* cp2=cp1;
    
    /*make sure  '(' and ')' matches;and there's no ')' before '(' ;*/
    for(;*cp1!='\0';cp1++ ){
        if(*cp1=='(' ){
            i++;
        }
        else if(*cp1==')' ){
            i--;
        }
        if(i<0){
            return 0;
        }
    }
    if(i!=0) return 0;
    
    for(cp1=cp2;*cp1!='\0';cp1++ ){
        if(*cp1=='(' ){
            if( (*(cp1+1)<'0'||*(cp1+1)>'9')&&*(cp1+1)!='.'&&*(cp1+1)!='(' ) return 0;
            if(cp1==cp2 ) continue;
            if(*(cp1-1)!='+' && *(cp1-1)!='-' && *(cp1-1)!='^' &&
                *(cp1-1)!='*' && *(cp1-1)!='/'&&*(cp1-1)!='.'&&*(cp1-1)!='(' ) return 0;
        }
        else if(*cp1==')' ){
            if( (*(cp1-1)<'0'||*(cp1-1)>'9')&&*(cp1-1)!='.'&&*(cp1-1)!=')' ) return 0;
            if(*(cp1+1)=='\0' ) continue;
            if(*(cp1+1)!='+' && *(cp1+1)!='-' && *(cp1+1)!='^' &&
                *(cp1+1)!='*' && *(cp1+1)!='/'&&*(cp1+1)!=')' ) return 0;
        }
    }
    return 1;
}