• Lucid Dreaming - Dream Views




    Results 1 to 16 of 16
    1. #1
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935

      Want to QA some C++ code?

      I wrote a library for a function that I needed in my company's scripting language. It's complex and 700 lines of code, so I'd like to have someone other than myself check it for bugs. Just give the math() function various expressions and see if the results come out right. The main function is the last one at the bottom. A list of functions is available here: Math Functions

      That page says that it supports hex values, but the number converter for this one doesn't, so don't use them

      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;
      }
      Last edited by ninja9578; 08-10-2009 at 01:26 PM.

    2. #2
      Rational Spiritualist DrunkenArse's Avatar
      Join Date
      May 2009
      Gender
      Location
      Da Aina
      Posts
      2,941
      Likes
      1092
      Christ! what does your company do again? 10 digits for a numerical constant is excessive in most cases.....

      I'll take a look at it.
      Previously PhilosopherStoned

    3. #3
      Member Achievements:
      1 year registered Veteran First Class 5000 Hall Points

      Join Date
      Sep 2004
      Gender
      Location
      Seattle, WA
      Posts
      2,503
      Likes
      217
      Hey! I didn't enter test values (yet) but skimmed it, and wanted to make some constructive suggestions based on random stuff that caught my eye:

      1) Instead of ToString/ToNumber, consider using boost::lexical_cast

      2) Factorial should probably be returning an unsigned long long. Are you returning a double just because you want to work out all solutions in floating point and not have to worry about it?

      3) The giant switch - You might want to just define an enum for your operations and use it instead of hardcoding magic numbers

      4) This line bothers me: return -1.0f/0.0f; Might want to consider modeling infinity symbolically, unless you're doing something specifically with the C implementation (in which case I think you can just return that directly). Also, I'm not 100% sure if 0.0f is guaranteed to be non-negative, which might mess up results in peculiar ways.

      5) Same kind of thing for the sqrt(-1) - what does that even return? Are complex numbers supported, or is that an exceptional case (requiring an exception to be thrown)?

      6) One random comment: If this was for your own learning, then wonderful! If it was for work, I highly recommend you look into a BNF parser to do the dirty work for you, such as Spirit. It will pretty much eliminate the inevitable bugs in the parsing logic, and force you to fully define your grammar/semantics up front. (actually, I think you should look into this anyway, cause it's cool stuff no matter what hehe)

      Nice work on actually getting that much code out there! There's tons of functionality, and it's a non-trivial problem!

    4. #4
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      Quote Originally Posted by Replicon View Post
      1) Instead of ToString/ToNumber, consider using boost::lexical_cast
      I have real tonumber and tostring functions in another library, i just wrote quick ones to test. mine include things like hex support and various other odd sontants.

      2) Factorial should probably be returning an unsigned long long. Are you returning a double just because you want to work out all solutions in floating point and not have to worry about it?
      I don't want to worry about it

      3) The giant switch - You might want to just define an enum for your operations and use it instead of hardcoding magic numbers
      Thought about it, but wrote the whole thing in 3 hours so didn't think it through as well as I should have.

      4) This line bothers me: return -1.0f/0.0f; Might want to consider modeling infinity symbolically, unless you're doing something specifically with the C implementation (in which case I think you can just return that directly). Also, I'm not 100% sure if 0.0f is guaranteed to be non-negative, which might mess up results in peculiar ways.

      5) Same kind of thing for the sqrt(-1) - what does that even return? Are complex numbers supported, or is that an exceptional case (requiring an exception to be thrown)?
      I have an infinity constant in my real program, will change it to that in implementation. sqrt(-1) returns nan, not an exception in the C99 definition.

      6) One random comment: If this was for your own learning, then wonderful! If it was for work, I highly recommend you look into a BNF parser to do the dirty work for you, such as Spirit. It will pretty much eliminate the inevitable bugs in the parsing logic, and force you to fully define your grammar/semantics up front. (actually, I think you should look into this anyway, cause it's cool stuff no matter what hehe)

      Nice work on actually getting that much code out there! There's tons of functionality, and it's a non-trivial problem!
      I'll look at it. I think I saw spirit, but it did something that I didn't like, I forget what.

    5. #5
      Member Achievements:
      1 year registered Veteran First Class 5000 Hall Points

      Join Date
      Sep 2004
      Gender
      Location
      Seattle, WA
      Posts
      2,503
      Likes
      217
      Rock on!

      Because I'm weird, I was thinking about the enum thing in the shower, and I remembered there was a cute but hackish solution in an old game programming book I have, which looks like this:

      Code:
      // data.h
      DATA(sin)
      DATA(cos)
      DATA(tan)
      // ...
      Code:
      // main or whatever
      #define DATA(x) x,
      
      enum Operation {
        #include "data.h"
      };
      
      #undef DATA
      #define DATA(x) #x,
      static const char *operationNames[] = {
        #include "data.h"
      };
      
      #undef DATA
      Though really, you'd be much better off with something like:

      typedef map< string, boost::shared_ptr<Operation > > OperationMap;

      And eventually:

      OperationMap operations;
      boost::shared_ptr<Operation> sinOperation= new SinOperation();
      operations["sin"] = sinOperation;

      or somesuch.

    6. #6
      FBI agent Ynot's Avatar
      Join Date
      Oct 2005
      Gender
      Location
      Southend, Essex
      Posts
      4,337
      Likes
      14
      small points, really
      but don't mix C & C++ headers

      #include <math.h>
      should be
      #include <cmath>

      Also,
      if you're going to use string streams, then use them
      don't use atof(), which is part of the old-style C functions for converting between types
      (also, atof() is defined in cstdlib, which you haven't included)
      (\_ _/)
      (='.'=)
      (")_(")

    7. #7
      Member Achievements:
      1 year registered Veteran First Class 5000 Hall Points

      Join Date
      Sep 2004
      Gender
      Location
      Seattle, WA
      Posts
      2,503
      Likes
      217
      Ok, random first round of test inputs:

      Degenerate cases:
      Failed: "-0" != 0, it's -0
      Failed: "-(-1)" != 1, it's -1
      Failed: "(-(-(1)))" != 1, it's -1

      Rounding inconsistencies:
      Failed: "Pi+(-(-(-Pi)))" != 0, it's 2.65359e-06
      Failed: "Pi+---Pi" != 0, it's 3.14159
      Failed: "Pi+Pi-Pi" != 3.14159, it's 3.1416
      Failed: "Pi" != 3.14159, it's 3.141592653589793238462643383279502884197169399375 10582097494459230781640628620899862803482534211706 79
      Failed: "3.5^4-1" != 149.0625, it's 149.062

      (for the last one, the correct output is probably 149.063 if you want 6 digits, which I think is what you were going for, except for outputing just Pi)

      Parsing issues:
      Running this test:

      Code:
      Check("(", 1);
      Causes it to peg the CPU and start muching on memory.
      Last edited by Replicon; 08-09-2009 at 05:43 AM.

    8. #8
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      Unfortunately, there is nothing anyone can do about the rounding problems, that's the Intel processor, not my code. If you want accurate rounding, you have to run it on a SPARC or IBM processor.

      I didn't even realize that I had including math.h, that was stupid of me.

      My real tonumber and tostring function use stringstreams exclusively, atof was just for quick writing.

      cstdlib is included in iostream I believe.

      In computer science -0 == 0, however, that's not true in mathematics. In the mathematical field, negations are always preserved

      Hmm, I'll fix those negation cases.

      I don't care about incorrect input failing, everything going through is assumed to be correct.
      Last edited by ninja9578; 08-09-2009 at 02:45 PM.

    9. #9
      Rational Spiritualist DrunkenArse's Avatar
      Join Date
      May 2009
      Gender
      Location
      Da Aina
      Posts
      2,941
      Likes
      1092
      Quote Originally Posted by ninja9578 View Post
      In computer science -0 == 0, however, that's not true in mathematics. In the mathematical field, negations are always preserved
      That's not true. -0 == 0 is an identity in math that everybody uses all the time.
      I figured out that you have so many digits for your constants so you can do things like 9*10^-15*pi. Don't know why you wouln't just want to change your units though......


      Out of curiosity, what made you think that -0 =/= 0?
      Previously PhilosopherStoned

    10. #10
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      Fixed. Try the new code that I posted

      My company's scripting language has rounding functions and an about equal to operator, when using trip functions, I'll make sure the people using it use those
      @Boolean(2.65359e-06 ~~ 0) will pass.

      I was thinking that -0 != 0 because in physics vector direction should always be preserved, but I guess there is nothing that you can do with 0 to a vector that will make a difference in direction.

      The check macro that I'm using is a string comparison. In the language that I wrote, == uses a numeric comparison for numbers so it will act like the C++ code 0.0f == -0.0f. Which I'm fairly certain will fail.

      0.0f = 0 00000000 00000000000000000000000
      -0.0f =1 00000000 00000000000000000000000

      But in LiveScript @Boolean(0 ~~ -0) will pass (~~ being my operator for about equal to)
      Last edited by ninja9578; 08-09-2009 at 04:48 PM.

    11. #11
      Member Achievements:
      1 year registered Veteran First Class 5000 Hall Points

      Join Date
      Sep 2004
      Gender
      Location
      Seattle, WA
      Posts
      2,503
      Likes
      217
      Quote Originally Posted by ninja9578 View Post
      I don't care about incorrect input failing, everything going through is assumed to be correct.
      Errr, I would not touch a library that makes that assumption with a 10 foot pole, especially if it goes and runs away and eats the entire system's resources.

      Seriously, you should do the right thing and fix that. It shouldn't be that hard.

    12. #12
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      Seeing that the fix is only one line of code, I guess I'll do that.

      Fixed

    13. #13
      Member Achievements:
      1 year registered Veteran First Class 5000 Hall Points

      Join Date
      Sep 2004
      Gender
      Location
      Seattle, WA
      Posts
      2,503
      Likes
      217
      Yay, the world is sane again! (what was the one line?)

    14. #14
      Be a man of Value. Jorge's Avatar
      Join Date
      May 2008
      Gender
      Location
      Pico Rivera
      Posts
      529
      Likes
      22
      My gosh, I don't belong in this thread...I have no idea what the hell is going on.

    15. #15
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      Quote Originally Posted by Replicon View Post
      Yay, the world is sane again! (what was the one line?)
      This one:
      if (pos2 == string::npos) return "nan";

      I also fixed the 'e' in hyper being seen as the e constant
      Last edited by ninja9578; 08-10-2009 at 01:28 PM.

    16. #16
      Banned
      Join Date
      Apr 2007
      Location
      Out Chasing Rabbits
      Posts
      15,193
      Likes
      935
      I hope you guys helped me get all of the bugs out, I put it live today.

      I also added beta, gamma, integral, sinc, usinc, eta, zeta, ackemann, minkowski, and sign functions
      Last edited by ninja9578; 08-11-2009 at 03:40 AM.

    Bookmarks

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •