Simple Equation Solver ( in C++ )

Please support our C++ advertiser: Intel Parallel Studio Home
Alex Edwards Alex Edwards is offline Offline Nov 9th, 2008, 10:47 am |
0
This is a remake of the Simple Equation Solver snippet that is written in Java.

This is an equivalent version written in C++.
Quick reply to this message  
C++ Syntax
  1. /**
  2.  * Numerics.cpp
  3.  */
  4. #ifndef NUMERICS
  5. #define NUMERICS
  6.  
  7. #include <sstream>
  8. #include <string>
  9.  
  10. using std::stringstream;
  11. using std::string;
  12.  
  13. /**
  14.  * Convenience class for enabling easy
  15.  * conversions between strings and primitive types.
  16.  *
  17.  * This class is not meant to support non-primitive types,
  18.  * but it should if target class Type has istream and ostream
  19.  * operators overloaded.
  20.  */
  21. namespace Numerics{
  22.  
  23. template<class T>
  24. class Numerical{
  25.  
  26. private:
  27. T number;
  28.  
  29. public:
  30. Numerical(T value = 0) : number(value){}
  31. Numerical(const string& arg){
  32. const_cast<Numerical&>(*this)(arg);
  33. }
  34.  
  35. /**
  36. * Attempts to assign the argument value to the value
  37. * of this Object's type.
  38. * If the value is invalid, nothing happens.
  39. */
  40. string operator()(const string& arg){
  41. stringstream ss (stringstream::in | stringstream::out);
  42. try{
  43. ss << arg;
  44. ss >> number;
  45. }catch(...){
  46. // currently unsupported
  47. }
  48. return ss.str();
  49. }
  50.  
  51. /**
  52. * Attempts to assign the argument value to the value of
  53. * this Object's type.
  54. */
  55. T operator()(T value){
  56. return (number = value);
  57. }
  58.  
  59. /**
  60. * Returns a string representation of this Object's number
  61. */
  62. string getString(){
  63. stringstream ss (stringstream::in | stringstream::out);
  64. ss << number;
  65. return ss.str();
  66. }
  67.  
  68. /**
  69. * Returns a copy of this Object's number
  70. */
  71. T getValue(){
  72. return number;
  73. }
  74.  
  75. /**
  76. * Extraction operator used to return the underlying value
  77. * during operations assosciated with primitive types.
  78. */
  79. operator T& (){
  80. return number;
  81. }
  82.  
  83. /**
  84. * const version of the above operator. Returns a copy
  85. * of this Object's number.
  86. */
  87. operator T () const{
  88. return number;
  89. }
  90. };
  91.  
  92. /* Some meaningful typedefs for Numerical types */
  93. typedef Numerical<short> Short;
  94. typedef Numerical<unsigned short> U_Short;
  95. typedef Numerical<int> Integer;
  96. typedef Numerical<unsigned int> U_Integer;
  97. typedef Numerical<double> Double;
  98. typedef Numerical<float> Float;
  99. typedef Numerical<char> Byte;
  100. typedef Numerical<unsigned char> U_Byte;
  101. typedef Numerical<wchar_t> Wide_Byte;
  102. typedef Numerical<long int> Long;
  103. typedef Numerical<unsigned long int> U_Long;
  104.  
  105. /* For non-standard types, like __int8, __int16, __int32, and __int64 */
  106. #ifdef ALLOW_NONSTANDARD_PRIMITIVE_TYPES
  107.  
  108. #if (ALLOW_NONSTANDARD_PRIMITIVE_TYPES == 0x01)
  109. typedef Numerical < __int8 > __Int8;
  110. typedef Numerical < unsigned __int8 > U___Int8;
  111. typedef Numerical < __int16 > __Int16;
  112. typedef Numerical < unsigned __int16 > U___Int16;
  113. typedef Numerical < __int32 > __Int32;
  114. typedef Numerical < unsigned __int32 > U___Int32;
  115. typedef Numerical < __int64 > __Int64;
  116. typedef Numerical < unsigned __int64 > U___Int64;
  117. #endif
  118.  
  119. #endif
  120. }
  121.  
  122. #endif
  123.  
  124.  
  125. ////////////////////////////////////
  126.  
  127. /**
  128.  * EquationSolver.h
  129.  */
  130.  
  131. #ifndef EQUATION_SOLVER_H
  132. #define EQUATION_SOLVER_H
  133.  
  134. #include <string>
  135. #include <vector>
  136.  
  137. using std::string;
  138. using std::vector;
  139.  
  140. namespace EquationHelper{
  141.  
  142. class EquationSolver{
  143. private:
  144. EquationSolver();
  145. static string doOperation(const string&, char, const string&);
  146. static void correctedString(string&);
  147. static void removeSpaces(string&);
  148. static string parse(const string&);
  149. static bool isSolvable(const string&);
  150. static void calculate(vector<string>&, vector<char>&, const string&);
  151.  
  152. public:
  153. static string solve(const string&, int = 50);
  154. };
  155. }
  156. #include "EquationSolver.cpp"
  157.  
  158. #endif
  159.  
  160. ////////////////////////////////
  161.  
  162. /**
  163.  * EquationSolver.cpp
  164.  */
  165.  
  166. #ifdef EQUATION_SOLVER_H
  167.  
  168. #include <iostream>
  169. #include <cmath>
  170. #include <vector>
  171. #include <string>
  172. #include "Numerics.cpp"
  173.  
  174. using namespace EquationHelper;
  175. using namespace Numerics;
  176. using std::size_t;
  177. using std::vector;
  178. using std::string;
  179. using std::cout;
  180. using std::endl;
  181. using std::ios;
  182.  
  183. typedef EquationSolver ES;
  184.  
  185. /**
  186.  * Private constructor - does nothing.
  187.  */
  188. ES::EquationSolver(){}
  189.  
  190. /**
  191.  * Performs the specified operation against the
  192.  * argument strings. The operation is dependant on
  193.  * the value of op.
  194.  */
  195. string ES::doOperation(const string& lhs, char op, const string& rhs){
  196.  
  197. Double bdLhs = lhs;
  198. Double bdRhs = rhs;
  199. Double temp;
  200. switch(op){
  201. case '^':
  202. temp( pow( bdLhs, bdRhs ) );
  203. break;
  204. case '*':
  205. temp( bdLhs * bdRhs );
  206. break;
  207. case '/':
  208. temp( bdLhs / bdRhs );
  209. break;
  210. case '+':
  211. temp( bdLhs + bdRhs );
  212. break;
  213. case '%':
  214. temp( fmod(bdLhs, bdRhs) );
  215. break;
  216. }
  217. return temp.getString();
  218. }
  219.  
  220. /**
  221.  * Returns the string with its enclosing paranthesis
  222.  * stripped from it.
  223.  */
  224. void ES::correctedString(string& arg){
  225.  
  226. size_t pos1 = arg.find_first_of("(");
  227. size_t pos2 = arg.find_last_of(")");
  228.  
  229. if(pos1 >= 0 && pos1 < arg.length() && pos2 >= 0 && pos2 <= arg.length())
  230. arg[pos1] = arg[pos2] = ' ';
  231. }
  232.  
  233. /**
  234.  * Remove spaces from the argument string.
  235.  */
  236. void ES::removeSpaces(string& argu){
  237.  
  238. string temp = "";
  239. for(size_t i = 0; i < argu.length(); i++)
  240. if(argu[i] != ' ')
  241. temp += argu[i];
  242. argu = temp;
  243. }
  244.  
  245. /**
  246.  * The brains of the program.
  247.  * Solves expressions by using recursion for complex expressions.
  248.  */
  249. string ES::parse(const string& param){
  250.  
  251. string expression = param;
  252. correctedString(expression);
  253. removeSpaces(expression);
  254. string finalExpression = "";
  255.  
  256. bool operatorEncountered = true;
  257. for(size_t i = 0; i < expression.length(); i++){
  258. if(expression[i] == '('){
  259. string placeHolder = "(";
  260. int valuesCounted = 1;
  261. operatorEncountered = false;
  262. for(size_t j = i + 1; valuesCounted != 0; j++){
  263. if(expression[j] == '(')
  264. valuesCounted++;
  265. else if(expression[j] == ')')
  266. valuesCounted--;
  267.  
  268. placeHolder += expression[j];
  269. }
  270.  
  271. string evaluatedString = parse(placeHolder);
  272. finalExpression += evaluatedString;
  273. i += (placeHolder.length() - 1);
  274. }else{
  275. if(expression[i] == '-' && operatorEncountered == false)
  276. finalExpression += '+';
  277.  
  278. finalExpression += expression[i];
  279. if((expression[i] == '+'
  280. || expression[i] == '/'
  281. || expression[i] == '^'
  282. || expression[i] == '*'
  283. || expression[i] == '%'
  284. || expression[i] == '-'))
  285. operatorEncountered = true;
  286. else if(expression[i] != ' ')
  287. operatorEncountered = false;
  288. }
  289. }
  290.  
  291. removeSpaces(finalExpression);
  292. string perfectExpression = "";
  293.  
  294. for(size_t i = 0; i < finalExpression.length(); i++){
  295. if((i + 1) < finalExpression.length())
  296. if(finalExpression[i] == '-' && finalExpression[i + 1] == '-')
  297. i += 2;
  298. perfectExpression += finalExpression[i];
  299. }
  300. finalExpression = perfectExpression;
  301.  
  302. vector<string> totalNumbers;
  303. vector<char> totalOperations;
  304. cout << finalExpression << endl;
  305.  
  306. for(size_t i = 0; i < finalExpression.length(); i++){
  307. if(finalExpression[i] >= '0' && finalExpression[i] <= '9'
  308. || finalExpression[i] == '-' || finalExpression[i] == '.'){
  309. string temp = ""; //
  310. for(size_t j = i; j < finalExpression.length(); j++){
  311. if(finalExpression[j] >= '0' && finalExpression[j] <= '9'
  312. || finalExpression[j] == '-' || finalExpression[j] == '.'){
  313. temp += finalExpression[j];
  314. }else break;
  315. }
  316. totalNumbers.push_back(temp);
  317. i += temp.length() == 0 ? 0 : (temp.length() - 1);
  318. }else if(finalExpression[i] == '*'
  319. || finalExpression[i] == '/'
  320. || finalExpression[i] == '^'
  321. || finalExpression[i] == '+'
  322. || finalExpression[i] == '%'
  323. ){
  324. totalOperations.push_back(finalExpression[i]);
  325. }
  326. }
  327.  
  328. ES::calculate(totalNumbers, totalOperations, "^");
  329. ES::calculate(totalNumbers, totalOperations, "*/%");
  330. ES::calculate(totalNumbers, totalOperations, "+");
  331.  
  332. return totalNumbers[0];
  333. }
  334.  
  335. /**
  336.  * Calculates the numbers in the first vector using the operands in the 2nd vector,
  337.  * based on the expressions allowed which are determined by the string argument.
  338.  */
  339. void ES::calculate(vector<string>& totalNumbers, vector<char>& totalOperations,
  340. const string& arg){
  341.  
  342. for(int i = 0; i < static_cast<int>(totalOperations.size()); i++){
  343. if( arg.find(totalOperations[i]) != arg.npos){
  344. totalNumbers[i] = doOperation(totalNumbers[i], totalOperations[i], totalNumbers[i + 1]);
  345.  
  346. size_t oldNumberLength = totalNumbers.size();
  347. size_t oldOperatorLength = totalOperations.size();
  348. size_t nextNumberLength = oldNumberLength - 1;
  349. size_t nextOperatorLength = oldOperatorLength - 1;
  350. size_t sCount = 0;
  351. size_t oCount = 0;
  352. vector<string> temp1 ( nextNumberLength );
  353. vector<char> temp2 ( nextOperatorLength );
  354.  
  355. for(size_t j = 0; j < oldNumberLength; j++){
  356. if(j != static_cast<int>(i + 1)){
  357. temp1[sCount++] = totalNumbers[j];
  358. }
  359. if(j != i && j < oldOperatorLength){
  360. temp2[oCount++] = totalOperations[j];
  361. }
  362. }
  363. totalNumbers = temp1;
  364. totalOperations = temp2;
  365.  
  366. i--;
  367. }
  368. }
  369. }
  370.  
  371. /**
  372.  * Returns true if the equation is solvable (not really),
  373.  * returns false otherwise.
  374.  *
  375.  * This function is truly a misnomer, because more restrictions
  376.  * should be put in place.
  377.  */
  378. bool ES::isSolvable(const string& eq){
  379.  
  380. int paranthesisCount = 0;
  381. for(size_t i = 0; i < eq.length(); i++){
  382. if(eq[i] == '(')
  383. paranthesisCount++;
  384. else if(eq[i] == ')')
  385. paranthesisCount--;
  386.  
  387. if(paranthesisCount < 0)
  388. return false;
  389. }
  390. return paranthesisCount == 0;
  391. }
  392.  
  393. /**
  394.  * An attempt to solve a string-expression, given
  395.  * a precision value.
  396.  */
  397. string ES::solve(const string& eq, int prec){
  398.  
  399. if(isSolvable(eq)){
  400.  
  401. stringstream ss (stringstream::in | stringstream::out);
  402. cout << eq << endl;
  403. string value;
  404.  
  405. value += '(';
  406. value += eq;
  407. value += ')';
  408.  
  409. ss.setf(0, ios::floatfield);
  410. ss.precision(prec);
  411. ss << parse(value);
  412.  
  413. return ss.str();
  414. }else return "";
  415. }
  416.  
  417. #endif
  418.  
  419. ////////////////////////////////////
  420.  
  421. /**
  422.  * DriverProgram.cpp
  423.  */
  424.  
  425. /****************************************
  426.  * @Author: Mark Alexander Edwards Jr.
  427.  *
  428.  * This program uses a utility class to solve
  429.  * complex equations represented by string
  430.  * expressions
  431.  ****************************************/
  432. #include <iostream>
  433. #include <ctime>
  434. #include <string>
  435. #include "Numerics.cpp"
  436. #include "EquationSolver.h"
  437.  
  438. using std::cin;
  439. using std::cout;
  440. using std::endl;
  441. using std::flush;
  442. using std::string;
  443. using namespace Numerics;
  444. using namespace EquationHelper;
  445.  
  446. int main(){
  447.  
  448. cout << ES::solve("5 + 3 * (8 - 4)") << endl << endl;
  449. cout << ES::solve("12 / (3 * 4) + 5") << endl << endl;
  450.  
  451. while(true){
  452. cout << "Enter an equation you would \nlike to solve (enter 'exit' to quit): " << flush;
  453. try{
  454. string temp;
  455. getline(cin, temp);
  456. if(temp.compare("exit") == 0) exit(0);
  457. clock_t t1 = clock();
  458. cout << "Answer: " + ES::solve(temp, 4) << endl;
  459. clock_t t2 = clock();
  460. cout << "\nTime taken to calculate value: " <<
  461. (t2-t1) << " Clock cycles" <<endl;
  462. }catch(...){
  463. cout << "Invalid expression! Please try again!" << endl;
  464. }
  465. }
  466.  
  467. cin.ignore();
  468. cin.get();
  469. return 0;
  470. }
0
William Hemsworth William Hemsworth is online now Online | Nov 9th, 2008
Very nice
 
0
Alex Edwards Alex Edwards is offline Offline | Nov 9th, 2008
When I have more time I might refactor the program to accept manipulators like cos, sin, tan, etc as well as SQRT but then again that can be accomplished by taking values to the .5 power @_@
 
0
Alex Edwards Alex Edwards is offline Offline | Nov 10th, 2008
Apparently there's a problem with the current program @_@

According to VernonDozier, Algebraic law states that exponents are evaluated in a R-to-L fashion and not L-to-R.

The fix is easy to produce, but introducing negative multiplication after exponential calculation may indeed be difficult unless I re-evaluate the entire system and determine how to conveniently change the implementation.
 
0
ddanbe ddanbe is offline Offline | Nov 27th, 2008
Just browsing the code I wondered why there is no case '-' in string ES::doOperation(const string& lhs, char op, const string& rhs){...
 
0
Alex Edwards Alex Edwards is offline Offline | Nov 27th, 2008
Subtraction is technically the same as adding a negative, so there was no need to add a subtraction operator to the doOperation method. Conversions from subtraction to adding a negative are most likely done during the expansion/compression portion of the parse method.
 
0
ddanbe ddanbe is offline Offline | Nov 28th, 2008
Thanks for the explanation :o)
 
 

Message:


Thread Tools Search this Thread



About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC