21 December 2013

[C++][Project] Calculator + Automatic Syntax Checking + Complex Analysis


This is my first ever C++ coding project.The main motivation behind it was a Casio scientific calculator I once owned and I had a hard time figuring out the syntax.

I have only 2 year experience studying Borland C++ in school.So please don't go too technical in comments. This is a genuine piece of work and no code has been borrowed from anywhere.I am open to suggestions and you are free to use the code in any way you please.


Overview:

This is a scientific calculator with support for complex analysis.(support calculations and functions of complex numbers)
It automatically check for syntax errors.You can only enter valid syntax.
The program is written entirely using basic input and output manipulation techniques and inbuilt math functions.Thus it has limitations when it comes to precision and possible logical workarounds are implemented wherever possible.

Changelog:

V4.5
+Rewrote in GCC compiler

V4.4
+Very tiny Bugfix

V4.3
+Very tiny bugfix

V4.2
+Removed Gamma function for computing factorial.
+Bugfix: Inverse Cos.
+Error handling: 0th root.

V4.1
+Fixed approximation bug in complex power function.
+Changed the way evaluation code handle 'i' and '-'.

V4.0
+Optimised evaluation technique(Functions are evaluated first).
+Minor Tweaks in Syntax checking.
+Optimised code converting string to long double.
+Bugfix in complex log function.
+Bugfix: i^x bug.
+Tweak: Precise ambiguity.
+Cleaned up the code.

V3.7
+Temporary bugfix for i^x bug.

V3.6
+Complex Math By GenX introduced.
+Support for complex functions.
+Redid evaluation code.

V3.5
+Added support for complex numbers.
+ -,+,*,/ operations on complex numbers supported.
+Tweaks in syntax checking
+Tweaks in evaluation

V3.2
+Redid a major part of the program to remove bugs.
+ √ function added

V3.1
+Added support for inverse functions.
+Tweaks in syntax checking.
+Tweaks in evaluation.

V3.0
+Added support for functions: cos, sin, tan, log .....
+Tweaks in syntax checking.
+Tweaks in evaluation.

V2.9
+Bugfix: Backspace handling in syntax checking.

V2.8
+Added Help
+Syntax checking calls help after a number of invalid entries.

V2.7
+Added nCr , nPr and factorial operations.
+Tweaks in syntax checking.
+Tweaks in evaluation.

V2.6
+Compiled as independent program.
+Bugfix in syntax checking.

V2.5 and earlier
+The program was part of X Shell a dos like command prompt written in C++.
+Supports only +,-,/,*,(,) operators.



Download Exe x64 (V4.5)  

Code(V4.5):

#include<iostream>
#include<conio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<stdio.h>
#include<ctype.h>
#define trig(x) ch[x]=='s'||ch[x]=='c'||ch[x]=='t'||ch[x]=='L'||ch[x]=='l'||ch[x]=='e'
#define reint po[co].strt=-1;po[co].ende=-1;po[co].dot=-1
#define digitl if(isdigit(ch[i-1]))po[co++].ende=i-1;reint
#define less(x) no=j;while(no<(strlen(eval)-x)){eval[no]=eval[no+x];no++;}eval[no]='\0';bra2-=x
#define s(x) sum[eval[x]-'a']

using namespace std;
//***********************************************************************
//*************************COMPLEX MATH BY GENX**************************
class comp
{public:
  double x,y;
void COUT()
{ if(y==0)cout<<x;
  else if(x==0&&y>0)cout<<"i"<<y;
  else if(x==0&&y<0)cout<<"-i"<<-y;
  else if(y>0)cout<<x<<" + i"<<y;
  else cout<<x<<" - i"<<-y;
}
void ADD(comp a,comp b)
{ x=a.x+b.x;y=a.y+b.y;
}
void SUB(comp a,comp b)
{ x=a.x-b.x;y=a.y-b.y;
}
void MULT(comp a,comp b)
{ x=a.x*b.x-a.y*b.y;
  y=a.y*b.x+b.y*a.x;
 }
void DIVI(comp a,comp b)
{ if(!b.ABS()){cout<<"Err: Div By 0";errno=33;}
  x=(a.x*b.x+a.y*b.y)/(b.x*b.x+b.y*b.y);
  y=(a.y*b.x-b.y*a.x)/(b.x*b.x+b.y*b.y);
}
void ASIN(comp a)
{ if(a.y==0&&a.x>=-1&&a.x<=1){x=asinl(a.x);return;}
  POW(comp(((1+a.y*a.y)-(a.x*a.x)),-2.0*a.y*a.x),comp(0.5,0));
  x-=a.y;y+=a.x;LOG(*this);y-=x;x+=y;y-=x;
}
void ACOS(comp a)
{ if(a.y==0&&a.x>=-1&&a.x<=1){x=acosl(a.x);return;}
  POW(comp(a.y*a.y+1-a.x*a.x,-2*a.y*a.x),comp(0.5,0));
  x-=y;y+=x;x-=y;x+=a.x;y+=a.y;
  LOG(*this);y-=x;x+=y;y-=x;
}
void ATAN(comp a)
{ if(a.y==0){x=atanl(a.x);return;}
  y=a.y*a.y;x=y+a.x*a.x+1;
  LOG(comp((2-x)/(x-2*a.y),(-2*a.x)/(x-2*a.y)));
  x/=2;y/=2;x-=y;y+=x;x-=y;
}
void SIN(comp a)
{ if(a.y==0){x=sinl(a.x);y=0;}
  else{
   x=sinl(a.x)*coshl(a.y);
   y=sinhl(a.y)*cosl(a.x);
      }
}
void COS(comp a)
{ if(a.y==0){x=cosl(a.x);y=0;}
  else{
   x=cosl(a.x)*coshl(a.y);
   y=-(sinhl(a.y)*sinl(a.x));
      }
}
void TAN(comp a)
{ if(a.y==0){x=tanl(a.x);y=0;}
  else{
   double b=cosl(2*a.x)+coshl(2*a.y);
   x=sinl(2*a.x)/b;y=sinhl(2*a.y)/b;
      }
}
void LOG(comp a)
{ if(a.y==0&&a.x>0){x=logl(a.x);y=0;}
  else{x=logl(a.ABS());y=a.ARG();}
}
void LOG10(comp a)
{ if(a.y==0&&a.x>0){x=log10l(a.x);y=0;}
  else{
   x=logl(a.ABS())*0.4342944819032518277;
   y=a.ARG()*0.4342944819032518277;
      }
}
void EXP(comp a)
{ if(a.y==0){x=exp(a.x);y=0;}
  else{
   a.x=expl(a.x);
   x=a.x*cosl(a.y);y=a.x*sinl(a.y);
      }
}
void POW(comp a,comp b)
{  if(a.x==0&&a.y==0)
    if(b.x==0&&b.y==0){cout<<"Invalid";errno=33;}
    else {x=0;y=0;}
  else if(a.y==0&&b.y==0&&!(a.x<0&&floorl(b.x)!=b.x))
  {x=powl(a.x,b.x);y=0;}
  else
  {double c=0,d=0;
   c=a.ABS(),d=a.ARG();
  a.x=b.x*d+b.y*logl(c);
  a.y=expl(b.x*logl(c)-b.y*d);
  c=cosl(a.x);d=sinl(a.x);
  if(fabsl(c)<0.000000000000001)x=0;  //workaround for precsicion
  else x=a.y*c;
  if(fabsl(d)<0.0000000000001)y=0;  //eg:i^100 does not display 1}
  else y=a.y*d;
  }
}
double ABS()
 {return sqrtl(x*x+y*y);}
double ARG()
 {double b;
  if(x==0&&y==0)b=0;
  else if(x==0&&y>0)b=M_PI_2;
  else if(x==0&&y<0)b=-M_PI_2;
  else {b=atanl(y/x);if(x<0&&y<0)b-=M_PI;
  else if(x<0&&y>=0)b+=M_PI;}return b;}
void FACT(comp n,comp r,short int c);
comp(double a=0,double b=0)
{x=a;y=b;}
};
void comp::FACT(comp a,comp b,short int c)
 {if(a.y!=0||b.y!=0||a.x<0||b.x<0||floorl(a.x)!=a.x||floorl(b.x)!=b.x)
  {cout<<"Factorial Error";errno=33;return;}
  switch(c)
 {case 1:if(a.x<b.x){x=0;break;}
  a.y=a.x,b.y=b.x;x=1;
  for(;b.y>0;a.y=a.y-1,b.y=b.y-1)  //nPr
  x*=a.y;a.y=b.y=0;break;
  case 2:if(a.x<b.x){x=0;break;}
  a.y=a.x,b.y=b.x;x=1;
  if(a.y-b.y<b.y)b.y=a.y-b.y;
  for(;b.y>=1;a.y=a.y-1,b.y=b.y-1) //nCr
  x*=a.y/b.y;a.y=b.y=0;break;
case 3:if(a.x==0||a.x==1){x=1;break;}  //!
       for(x=1;a.x>=2;a.x--)
       x*=a.x;
       break;
default:cout<<"Fact Engine Error";errno=33;}
 }
//*************************COMPLEX MATH BY GENX*************************
//***********************************************************************

class calculator
{private: struct marker
 {short int strt,ende,dot;
 };
 char set[9];
 marker po[100];
 comp sum[100];
 char ch[10000],eval[300];
 int co,bra1,bra2,hlp;


 //co - count numbers

public:calculator(){strcpy(set,"!PCû^/*+-");}
int calc();
double conv(int n);
void output();
void calchelp(int);
}calc;


//******************CONVERSION-STRING TO DOUBLE****************************

double calculator::conv(int n)
{
 int i=0;
 double sum=0.0;
 if(po[n].strt!=-1)
 {   while(po[n].strt!=po[n].dot&&po[n].strt<=po[n].ende)
  {   i=((ch[po[n].strt++])-'0');
      sum*=10;sum+=i;
  }
}
 if(po[n].dot!=-1)
 for(i=1;i<=(po[n].ende-po[n].dot);i++)           //i<20 double precision limit
 {sum+=((ch[po[n].dot+i]-48))*powl(10,-i);}  //decimal part conv
 return(sum);
}
//******************CONVERSION-STRING TO DOUBLE****************************


//******************************HELP**************************************

void calculator::calchelp(int typ=0)
{
if(typ==0)cout<<"----------------Welocme To Calculator-----------------\n";
if(typ==0){cout<<"This is a tool for complex analysis\n";
cout<<"It allows seamless calculation with automatic syntax checking.\n";
cout<<"This is still in development phase.\n";
cout<<"\nKnown Bugs:\nMemory Wastage\n";
cout<<"Press any key to continue.\n";getch();
}
cout<<"\nValid operators:\n";
cout<<" s   - Sin()\n";
cout<<" c   - Cos()\n";
cout<<" t   - Tan()\n";
cout<<" L   - Log()\n";
cout<<" l   - Ln()\n";
cout<<" I   - Inverse. eg:Is => Inverse sin()\n";
cout<<" P   - Permutation. eg:3P2=6\n";
cout<<" C   - Combination. eg:3C2=3\n";
cout<<" R   - X"<<(char)251<<"Y. eg:2"<<(char)251<<"9=3\n";
cout<<" ^   - Power\n";
cout<<" !   - Factorial\n";
cout<<" /   - Division\n";
cout<<" *   - Multiplication\n";
cout<<" +   - Addition\n";
cout<<" -   - Subtraction\n";
cout<<" x   - To exit\n";
if(typ==1)cout<<"\n\n";
}
//******************************HELP**************************************

int main()
{calc.calc();}

//*************************COMPLEX ENGINE V3.1****************************
//*************************                   ****************************

int calculator::calc()
{
cout<<"\n\n---------Calculator (Beta Version)----------\n";
calchelp();int i;
again:
co=bra1=bra2=hlp=0;reint;
cout<<endl<<"Calc:";
for(i=0; ;i++)
{  ch[i]=getch();


//**************************SYNTAXER*************************************

if((ch[i]=='+'||ch[i]=='*'||ch[i]=='/'||ch[i]=='^')&&
  (i!=0&&ch[i-1]!='+'&&ch[i-1]!='*'&&ch[i-1]!='/'&&ch[i-1]!='^'&&ch[i-1]!='('&&ch[i-1]!='.'&&ch[i-1]!='I'))
  {digitl;cout<<ch[i];}

else if(ch[i]=='.'&&po[co].dot==-1)                                    //dot
    {cout<<ch[i];po[co].dot=i;}

else if(isdigit(ch[i])&&ch[i-1]!='!'&&ch[i-1]!='I')                   //digits
    {if(po[co].strt==-1&&po[co].dot==-1)
     {po[co].strt=i;}
     cout<<ch[i];
    }

else if((ch[i]=='!')&&
  (ch[i-1]==')'||ch[i-1]=='i'||isdigit(ch[i-1])))
  {digitl;cout<<'!';}

else if((ch[i]=='P'||ch[i]=='C')&&
  (ch[i-1]==')'||ch[i]=='!'||isdigit(ch[i-1])))
  {digitl;cout<<ch[i];}


else if((ch[i]=='R')&&
  (ch[i-1]!='I'&&ch[i-1]!='.'))
  {digitl;ch[i]='û';cout<<'û';}


else if((ch[i]=='i')&&
  (ch[i-1]!='.'&&ch[i-1]!='P'&&ch[i-1]!='C'&&ch[i-1]!='I'&&ch[i-1]!='i'))
  {digitl;cout<<'i';}

else if((ch[i]=='-')&&
  (ch[i-1]!='-'&&ch[i-1]!='I'&&ch[i-1]!='P'&&ch[i-1]!='C'&&ch[i-1]!='.'))
  {digitl;cout<<'-';}

else if((ch[i]=='(')&&
  (ch[i-1]!='I'))
  {cout<<'(';bra1++;digitl;}

else if((ch[i]==')')&&(bra1>bra2)&&
  (ch[i-1]=='!'||ch[i-1]=='i'||isdigit(ch[i-1])||ch[i-1]==')'))
  {digitl;cout<<')';bra2++;}

else if((ch[i]=='='||ch[i]==13)&&(bra1==bra2)&&
     (ch[i-1]=='!'||ch[i-1]=='i'||isdigit(ch[i-1])||ch[i-1]==')'))
  {digitl;cout<<'=';ch[i]='\0';break;}

 //trig log inv trig
else if((trig(i)||(ch[i]=='I'&&ch[i-1]!='I'))
&&!isdigit(ch[i-1])&&ch[i-1]!='!'&&ch[i-1]!=')')
     {switch(ch[i])
      {case 'l':cout<<"Ln (";break;
       case 'L':cout<<"Log(";break;
       case 's':cout<<"Sin(";break;
       case 'c':cout<<"Cos(";break;
       case 't':cout<<"Tan(";break;
       case 'e':cout<<"Exp(";break;
       case 'I':cout<<"Inv:";break;
      }
//exception eg
      if(ch[i]!='I'){ch[++i]='(';bra1++;}
     }

else if(ch[i]=='x'||ch[i]=='X')     //exit
   {
   cout<<"\nPress 'X' Again To Exit\n";
   ch[i]=getch();
   if(ch[i]=='x'||ch[i]=='X')exit(0);
   goto reprint;
  }

else if(ch[i]==8&&i!=0)                                         //Backspace
   {
   if(i-2==po[co-1].ende&&co!=0){co--;}
    else if(i-1==po[co].strt){po[co].strt=-1;}
    else if(i-1==po[co].dot)po[co].dot=-1;
    else if(trig(i-2)){cout<<"\b \b"<<"\b \b"<<"\b \b";i--;bra1--;}
    else if(ch[i-1]=='I'){cout<<"\b \b"<<"\b \b"<<"\b \b";}
    else if(ch[i-1]==')'){bra2--;}
    else if(ch[i-1]=='('){bra1--;}
    cout<<"\b \b";i-=2;
   }

else                                                      //Help & error
   {if(++hlp==25)
    {calchelp(1);
reprint:
     cout<<"Calc:";
     for(hlp=0;hlp<i;hlp++)
     if(trig(hlp)||ch[hlp]=='I')
      switch(ch[hlp])
       {case 'l':cout<<"Ln (";break;
case 'L':cout<<"Log(";break;
case 's':cout<<"Sin(";break;
case 'c':cout<<"Cos(";break;
case 't':cout<<"Tan(";break;
case 'e':cout<<"Exp(";break;
case 'I':cout<<"Inv:";break;
       }
    else cout<<ch[hlp];
     hlp=0;
   }i--;
  }
}
//**************************SYNTAXER*************************************


//***************************PARSER**************************************

co=0;
int j=0,cou=0,no=0;
for(;j<=i;j++)  //'no' count sum & 'cou' count eval
      //'i' from previous for loop
{
if(ch[j]=='P'||ch[j]=='C'||ch[j]=='û'||ch[j]=='+'||ch[j]=='-'||ch[j]=='*'
  ||ch[j]=='/'||ch[j]=='^'||ch[j]==')'||ch[j]=='('||ch[j]=='!')
   eval[cou++]=ch[j];

else if(ch[j]=='i')
      {sum[co].y=1;sum[co].x=0;
      eval[cou++]=co+++97;
      }

else if(trig(j))
 {eval[cou++]='F';
  if(ch[j-1]=='I'&&j!=0)
    eval[cou++]='I';
  eval[cou++]=ch[j];
  }

 else if(j==po[no].ende)
  {sum[co].x=conv(no);sum[co].y=0;
   eval[cou++]=co+++97;
   no++;
  }

}eval[cou]='\0';
//***************************PARSER**************************************


//***************************EVALUATER************************************

cou=strlen(set);      //optimise speed
bracket:
bra2=strlen(eval);bra1=-1;
for(i=bra2-1;i>=0;i--)
 {if(eval[i]==')'){bra2=i;}
  else if(eval[i]=='('){bra1=i;break;}
 }

if(bra2-bra1==2)
 {if(bra2==1)
 {s(0).COUT();                            //output
  goto again;}
  j=bra1;
 eval[j]=eval[j+1];j++;less(2);goto bracket;}

for(j=bra1+1;j<bra2;j++)
   if(eval[j]=='F')
     {less(1);
      if(eval[j]=='I')
       {less(1);
    switch(eval[j])
    {case 'l':s(j+1).EXP(s(j+1));less(1);break;
     case 'L':s(j+1).POW(comp (10,0),s(j+1));less(1);break;
     case 'e':s(j+1).LOG(s(j+1));less(1);break;
     case 's':s(j+1).ASIN(s(j+1));less(1);break;
     case 'c':s(j+1).ACOS(s(j+1));less(1);break;
     case 't':s(j+1).ATAN(s(j+1));less(1);break;
     default:cout<<"Inverse Function Error";errno=1;
    }
      }
      else
     switch(eval[j])
    {case 'e':s(j+1).EXP(s(j+1));less(1);break;
     case 'l':s(j+1).LOG(s(j+1));less(1);break;
     case 'L':s(j+1).LOG10(s(j+1));less(1);break;
     case 's':s(j+1).SIN(s(j+1));less(1);break;
     case 'c':s(j+1).COS(s(j+1));less(1);break;
     case 't':s(j+1).TAN(s(j+1));less(1);break;
     default:cout<<"Function Error";errno=1;
    }
     }

for(i=0;i<cou;i++)
 {
  for(j=bra2-1;j>bra1;j--)
  {                           //û = -5 = 251
  if(i==3)                    //when to check for lose operator eg:10^-2
  if(eval[j]>=97&&eval[j+1]>97)      //workaround for ab=a*b
  {
      s(j+1).MULT(s(j),s(j+1));less(1);continue;}    //eg:3i=>ab a=3 b=1i   =>a=3i
   else if(eval[j]=='-'&&eval[j-1]<97&&eval[j+1]>=97)  //workaround 10^-2
   {s(j+1).x=-s(j+1).x;s(j+1).y=-s(j+1).y;less(1);continue;}

   if(eval[j]==set[i])
      switch(set[i])
      {case 'P':s(j-1).FACT(s(j-1),s(j+1),1);
  less(2);break;
       case 'C':s(j-1).FACT(s(j-1),s(j+1),2);
  less(2);break;
       case '!':s(j-1).FACT(s(j-1),0,3);
  less(1);break;
       case 'û':if(eval[j-1]<97||j==0){s(j+1).POW(s(j+1),comp(0.5,0));less(1);}
else if(!s(j-1).ABS()){cout<<"Err: 0th Root";errno=33;}
else{s(j-1).DIVI(comp(1,0),s(j-1));
s(j-1).POW(s(j+1),s(j-1));less(2);}break;
       case '^':s(j-1).POW(s(j-1),s(j+1));less(2);break;
       case '/':s(j-1).DIVI(s(j-1),s(j+1));less(2);break;
       case '*':s(j-1).MULT(s(j-1),s(j+1));less(2);break;
       case '+':if(eval[j-1]>=97){s(j-1).ADD(s(j-1),s(j+1));less(2);break;}
else {less(1);break;}
       case '-':if(eval[j-1]>=97){s(j-1).SUB(s(j-1),s(j+1));less(2);break;}
else {s(j+1).MULT(s(j+1),comp(-1,0));less(1);break;}
   }
   }

       if(errno){errno=0;goto again;}

 if(bra2-bra1==2)
 {if(bra2==1)
 {s(0).COUT();                            //output
 goto again;}
 eval[j]=eval[j+1];j++;less(2);goto bracket;}
 }

 cout<<"Unexpected Error!\a!\a!";
 goto again;
}

//******************************EVALUATER*********************************
//*************************                   ****************************
//*************************COMPLEX ENGINE V3.1****************************





Known Bugs:
-long double not working properly in GCC
-Exe not working properly when placed in desktop directory in windows 8.1 x64

Please Report Bugs(Errors) through comments.

4 comments:

  1. There are bugs in syntax checking i can type '^^^^^' without any numbers

    ReplyDelete
  2. i3C2 give factorial error. nice idea btw. loads of unused variables

    ReplyDelete