A paper is proposing that most elementary functions such as sin, cos, 1/x can be expressed as a combination of the following function and the constant 1.
For example ln(x) can be written as eml(1, eml(eml(1, x), 1)). Let’s do a few experiments with this.
A Basic Test
Let’s write the EML function.
#include <iostream>
#include <cmath>
double eml(double x, double y) {
return std::exp(x) - std::log(y);
}
We can now calculate and print ln(x) using the EML function.
int main() {
double x = 10.23189; //Sample input
double r = eml(1, eml(eml(1, x), 1));
double expected = std::log(x);
std::cout << r << " " << expected << std::endl;
return 0;
}
This should print.
An RPN Expression Evaluator
We can express the EML combination for ln(x) using the RPN notation 11xE1EE where E is the EML function operator. Let’s write a C++ function that evaluates such an RPN expression.
#include <stack>
bool eval_rpn(const char* rpn, double x, double& result) {
std::stack<double> s;
while (*rpn != '\0') {
if (*rpn == '1') {
s.push(1.0);
} else if (*rpn == 'x') {
s.push(x);
} else if (*rpn == 'E') {
//Pop the last two values
if (s.size() < 2) {
return false; //Invalid expression
}
double vy = s.top();
s.pop();
double vx = s.top();
s.pop();
//Push the EML result
s.push(eml(vx, vy));
} else {
return false; //Invalid expression
}
++rpn;
}
if (s.size() != 1) {
return false;
}
result = s.top();
return true;
}
We can now use this function.
int main() {
double x = 10.23189; //Sample input
double expected = std::log(x);
double r;
if (eval_rpn("11xE1EE", x, r)) {
std::cout << r << " " << expected << std::endl;
} else {
std::cout << "RPN evaluation failed." << std::endl;
}
return 0;
}
A Few More Examples
We can write a generic function that evaluates an RPN expression and prints out the result next to the expected values.
void run_test(double x, double expected, const char* rpn) {
double r;
if (eval_rpn(rpn, x, r)) {
std::cout << r << " " << expected << std::endl;
} else {
std::cout << "RPN evaluation failed." << std::endl;
}
}
We can now test out a few elimentary functions.
int main() {
//ln(x)
run_test(100.23189, std::log(100.23189), "11xE1EE");
//x
run_test(89.12, 89.12, "11x1EE1EE");
//-x
run_test(89.12, -89.12, "11111E1EEEEx1EE");
//1/x
run_test(15.0, 1/15.0, "11111E1EEEExE1E");
return 0;
}
Binary Operations
We can make small changes to support binary operations like x * y.
bool eval_rpn(const char* rpn, double x, double y, double& result) {
std::stack<double> s;
while (*rpn != '\0') {
if (*rpn == '1') {
s.push(1.0);
} else if (*rpn == 'x') {
s.push(x);
} else if (*rpn == 'y') {
s.push(y);
} else if (*rpn == 'E') {
//Pop the last two values
if (s.size() < 2) {
return false; //Invalid expression
}
double vy = s.top();
s.pop();
double vx = s.top();
s.pop();
//Push the EML result
s.push(eml(vx, vy));
} else {
return false; //Invalid expression
}
++rpn;
}
if (s.size() != 1) {
return false;
}
result = s.top();
return true;
}
void run_test(double x, double y, double expected, const char* rpn) {
double r;
if (eval_rpn(rpn, x, y, r)) {
std::cout << r << " " << expected << std::endl;
} else {
std::cout << "RPN evaluation failed." << std::endl;
}
}
int main() {
//x * y
run_test(15.0, 2.0, 15.0 * 2.0, "1111xEE1EE11y1EE1EEE1EE1E");
return 0;
}