Code:
#include <iostream>
#include <cmath>
#include <sstream>
using namespace std;
#define e 2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274f
#define pi 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f
#define e_string "(2.7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274)"
#define pi_string "(3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679)"
string math(const string & exp);
string ToString(long double v){
stringstream out;
out << v;
string res(out.str());
if (res == "inf") return "Infinity";
if (res == "-inf") return "-Infinity";
return res;
}
long double ToNumber(const string & s){
return atof(s.c_str());
}
long double Factorial(int n){
long double result = (long double)n;
if (n < 2){ //0! = 1
return 1.0f;
} else {
for (unsigned int value = n - 1; value > 1; --value){
result *= (long double)value;
}
}
return result;
}
long double logn(long double value, long double base){
return (log10(value) / log10(base));
}
long double hyper(long double a, long double x){
if (x <= -1.0f){
return (logn(hyper(x + 1.0f, a), a));
} else if (x > 0.0f){
return (pow(a, hyper(x - 1.0f, a)));
} else {
long double part1 = ((2.0f * log10(a)) / (1.0f + log10(a))) * x;
long double part2 = ((1.0f - log10(a)) / (1.0f + log10(a))) * (x * x);
return 1.0f + part1 - part2;
}
}
long double slog(long double z, long double b){
if (z <= 0.0f){
return slog(pow(b, z), b) - 1.0f;
} else if (1.0 < z){
return slog(logn(z, b), b) + 1.0f;
} else {
long double part1 = ((2.0f * log10(b)) / (1.0f + log10(b))) * z;
long double part2 = ((1.0f - log10(b)) / (1.0f + log10(b))) * (z * z);
return -1.0f + part1 + part2;
}
}
string DoubleNegatives(string & exp){
size_t pos = exp.find('-');
string res;
string funcs(")*+%/\\^-");
while (pos != string::npos){
if (pos){
res += exp.substr(0, pos);
}
char c = '*';
if (res.length()){
c = res[res.length() - 1];
}
if (funcs.find_first_of(c) != string::npos){ //negation
if (exp[pos + 1] == '-'){ //double negative
exp = exp.substr(pos + 2);
} else {
res += "-";
exp = exp.substr(pos + 1);
}
} else { //subtraction
res += "-";
exp = exp.substr(pos + 1);
}
pos = exp.find('-');
}
return res + exp;
}
size_t math_paren(const string & exp, size_t pos){
size_t pos2 = pos + 1;
int params = 0;
while(exp[pos2]){
switch(exp[pos2]){
case '(':
params++;
break;
case ')':
if (!params) return pos2;
params--;
break;
}
pos2++;
}
return string::npos;
}
void CheckForFunction(string & side, bool front){
size_t pos;
pos = (front) ? side.find_last_not_of(' ') : side.find_first_not_of(' ');
if (pos == string::npos) return;
char c = side[pos];
string funcs("<>%^*/+-hnsctglrdmaw");
if (funcs.find_first_of(c) != string::npos) return;
if (front){
side += "*";
} else {
side = string("*") + side;
}
}
void Split(const string & exp, long double & p1, long double & p2, long double default_second){
int parens = 0;
size_t pos = 0;
while(exp[pos]){
switch (exp[pos]){
case '(':
parens++;
break;
case ')':
parens--;
break;
case ',':
if (!parens){
p1 = ToNumber(math(exp.substr(0, pos)));
p2 = ToNumber(math(exp.substr(pos + 1)));
return;
}
break;
}
pos++;
}
p1 = ToNumber(math(exp));
p2 = default_second;
}
void math_constants(string & res){
string constants[] = {"Pi", "pi", "PI"};
for (unsigned int i = 0; i < 3; ++i){
size_t pos = 0;
while(true){
pos = res.find(constants[i], pos);
if (pos == string::npos) break;
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 2);
res = side1 + pi_string + side2;
pos += string(pi_string).length();
}
}
size_t pos = 0;
while(true){
pos = res.find('e', pos);
if (pos == string::npos) break;
bool e_constant = true;
// Wait! Could be scientific notation or a function
if (pos != 0){
char c = res[pos-1];
string digits("01234567890scp");
if (digits.find_first_of(c) != string::npos) e_constant = false;
}
if ((e_constant) && (pos != res.length() - 1)){
char c = res[pos-1];
string digits("01234567890-scp");
if (digits.find_first_of(c) != string::npos) e_constant = false;
}
if (e_constant){
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 1);
res = side1 + e_string + side2;
pos += string(pi_string).length();
} else {
pos++;
}
}
}
string math_operations_one(const string & exp){
string ress(exp);
ress = DoubleNegatives(ress);
string funcs[] = {
"asinh", "acosh", "atanh", "asech", "acsch" , "acoth",
"sinh" , "cosh" , "tanh" , "sech" , "csch" , "coth" ,
"asin" , "acos" , "atan2", "atan" , "asec" , "acsc" , "acot",
"sin" , "cos" , "tan" , "sec" , "csc" , "cot" ,
"logn" , "root" , "nPr" , "nCr" , "lambda", "hyper", "slog", "pow",
"ln" , "log" , "sqrt" , "abs" , "ceil" , "floor", "round",
"asum"};
size_t pos = ress.find('(');
size_t pos2 = math_paren(ress, pos);
if (pos2 == string::npos) return "nan";
string side1 = ress.substr(0, pos);
CheckForFunction(side1, true);
string side2 = ress.substr(pos2 + 1);
CheckForFunction(side2, false);
string inside = ress.substr(pos + 1, pos2 - pos - 1);
bool indegrees = true;
if (inside.length() > 3){
if (inside.substr(inside.length() - 3) == "rad"){
inside = inside.substr(0, inside.length() - 3);
indegrees = false;
}
}
inside = math(inside);
unsigned int function = 0xFFFF;
for (unsigned int i = 0; i < 41; ++i){
int ppos = pos - funcs[i].length();
if (ppos >= 0){
if (side1.substr(ppos) == funcs[i]){
function = i;
side1 = side1.substr(0, side1.length() - funcs[i].length());
break;
}
}
}
if (function == 0xFFFF){
return side1 + inside + side2;
}
long double value = ToNumber(inside);
long double degrees = (indegrees) ? value / 57.2957795f : value;
long double res;
switch (function){
/*
Inverse Hyperbolic Trig Functions
*/
case 0: //asinh
res = log(value + sqrt((value * value) + 1.0f));
return side1 + ToString(res) + side2;
case 1: //acosh
if (value > 1.0f) return side1 + "nan" + side2;
res = log(value + sqrt((value * value) - 1.0f));
return side1 + ToString(res) + side2;
case 2: //atanh
if (value <= 1.0f) return side1 + "nan" + side2;
res = 0.5f * log((1.0f + value) / (1.0f - value));
return side1 + ToString(res) + side2;
case 3: //asech
if ((value <= 0) || (value > 1)) return side1 + "nan" + side2;
res = log((1.0f + sqrt(1.0f - value * value)) / value);
return side1 + ToString(res) + side2;
case 4: //acsch
res = log((1.0f / value) + (sqrt(1.0f + value * value) / fabs(value)));
return side1 + ToString(res) + side2;
case 5: //acoth
if (fabs(value <= 1)) return side1 + "nan" + side2;
res = 0.5f * log((value + 1.0f) / (value - 1.0f));
return side1 + ToString(res) + side2;
/*
Hyperbolic Trig Functions
*/
case 6: //sinh
return side1 + ToString(sinh(degrees)) + side2;
case 7: //cosh
return side1 + ToString(cosh(degrees)) + side2;
case 8: //tanh
return side1 + ToString(tanh(degrees)) + side2;
case 9: //sech
return side1 + ToString(1.0f / cosh(degrees)) + side2;
case 10: //csch
return side1 + ToString(1.0f / sinh(degrees)) + side2;
case 11: //coth
return side1 + ToString(cosh(degrees) / sinh(degrees)) + side2;
/*
Inverse Trig Functions
*/
case 12: //asin
return side1 + ToString(asin(value)) + side2;
case 13: //acos
return side1 + ToString(acos(value)) + side2;
case 14: //atan2
long double p1, p2;
Split(inside, p1, p2, 1);
return side1 + ToString(atan2(p1, p2)) + side2;
case 15: //atan
return side1 + ToString(atan(value)) + side2;
case 16: //asec
return side1 + ToString(1.0f / acos(value)) + side2;
case 17: //acsc
return side1 + ToString(1.0f / asin(value)) + side2;
case 18: //atan
return side1 + ToString(acos(value) / asin(value)) + side2;
/*
Trig Functions
*/
case 19: //sin
return side1 + ToString(sin(degrees)) + side2;
case 20: //cos
return side1 + ToString(cos(degrees)) + side2;
case 21: //tan
return side1 + ToString(tan(degrees)) + side2;
case 22: //sec
return side1 + ToString(1.0f / cos(degrees)) + side2;
case 23: //csc
return side1 + ToString(1.0f / sin(degrees)) + side2;
case 24: //cot
return side1 + ToString(cos(degrees) / sin(degrees)) + side2;
/*
Two parameter functions
*/
case 25:{ //logn
long double p1, p2;
Split(inside, p1, p2, 1);
return side1 + ToString(logn(p1, p2)) + side2;}
case 26:{ //root
long double p1, p2;
Split(inside, p1, p2, 1);
return side1 + ToString(pow(p1, 1.0f / p2)) + side2;}
case 27:{ //nPr
long double p1, p2, res;
Split(inside, p1, p2, 1);
int n = (int)p1;
int r = (int)p2;
res = (Factorial(r) / Factorial(n - r));
return side1 + ToString(res) + side2;}
case 28:{ //nCr
long double p1, p2, res;
Split(inside, p1, p2, 1);
int n = (int)p1;
int r = (int)p2;
res = (Factorial(r) / (Factorial(r) * Factorial(n - r)));
return side1 + ToString(res) + side2;}
case 29:{ //lambda
long double x, b;
Split(inside, x, b, 1);
return side1 + ToString(pow(x, pow(x, b - 1))) + side2;}
case 30:{ //hyper
long double a, x, res;
Split(inside, a, x, 1);
return side1 + ToString(hyper(a, x)) + side2;}
case 31:{ //slog
long double z, b, res;
Split(inside, z, b, 1);
return side1 + ToString(slog(z, b)) + side2;}
case 32:{ //pow
long double a, x, res;
Split(inside, a, x, 1);
return side1 + ToString(pow(a, x)) + side2;}
/*
The rest
*/
case 33: //ln
return side1 + ToString(log(value)) + side2;
case 34: //log
return side1 + ToString(log10(value)) + side2;
case 35: //sqrt
return side1 + ToString(sqrt(value)) + side2;
case 36: //abs
return side1 + ToString(fabs(value)) + side2;
case 37: //ceil
return side1 + ToString(ceil(value)) + side2;
case 38: //floor
return side1 + ToString(floor(value)) + side2;
case 39: //round
return side1 + ToString((long double)((int)(value))) + side2;
case 40: //asum
int n = (int)value;
int res = 0;
if (n % 2) res += n--;
res += (n + 1) * (n >> 1);
return side1 + ToString((long double)res) + side2;
}
return side1 + inside + side2;
}
long double GetLastNumber(string & exp){
//this also strips it from the expression
size_t pos = exp.find_last_not_of(" 0123456789ABCDEF.xe"); //e is okay here as scientific notiation, because of the first pass, e can not be directly next to a number
if (pos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
string funcs("<>%^*/+");
if (exp[pos] == '-'){ //dammit, it this a negation or a subtraction
if (pos == 0){ //has to be a negation
long double res = ToNumber(exp);
exp = "";
return res;
}
if (exp[pos - 1] == 'e'){ //negative scientific notation
size_t nextpos = exp.find_last_not_of("0123456789.", pos - 2); //scientific notation can only have these characters
if (nextpos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
if (exp[nextpos] == '-'){ //dammit, it this a negation or a subtraction
if (nextpos == 0){ //has to be a negation
long double res = ToNumber(exp);
exp = "";
return res;
}
}
pos = nextpos;
}
int number_of_minuses = 1;
long double res = ToNumber(exp.substr(pos + 1));
string temp = exp.substr(0, pos);
size_t previous = temp.find_last_not_of(' ', pos - 2);
if (previous == string::npos){ //must be a negative
if (number_of_minuses % 2) res *= -1.0f;
exp = "";
return res;
}
char c = exp[previous];
if (funcs.find_first_of(c) != string::npos){ //there is another function before it, it must be negation
pos--;
}
} else if (exp[pos] == '+'){
if (exp[pos - 1] == 'e'){
size_t nextpos = exp.find_last_not_of("0123456789.", pos - 2); //scientific notation can only have these characters
if (nextpos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
pos = nextpos;
}
} else if (exp[pos] == 'y'){
exp = exp.substr(0, pos - 7);
pos = exp.find_last_not_of(' ');
if (pos == string::npos) return 1.0f/0.0f;
if (exp[pos] == '-'){ //negative infinity or subtraction?
if (pos == 0){ //has to be a negation
long double res = 1.0f/0.0f;
exp = "";
return res;
}
}
string temp = exp.substr(0, pos);
size_t previous = temp.find_last_not_of(' ');
if (previous == string::npos){ //must be a negative
long double res = -1.0f/0.0f;
exp = "";
return res;
}
char c = exp[previous];
if (funcs.find_first_of(c) != string::npos){ //there is another function before it, it must be negation
long double res = -1.0f/0.0f;
exp = "";
return res;
}
return 1.0f/0.0f;
} else if ((exp[pos] == 'n') && (exp[pos - 1] == 'a') && (exp[pos - 2] == 'n')){
exp = exp.substr(0, pos - 2);
return sqrt(-1);
}
string num = exp.substr(pos + 1);
cout << num << endl;
exp = exp.substr(0, pos + 1);
return ToNumber(num);
}
long double GetFirstNumber(string & exp){
//this also strips it from the expression
size_t pos = exp.find_first_not_of(" 0123456789ABCDEF.xe"); //e is okay here as scientific notiation, because of the first pass, e can not be directly next to a number
if (pos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
string funcs("<>%^*/+-");
if (exp[pos] == '-'){ //dammit, it this a negation or a subtraction
if (exp[pos + 1] == 'I'){ //-Infinity
exp = exp.substr(pos + 9);
return -1.0f/0.0f;
}
if (pos == 0){ //has to be a negation
long double res = ToNumber(exp);
exp = "";
return res;
}
if (exp[pos - 1] == 'e'){ //negative scientific notation
size_t nextpos = exp.find_first_not_of("0123456789.", pos + 1); //scientific notation can only have these characters
if (nextpos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
pos = nextpos;
}
string temp = exp.substr(pos);
size_t next = temp.find_first_not_of(' ');
if (next == string::npos){ //must be a negative
long double res = ToNumber(exp.substr(pos));
exp = "";
return res;
}
char c = exp[next];
if (funcs.find_first_of(c) != string::npos){ //there is another function before it, it must be negation
pos--;
}
} else if (exp[pos] == '+'){
if (exp[pos - 1] == 'e'){
size_t nextpos = exp.find_first_not_of("0123456789.", pos + 1); //scientific notation can only have these characters
if (nextpos == string::npos){
long double res = ToNumber(exp);
exp = "";
return res;
}
pos = nextpos;
}
} else if (exp[pos] == 'I'){ //Infinity
exp = exp.substr(pos + 8);
return 1.0f/0.0f;
} else if ((exp[pos] == 'n') && (exp[pos + 1] == 'a') && (exp[pos + 2] == 'n')){
exp = exp.substr(pos + 3);
return sqrt(-1);
}
string num = exp.substr(0, pos);
exp = exp.substr(pos);
return ToNumber(num);
}
string math_operations_two(const string & exp){
/*
! (factorial)
*/
string res(exp);
res = DoubleNegatives(res);
size_t pos = res.find('!');
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 1);
int result = (int)GetLastNumber(side1);
/*
GetLastNumber will include a negation if there is one, but negation has lower order precedence
than factorial, so I need to strip it and readd it later.
*/
if (result < 0){ //strip the negative and put it on side1
side1 += "-";
result *= -1;
}
return side1 + ToString(Factorial(result)) + side2;
}
string math_operations_three(const string & exp){
/*
*
/
^
\
*/
string res(exp);
res = DoubleNegatives(res);
size_t pos = res.find_first_of("*/^\\");
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 1);
switch(res[pos]){
case '*':{
long double aa = GetLastNumber(side1);
long double bb = GetFirstNumber(side2);
return side1 + ToString(aa * bb) + side2;}
case '/':{
long double aa = GetLastNumber(side1);
long double bb = GetFirstNumber(side2);
return side1 + ToString(aa / bb) + side2;}
case '\\':{
int aa = (int)GetLastNumber(side1);
int bb = (int)GetFirstNumber(side2);
return side1 + ToString((long double)(aa / bb)) + side2;}
case '^':{
long double aa = GetLastNumber(side1);
long double bb = GetFirstNumber(side2);
return side1 + ToString(pow(aa, bb)) + side2;}
}
return res;
}
string math_operations_four(const string & exp, bool & true_pos){
/*
+
-
% : modulus
*/
string res(exp);
res = DoubleNegatives(res);
size_t pos = res.find_first_of("+-%");
/*
Check for scientific notation and negations
*/
if ((res[pos] == '+') && (res[pos - 1] == 'e')){
pos = res.find_first_of("+-%", pos + 1);
if (pos == string::npos){
true_pos = false;
return res;
}
}
if (res[pos] == '-'){
if (pos == 0){ //obviously a negation
pos = res.find_first_of("+-%", 1);
if (pos == string::npos){
true_pos = false;
return res;
}
}
if (res[pos - 1] == 'e'){
pos = res.find_first_of("+-%", pos + 1);
if (pos == string::npos){
true_pos = false;
return res;
}
}
}
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 1);
switch(res[pos]){
case '+':{
long double aa = GetLastNumber(side1);
long double bb = GetFirstNumber(side2);
return side1 + ToString(aa + bb) + side2;}
case '-':{
long double aa = GetLastNumber(side1);
long double bb = GetFirstNumber(side2);
return side1 + ToString(aa - bb) + side2;}
case '%':{
int aa = (int)GetLastNumber(side1);
int bb = (int)GetFirstNumber(side2);
return side1 + ToString((long double)(aa % bb)) + side2;}
}
return res;
}
string math_operations_five(const string & exp){
/*
<<
>>
*/
string res(exp);
res = DoubleNegatives(res);
size_t pos = res.find("<<");
if (pos == string::npos) pos = res.find(">>");
string side1 = res.substr(0, pos);
string side2 = res.substr(pos + 2);
switch(res[pos]){
case '<':{
int aa = (int)GetLastNumber(side1);
int bb = (int)GetFirstNumber(side2);
return side1 + ToString((long double)(aa << bb)) + side2;}
case '>':{
int aa = (int)GetLastNumber(side1);
int bb = (int)GetFirstNumber(side2);
return side1 + ToString((long double)(aa >> bb)) + side2;}
}
return res;
}
string StripWhiteSpace(const string & exp){
stringstream out;
size_t pos = 0;
char c = exp[pos++];
while(c){
if (c != ' ') out << c;
c = exp[pos++];
}
return out.str();
}
string math(const string & exp){
string res(exp);
res = StripWhiteSpace(res);
math_constants(res);
while(res.find('(') != string::npos) res = math_operations_one(res);
while(res.find('!') != string::npos) res = math_operations_two(res);
while(res.find_first_of("*/^\\") != string::npos) res = math_operations_three(res);
bool true_pos = true;
while((res.find_first_of("+-%") != string::npos) && (true_pos)) res = math_operations_four(res, true_pos);
while((res.find("<<") != string::npos) || (res.find(">>") != string::npos)) res = math_operations_five(res);
return res;
}
#define Check(func, ans);\
cout << ((math(func) == #ans) ? "Passed" : string("Failed: \"") + func + "\" != " + #ans + ", it's " + math(func)) << endl;
int main (int argc, char * const argv[]) {
// insert code here...
Check("2(4(2) + 1)" , 18);
Check("sqrt(52 * (50 + 2))" , 52);
Check("1+1 << 4" , 32);
Check("abs(70 - (ceil(2.2) * 25))" , 5);
Check("1e+2 + 1e2 - 10e-1" , 199);
Check("4.5/2 + ln(e)" , 3.25);
Check("4.5\\2" , 2);
Check("24%5" , 4);
Check("5 + -(4/2)" , 3);
Check("1/0" , Infinity);
Check("Pi + -Infinity" , -Infinity);
Check("12 + nan * 25" , nan);
Check("15 * sqrt(-1)" , nan);
Check("asum(1e+2)" , 5050);
Check("--1" , 1);
Check("-sqrt(144)" , -12);
return 0;
}
Bookmarks