fork download
  1. #include <iostream>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <ctype.h>
  5. #include <math.h>
  6. #include <cmath>
  7. #include <vector>
  8. #include <set>
  9.  
  10. using namespace std;
  11.  
  12. enum { EMPTY_VALUE, TOKEN_PLUS, TOKEN_MINUS, TOKEN_MULTIPLY, TOKEN_DIVIDE_FLOAT, TOKEN_DIVIDE_INT, TOKEN_POWER, TOKEN_LEFTBRACKET, TOKEN_RIGHTBRACKET, TOKEN_MODULO, TOKEN_DOUBLE,
  13. TOKEN_SPACE, TOKEN_SIN, TOKEN_COS, TOKEN_TAN, TOKEN_ABS, TOKEN_FACTORIAL, TOKEN_TO_TYPE };
  14. enum { NO_ERROR, EMPTY_BRACKETS, ERROR_ILLEGAL_STR, ERROR_SECOND_DECIMAL, ERROR_UNEXPECTED_DECIMAL, ERROR_FAULTY_END, ERROR_MISMATCHED_BRACKETS };
  15.  
  16. const double m_pi = 3.14159265358979323846264338327950288419716939937510;
  17. const double m_e = 2.71828182845904523536028747135266249775724709369995;
  18.  
  19. char a_allowedChars[] = {'+','-','*','/','0','1','2','3','4','5','6','7','8','9','(',')','^','.','%','!'};
  20. set<char> allowedCharsSet (a_allowedChars,a_allowedChars+50);
  21.  
  22. struct stTokens {
  23. vector<long double> values;
  24. vector<int> types;
  25. void removePos(int pos) {
  26. values.erase(values.begin()+pos);
  27. types.erase(types.begin()+pos);
  28. }
  29. void removePos(int pos, int posEnd) {
  30. values.erase(values.begin()+pos, values.begin()+posEnd);
  31. types.erase(types.begin()+pos, types.begin()+posEnd);
  32. }
  33. void dumpData() {
  34. for (unsigned int i = 0; i != values.size(); ++i) {
  35. cout << "#" << i << ": " << types.at(i) << ", " << values.at(i) << endl;
  36. }
  37. cout << "Dump complete\n";
  38. }
  39. void addData(int tokenType, long double value = EMPTY_VALUE) {
  40. types.push_back(tokenType);
  41. values.push_back(value);
  42. }
  43. void setData(int pos, int tokenType, long double value) {
  44. types.at(pos) = tokenType;
  45. values.at(pos) = value;
  46. }
  47. void insertData(int pos, int tokenType, long double value = EMPTY_VALUE) {
  48. types.insert(types.begin()+pos, tokenType);
  49. values.insert(values.begin()+pos, value);
  50. }
  51. };
  52.  
  53. /**
  54.   Stop searching through the string for extra characters to add to the current string, and place the number as a single value to our tokenList
  55.   */
  56. inline void addDoubleFromString(stTokens &tokenList, bool &numString, bool &foundDec, string input) {
  57. if(numString) {
  58. tokenList.addData(TOKEN_DOUBLE,atof(input.c_str()));
  59. numString = false;
  60. foundDec = false;
  61. }
  62. }
  63.  
  64. string stripSpaces(string input) {
  65. string newString;
  66. for(unsigned int i = 0; i != input.size(); ++i) {
  67. if(input.at(i) != ' ') {
  68. newString += input.at(i);
  69. }
  70. }
  71. return newString;
  72. }
  73.  
  74. int factorial(int n) {
  75. if(n == 1)
  76. return 1;
  77. else
  78. return n*factorial(n-1);
  79. }
  80.  
  81. int convertStringToTokens(string sInput, stTokens &tokenList, long double previousAnswer = 0) {
  82.  
  83. sInput = stripSpaces(sInput);
  84.  
  85. bool blNumberString = false;
  86. bool blFoundDecimal = false;
  87. string stNumber;
  88.  
  89. unsigned int i = 0;
  90. while(i < sInput.length()) {
  91.  
  92. if(isdigit(sInput.at(i))) { //We've found a number, keep adding the current char to a string til we find a non-digit
  93.  
  94. if(blNumberString) {
  95. stNumber += sInput.at(i);
  96. } else {
  97. stNumber = sInput.at(i);
  98. blNumberString = true;
  99. }
  100.  
  101. }
  102. else if(sInput.at(i) == '.') {
  103. if(blNumberString) {
  104. if(blFoundDecimal) {
  105. cout << "A second decimal point was found at position " << i+1 << ", could not evaluate expression.\n";
  106. return ERROR_SECOND_DECIMAL;
  107. } else {
  108. blFoundDecimal = true;
  109. stNumber += sInput.at(i);
  110. }
  111. } else {
  112. cout << "An unexpected decimal point was found at position " << i+1 << ", could not evaluate expression.\n";
  113. return ERROR_UNEXPECTED_DECIMAL;
  114. }
  115. }
  116. else if(isspace(sInput.at(i))) {
  117. //DO ABSOLUTELY NOTHING
  118. addDoubleFromString(tokenList, blNumberString,blFoundDecimal,stNumber);
  119. }
  120. else if(allowedCharsSet.find(sInput.at(i)) == allowedCharsSet.end()) {
  121. //We've found a letter, it could be part of a function. Find the whole word, and then see if it's an allowed word.
  122. addDoubleFromString(tokenList, blNumberString,blFoundDecimal,stNumber);
  123.  
  124. string stWordBuffer;
  125. while( (i < sInput.length()) ) {
  126. if( allowedCharsSet.find(sInput.at(i)) == allowedCharsSet.end() ) {
  127. stWordBuffer += tolower(sInput.at(i));
  128. } else {
  129. break;
  130. }
  131. ++i;
  132. }
  133.  
  134. if(stWordBuffer == "div") {
  135. tokenList.addData(TOKEN_DIVIDE_INT);
  136. }
  137. else if(stWordBuffer == "mod") {
  138. tokenList.addData(TOKEN_MODULO);
  139. }
  140. else if(stWordBuffer == "sin") {
  141. tokenList.addData(TOKEN_SIN);
  142. }
  143. else if(stWordBuffer == "cos") {
  144. tokenList.addData(TOKEN_COS);
  145. }
  146. else if(stWordBuffer == "tan") {
  147. tokenList.addData(TOKEN_TAN);
  148. }
  149. else if(stWordBuffer == "abs") {
  150. tokenList.addData(TOKEN_ABS);
  151. }
  152. else if(stWordBuffer == "e") {
  153. tokenList.addData(TOKEN_DOUBLE, m_e);
  154. }
  155. else if(stWordBuffer == "pi") {
  156. tokenList.addData(TOKEN_DOUBLE, m_pi);
  157. }
  158. else if(stWordBuffer == "ans") {
  159. tokenList.addData(TOKEN_DOUBLE, previousAnswer);
  160. }
  161. else if(stWordBuffer == "todegrees") {
  162. tokenList.addData(TOKEN_DOUBLE, 180/m_pi); //Convert radians to degrees
  163. }
  164. else if(stWordBuffer == "toradians") {
  165. tokenList.addData(TOKEN_DOUBLE, m_pi/180); //Convert radians to degrees
  166. }
  167. else {
  168. cout << "An illegal set of characters, '" << stWordBuffer << "' was found at position " << i+1 << ", could not evaluate expression.\n";
  169. return ERROR_ILLEGAL_STR;
  170. }
  171.  
  172. continue;
  173. }
  174. else { //Else add the appropriate token to the vector
  175. addDoubleFromString(tokenList, blNumberString, blFoundDecimal,stNumber);
  176.  
  177. switch (sInput.at(i)) {
  178. case '+': tokenList.addData(TOKEN_PLUS); break;
  179. case '-': tokenList.addData(TOKEN_MINUS); break;
  180. case '*': tokenList.addData(TOKEN_MULTIPLY); break;
  181. case '/': tokenList.addData(TOKEN_DIVIDE_FLOAT); break;
  182. case '^': tokenList.addData(TOKEN_POWER); break;
  183. case '(': tokenList.addData(TOKEN_LEFTBRACKET); break;
  184. case ')': tokenList.addData(TOKEN_RIGHTBRACKET); break;
  185. case '%': tokenList.addData(TOKEN_MODULO); break;
  186. case '!': tokenList.addData(TOKEN_FACTORIAL); break;
  187. //case ';': tokenList.addData(TOKEN_MULTIPLY); break;
  188. }
  189. }
  190.  
  191. ++i;
  192. }
  193. if(blNumberString) {
  194. addDoubleFromString(tokenList, blNumberString,blFoundDecimal,stNumber);
  195. }
  196.  
  197. //Remove spaces
  198. unsigned int count = 0, deleted = 0;
  199. while(count - deleted < tokenList.types.size()) {
  200. if(tokenList.types.at(count-deleted) == TOKEN_SPACE) {
  201. tokenList.removePos(count-deleted);
  202. ++deleted;
  203. }
  204. ++count;
  205. }
  206.  
  207. //Perform various checks to ensure we don't have faulty token data that could mess up our calculation later
  208.  
  209. //Check for mismatched brackets
  210. int lBrackets = 0, rBrackets = 0;
  211. for (unsigned int i = 0; i != tokenList.types.size(); ++i) {
  212. if(tokenList.types.at(i) == TOKEN_LEFTBRACKET) ++lBrackets;
  213. if(tokenList.types.at(i) == TOKEN_RIGHTBRACKET) ++rBrackets;
  214. }
  215. if(lBrackets != rBrackets) {
  216. cout << "Number of left brackets does not match the number of right brackets.\n";
  217. return ERROR_MISMATCHED_BRACKETS;
  218. }
  219.  
  220. //Add a leading 0 to starting + or - tokens
  221. if( (tokenList.types.front() == TOKEN_PLUS) || (tokenList.types.front() == TOKEN_MINUS) ) {
  222. tokenList.addData(TOKEN_DOUBLE,0);
  223. }
  224.  
  225. //Insert * between any TOKEN_DOUBLE and TOKEN_LEFTBRACKET
  226. for (unsigned int i = 1; i < tokenList.types.size(); ++i) {
  227. if( (tokenList.types.at(i-1) == TOKEN_DOUBLE) && (tokenList.types.at(i) == TOKEN_LEFTBRACKET) ) tokenList.insertData(i,TOKEN_MULTIPLY);
  228. if( (tokenList.types.at(i-1) == TOKEN_RIGHTBRACKET) && (tokenList.types.at(i) == TOKEN_DOUBLE) ) tokenList.insertData(i,TOKEN_MULTIPLY);
  229. if( (tokenList.types.at(i-1) == TOKEN_RIGHTBRACKET) && (tokenList.types.at(i) == TOKEN_LEFTBRACKET) ) tokenList.insertData(i,TOKEN_MULTIPLY);
  230. }
  231.  
  232. //tokenList.dumpData();
  233. //cout << "Converted string '" << sInput << "' to tokens successfully.\n";
  234. return NO_ERROR;
  235. }
  236.  
  237. int evaluateTokens_rc(stTokens &tokens, long double &result) {
  238.  
  239. //tokens.dumpData();
  240.  
  241. if(tokens.values.size() == 0) { result = 0; return EMPTY_BRACKETS; }
  242.  
  243. if (! ((tokens.types.back() == TOKEN_DOUBLE) || (tokens.types.back() == TOKEN_RIGHTBRACKET) || (tokens.types.back() == TOKEN_FACTORIAL)) ) {
  244. cout << "The final character (possibly within a bracket) was not a number or closing bracket.\n";
  245. result = 0;
  246. return ERROR_FAULTY_END;
  247. }
  248.  
  249. //Check for brackets, and recursively evaluate anything inside them
  250. int leftBracketsFound = 0;
  251. int rightBracketsFound = 0;
  252. stTokens smallerEvaluation;
  253. int posLeftBracket = 0, posRightBracket = 0;
  254. for (unsigned int i = 0; i < tokens.types.size(); ++i) {
  255.  
  256. if(leftBracketsFound > 0) {
  257. smallerEvaluation.types.push_back(tokens.types.at(i));
  258. smallerEvaluation.values.push_back(tokens.values.at(i));
  259. }
  260.  
  261. if(tokens.types.at(i) == TOKEN_LEFTBRACKET) {
  262. ++leftBracketsFound;
  263. if(leftBracketsFound == 1)
  264. posLeftBracket = i;
  265. }
  266. else if(tokens.types.at(i) == TOKEN_RIGHTBRACKET) {
  267. ++rightBracketsFound;
  268. if( (leftBracketsFound == rightBracketsFound) && (rightBracketsFound > 0) ) {
  269. posRightBracket = i;
  270. //Remove the brackets and contents
  271. tokens.removePos(posLeftBracket, posRightBracket);
  272. smallerEvaluation.removePos(smallerEvaluation.types.size()-1);
  273. //Evaluate the brackets and insert that into the tokens
  274. long double dblSmallerEvaluate;
  275. int rsltSmallerEvaluate = evaluateTokens_rc(smallerEvaluation, dblSmallerEvaluate);
  276. if(rsltSmallerEvaluate != NO_ERROR) {
  277. //There was an error found, so return that error code.
  278. return rsltSmallerEvaluate;
  279. }
  280.  
  281. tokens.types.at(posLeftBracket) = TOKEN_DOUBLE;
  282. tokens.values.at(posLeftBracket) = dblSmallerEvaluate;
  283.  
  284. i = 0;
  285. smallerEvaluation.values.clear();
  286. smallerEvaluation.types.clear();
  287.  
  288. leftBracketsFound = 0;
  289. rightBracketsFound = 0;
  290. }
  291. }
  292.  
  293. }
  294.  
  295. //Find carets for exponents evaluation
  296. for (int i = tokens.types.size() - 1; i >= 0; --i) {
  297. switch(tokens.types.at(i)) {
  298. case TOKEN_POWER:
  299. long double base = tokens.values.at(i-1);
  300. long double exp = tokens.values.at(i+1);
  301. tokens.setData(i,TOKEN_DOUBLE,pow(base,exp)); tokens.removePos(i-1); tokens.removePos(i); --i; break;
  302. }
  303. }
  304.  
  305.  
  306. //Now run functions, like sin, cos
  307. for (unsigned int i = 0; i < tokens.types.size(); ++i) {
  308. int changeMade = 0;
  309. switch(tokens.types.at(i)) {
  310. case TOKEN_SIN: tokens.setData(i,TOKEN_DOUBLE,sin(tokens.values.at(i+1))); changeMade = 1; break;
  311. case TOKEN_COS: tokens.setData(i,TOKEN_DOUBLE,cos(tokens.values.at(i+1))); changeMade = 1; break;
  312. case TOKEN_TAN: tokens.setData(i,TOKEN_DOUBLE,tan(tokens.values.at(i+1))); changeMade = 1; break;
  313. case TOKEN_ABS: tokens.setData(i,TOKEN_DOUBLE,abs(tokens.values.at(i+1))); changeMade = 1; break;
  314. case TOKEN_FACTORIAL: tokens.setData(i,TOKEN_DOUBLE,factorial(static_cast<int>(tokens.values.at(i-1)))); changeMade = 2; break;
  315. }
  316. if(changeMade == 1) {
  317. tokens.removePos(i+1);
  318. --i;
  319. }
  320. else if(changeMade == 2) {
  321. tokens.removePos(i-1);
  322. --i;
  323. }
  324. }
  325.  
  326. //Find multiplications and divisions
  327. for (unsigned int i = 0; i < tokens.types.size(); ++i) {
  328. bool changeMade = false;
  329. switch(tokens.types.at(i)) {
  330. case TOKEN_MULTIPLY: tokens.setData(i,TOKEN_DOUBLE,tokens.values.at(i-1) * tokens.values.at(i+1)); changeMade = true; break;
  331. case TOKEN_DIVIDE_FLOAT: tokens.setData(i,TOKEN_DOUBLE,tokens.values.at(i-1) / tokens.values.at(i+1)); changeMade = true; break;
  332. case TOKEN_DIVIDE_INT: tokens.setData(i,TOKEN_DOUBLE,static_cast<int>(tokens.values.at(i-1)) / static_cast<int>(tokens.values.at(i+1))); changeMade = true; break;
  333. case TOKEN_MODULO: tokens.setData(i,TOKEN_DOUBLE,static_cast<int>(tokens.values.at(i-1)) % static_cast<int>(tokens.values.at(i+1))); changeMade = true; break;
  334. }
  335. if(changeMade) {
  336. tokens.removePos(i-1);
  337. tokens.removePos(i);
  338. --i;
  339. }
  340. }
  341.  
  342. //And finally addition and subtraction
  343. for (unsigned int i = 0; i < tokens.types.size(); ++i) {
  344. bool changeMade = false;
  345. switch(tokens.types.at(i)) {
  346. case TOKEN_PLUS: tokens.setData(i,TOKEN_DOUBLE,tokens.values.at(i-1) + tokens.values.at(i+1)); changeMade = true; break;
  347. case TOKEN_MINUS: tokens.setData(i,TOKEN_DOUBLE,tokens.values.at(i-1) - tokens.values.at(i+1)); changeMade = true; break;
  348. }
  349. if(changeMade) {
  350. tokens.removePos(i-1);
  351. tokens.removePos(i);
  352. --i;
  353. }
  354. }
  355.  
  356. result = tokens.values.at(0);
  357. return NO_ERROR;
  358. }
  359.  
  360. int main()
  361. {
  362. cout.precision(10);
  363. cout << "Calculator program by Chris Winward. Type 'help' for instructions.\n";
  364. cout << ">>> ";
  365.  
  366. string sLineIn;
  367. long double calculatedResult = 0;
  368. getline(cin, sLineIn);
  369.  
  370. while(sLineIn != "exit") {
  371. if(sLineIn == "help") {
  372. cout << "This calculator uses BIFDMAS to calculate results - Brackets, Indices, Functions, Division & Multiplication, Addition & Subtraction.\n\n";
  373. cout << "The following single digit operators are available:\n";
  374. cout << "* / + - ^ % ( ) !\n";
  375. cout << "/ refers to real division, ^ is raise to power, % is modulo operando, and ! is factorial.\n\n";
  376. cout << "The following functions are available:\n";
  377. cout << "div mod sin cos tan abs\n";
  378. cout << "div refers to integer division. All trigonometric functions take input in radians.\n\n";
  379. cout << "The following constants are available:\n\n";
  380. cout << "e = 2.718... pi = 3.141... todegrees = 57.296... toradians = 0.0174...\n";
  381. cout << "The word 'ans' can be used to access the last equations answer.\n";
  382. cout << endl;
  383. } else {
  384. stTokens tokenList;
  385.  
  386. if(convertStringToTokens(sLineIn, tokenList, calculatedResult) == NO_ERROR) {
  387. if(evaluateTokens_rc(tokenList,calculatedResult) == NO_ERROR) {
  388. //tokenList.dumpData();
  389. cout << calculatedResult << endl << endl;
  390. }
  391. }
  392. }
  393.  
  394. cout << ">>> ";
  395. getline(cin, sLineIn);
  396. }
  397.  
  398. return 0;
  399. }
  400.  
Success #stdin #stdout 0.01s 2880KB
stdin
help
exit
stdout
Calculator program by Chris Winward. Type 'help' for instructions.
>>> This calculator uses BIFDMAS to calculate results - Brackets, Indices, Functions, Division & Multiplication, Addition & Subtraction.

The following single digit operators are available:
* / + - ^ % ( ) !
/ refers to real division, ^ is raise to power, % is modulo operando, and ! is factorial.

The following functions are available:
div mod sin cos tan abs
div refers to integer division. All trigonometric functions take input in radians.

The following constants are available:

e = 2.718... pi = 3.141... todegrees = 57.296... toradians = 0.0174...
The word 'ans' can be used to access the last equations answer.

>>>