#include <Arduino.h>
#include <math.h> // Include for sin, cos, tan, sinh, cosh, tanh, etc.
const double PIii = 3.14159265358979323846264338327950288419716939937510;
const double E = 2.71828182845904523536028747135266249775724709369995; // Euler's number
class CharStack {
private:
String* arr;
int capacity;
int top;
public:
CharStack(int size) : capacity(size), arr(new String[size]), top(-1) {}
~CharStack() { delete[] arr; }
void push(String element) { if (top < capacity - 1) arr[++top] = element; }
String pop() { return top >= 0 ? arr[top--] : ""; }
String peek() { return top >= 0 ? arr[top] : ""; }
bool isEmpty() { return top < 0; }
};
class FloatStack {
private:
double* arr;
int top;
public:
FloatStack(int size) : arr(new double[size]), top(-1) {}
~FloatStack() { delete[] arr; }
void push(double element) { arr[++top] = element; }
double pop() { return arr[top--]; }
bool isEmpty() const { return top < 0; }
};
// Define inverse hyperbolic functions
double my_asinh(double x) {
return log(x + sqrt(x * x + 1));
}
double my_acosh(double x) {
return log(x + sqrt(x * x - 1));
}
double my_atanh(double x) {
return 0.5 * log((1 + x) / (1 - x));
}
// Optimized power function to handle integer exponents efficiently
double power(double base, double exp) {
if (exp == 0) return 1;
if (exp == 1) return base;
if (base == 0) return 0;
if (floor(exp) == exp) { // Check if exponent is an integer
int n = (int)exp;
double result = 1.0;
bool isNegative = (n < 0);
n = abs(n);
while (n > 0) { // Exponentiation by squaring for integer exponents
if (n % 2 == 1) {
result *= base;
}
base *= base;
n /= 2;
}
return isNegative ? 1.0 / result : result;
}
return pow(base, exp); // For non-integer exponents, use standard pow function
}
// Factorial helper function
double factorial(int n) {
if (n < 0) return -1; // Factorial is not defined for negative numbers
double result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
// Helper function to detect and match functions
String matchFunction(const String& infix, int& i) {
// List of all supported functions (ordered by length to avoid partial matches)
String functionNames[] = {"asinh", "acosh", "atanh", "sinh", "cosh", "tanh", "sind", "cosd", "tand", "sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "exp", "ln", "log", "antilog"};
for (int j = 0; j < 20; j++) {
String func = functionNames[j];
if (infix.substring(i, i + func.length()) == func) {
i += func.length() - 1; // Move index to the end of the function name
return func;
}
}
return ""; // No match found
}
String infixToPostfix(String infix);
int precedence(String op);
bool isOperator(char c);
double evaluatePostfix(const String& postfix);
String addZeroBeforeUnaryMinus(const String& infix);
double convertToRadians(double angle);
// Convert radians to degrees
double convertToDegrees(double radians) {
return radians * (180.0 / PIii); // Converts radians to degrees using the constant PI
}
bool useRadians = true; // Global flag for radian or degree mode
void setup() {
Serial.begin(9600);
while (!Serial); // Wait for Serial to initialize
Serial.println("Choose mode: Enter 'r' for Radians or 'd' for Degrees:");
while (!Serial.available()) {
// Wait for the user to enter 'r' or 'd'
}
char mode = Serial.read();
useRadians = (mode == 'r'); // Set mode based on input
Serial.println("Enter an infix expression:");
}
void loop() {
if (Serial.available() > 0) {
String infix = Serial.readStringUntil('\n');
infix.trim();
Serial.print("Infix: ");
Serial.println(infix);
String postfix = infixToPostfix(infix);
Serial.print("Postfix: ");
Serial.println(postfix);
double result = evaluatePostfix(postfix);
Serial.print("Result: ");
Serial.println(result, 15); // Print result with 15 decimal places for precision
Serial.println("Enter another infix expression:");
}
}
String addZeroBeforeUnaryMinus(const String& infix) {
String modifiedInfix = "";
bool lastWasOperatorOrStart = true;
for (unsigned int i = 0; i < infix.length(); ++i) {
char ch = infix[i];
if ((ch == '-' || ch == '+') && lastWasOperatorOrStart) {
if (ch == '-') {
modifiedInfix += "(0-";
} else {
continue;
}
i++;
while (i < infix.length() && (isdigit(infix[i]) || infix[i] == '.')) {
modifiedInfix += infix[i];
i++;
}
i--;
modifiedInfix += ")";
} else {
modifiedInfix += ch;
}
lastWasOperatorOrStart = isOperator(ch) || ch == '(';
}
return modifiedInfix;
}
String infixToPostfix(String infix) {
infix = addZeroBeforeUnaryMinus(infix);
CharStack operatorStack(100);
String postfix = "";
bool expectOperand = true;
for (int i = 0; i < infix.length(); i++) {
char currentChar = infix[i];
String matchedFunction = matchFunction(infix, i);
if (matchedFunction != "") {
operatorStack.push(matchedFunction);
expectOperand = true;
}
else if (infix.substring(i, i + 2) == "pi") {
postfix += String(PIii, 15); // Add pi constant with double precision
postfix += " ";
i += 1;
expectOperand = false;
} else if (infix[i] == 'e') {
postfix += String(E, 15); // Add Euler's constant with double precision
postfix += " ";
expectOperand = false;
}
else if (currentChar == '!') {
postfix += "! ";
}
else if (isdigit(currentChar) || (currentChar == '-' && expectOperand)) {
if (currentChar == '-') {
postfix += '0';
postfix += ' ';
}
postfix += currentChar;
while (i + 1 < infix.length() && (isdigit(infix[i + 1]) || infix[i + 1] == '.')) {
postfix += infix[++i];
}
postfix += ' ';
expectOperand = false;
} else if (isOperator(currentChar)) {
while (!operatorStack.isEmpty() && precedence(operatorStack.peek()) >= precedence(String(currentChar))) {
postfix += operatorStack.pop();
postfix += ' ';
}
operatorStack.push(String(currentChar));
expectOperand = true;
} else if (currentChar == '(') {
operatorStack.push(String(currentChar));
expectOperand = true;
} else if (currentChar == ')') {
while (!operatorStack.isEmpty() && operatorStack.peek() != "(") {
postfix += operatorStack.pop();
postfix += ' ';
}
operatorStack.pop();
expectOperand = false;
}
}
while (!operatorStack.isEmpty()) {
postfix += operatorStack.pop();
postfix += ' ';
}
postfix.trim();
return postfix;
}
int precedence(String op) {
if (op == "+" || op == "-") return 1;
if (op == "*" || op == "/" || op == "%") return 2;
if (op == "^") return 3;
if (op == "sin" || op == "cos" || op == "tan" || op == "sind" || op == "cosd" || op == "tand" || op == "asin" || op == "acos" || op == "atan" || op == "ln" || op == "log" || op == "exp" || op == "antilog" || op == "sinh" || op == "cosh" || op == "tanh" || op == "sqrt" || op == "asinh" || op == "acosh" || op == "atanh") return 4;
if (op == "!") return 5;
return -1;
}
bool isOperator(char c) {
return c == '+' || c == '-' || c == '*' || c == '/' || c == '%' || c == '^' || c == '!';
}
double convertToRadians(double angle) {
return angle * (PIii / 180.0);
}
double evaluatePostfix(const String& postfix) {
FloatStack stack(50);
int i = 0;
while (i < postfix.length()) {
if (postfix[i] == ' ') {
i++;
continue;
}
if (isOperator(postfix[i])) {
double val2, val1;
switch (postfix[i]) {
case '+':
val2 = stack.pop();
val1 = stack.pop();
stack.push(val1 + val2);
break;
case '-':
val2 = stack.pop();
val1 = stack.pop();
stack.push(val1 - val2);
break;
case '*':
val2 = stack.pop();
val1 = stack.pop();
stack.push(val1 * val2);
break;
case '/':
val2 = stack.pop();
val1 = stack.pop();
stack.push(val1 / val2);
break;
case '^':
val2 = stack.pop();
val1 = stack.pop();
stack.push(power(val1, val2));
break;
case '!':
val1 = stack.pop();
stack.push(factorial((int)val1));
break;
}
} else if (isdigit(postfix[i]) || postfix[i] == '.') {
String numStr = "";
while (i < postfix.length() && (isdigit(postfix[i]) || postfix[i] == '.')) {
numStr += postfix[i++];
}
stack.push(numStr.toDouble());
continue;
}
// Handle inverse hyperbolic functions
else if (postfix.substring(i, i + 5) == "asinh") {
stack.push(my_asinh(stack.pop())); // Inverse hyperbolic sine
i += 5;
} else if (postfix.substring(i, i + 5) == "acosh") {
stack.push(my_acosh(stack.pop())); // Inverse hyperbolic cosine
i += 5;
} else if (postfix.substring(i, i + 5) == "atanh") {
stack.push(my_atanh(stack.pop())); // Inverse hyperbolic tangent
i += 5;
}
// Add support for sqrt
else if (postfix.substring(i, i + 4) == "sqrt") {
stack.push(sqrt(stack.pop())); // Calculate the square root
i += 4;
}
else if (postfix.substring(i, i + 4) == "sinh") {
stack.push(sinh(stack.pop()));
i += 4;
} else if (postfix.substring(i, i + 4) == "cosh") {
stack.push(cosh(stack.pop()));
i += 4;
} else if (postfix.substring(i, i + 4) == "tanh") {
stack.push(tanh(stack.pop()));
i += 4;
}
else if (postfix.substring(i, i + 4) == "sind") {
double degrees = stack.pop();
double radians = convertToRadians(degrees);
stack.push(sin(radians));
i += 4;
} else if (postfix.substring(i, i + 4) == "cosd") {
double degrees = stack.pop();
double radians = convertToRadians(degrees);
stack.push(cos(radians));
i += 4;
} else if (postfix.substring(i, i + 4) == "tand") {
double degrees = stack.pop();
double radians = convertToRadians(degrees);
stack.push(tan(radians));
i += 4;
}
else if (postfix.substring(i, i + 3) == "sin") {
stack.push(useRadians ? sin(stack.pop()) : sin(convertToRadians(stack.pop())));
i += 3;
} else if (postfix.substring(i, i + 3) == "cos") {
stack.push(useRadians ? cos(stack.pop()) : cos(convertToRadians(stack.pop())));
i += 3;
} else if (postfix.substring(i, i + 3) == "tan") {
stack.push(useRadians ? tan(stack.pop()) : tan(convertToRadians(stack.pop())));
i += 3;
}
else if (postfix.substring(i, i + 4) == "asin") {
double value = asin(stack.pop());
stack.push(useRadians ? value : convertToDegrees(value));
i += 4;
} else if (postfix.substring(i, i + 4) == "acos") {
double value = acos(stack.pop());
stack.push(useRadians ? value : convertToDegrees(value));
i += 4;
} else if (postfix.substring(i, i + 4) == "atan") {
double value = atan(stack.pop());
stack.push(useRadians ? value : convertToDegrees(value));
i += 4;
} else if (postfix.substring(i, i + 2) == "ln") {
double value = stack.pop();
stack.push(log(value));
i += 2;
} else if (postfix.substring(i, i + 3) == "log") {
double value = stack.pop();
stack.push(log10(value));
i += 3;
} else if (postfix.substring(i, i + 3) == "exp") {
double value = stack.pop();
stack.push(exp(value));
i += 3;
} else if (postfix.substring(i, i + 7) == "antilog") {
double value = stack.pop();
stack.push(pow(10, value));
i += 7;
}
i++;
}
return stack.pop();
}