Added support for complex numbers and added logarithm function
dc / build (push) Successful in 18s Details

This commit is contained in:
Marco Cetica 2024-04-19 16:14:47 +02:00
parent f3a83580e8
commit 9d5aeebef3
Signed by: marco
GPG Key ID: 45060A949E90D0FD
22 changed files with 567 additions and 20 deletions

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.12)
# Project name and version
project(dc VERSION 1.0.5)
project(dc VERSION 1.0.6)
# Retrieve build date
string(TIMESTAMP BUILD_DATE "%d-%b-%Y %H:%M:%S")

View File

@ -1,8 +1,8 @@
# dc ![](https://github.com/ceticamarco/dc/actions/workflows/dc.yml/badge.svg) [![CodeFactor](https://www.codefactor.io/repository/github/ceticamarco/dc/badge/master)](https://www.codefactor.io/repository/github/ceticamarco/dc/overview/master)
**dc** is an advanced, scientific and programmable RPN desktop calculator with macro support (re)written in C++.
By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions.
Its capabilities can be further extended by writing user-defined programs using the embedded, turing-complete, macro system.
By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions and
its capabilities can be further extended by writing user-defined programs using the embedded, turing-complete, macro system.
**dc** reads from the standard input, but it can also work with text files using the `-f` flag. Furthermore, you can decide to evaluate an expression
without opening the REPL by using the `-e` flag.
@ -23,7 +23,9 @@ RPN desktop calculator with macro support. Usage:
Some of the supported features are:
- Basic arithmetical operations(`+`, `-`, `*`, `/`, `^`, `%`);
- Scientific notation support(`5e3` -> `5000`);
- Support for complex numbers(`-1 v` -> `(0,1)`);
- Trigonometrical functions(`sin`, `cos`, `tan`, `asin`, `acos`, `atan`);
- Base-10 logarithm(`y`);
- Statistical functions(permutations, combinations, summation, sum of squares, mean, standard deviation, linear regression);
- Base conversion(binary: `pb`, octal: `po`, hexadecimal: `px`);
- Factorial and constants(`!`, `pi`, `e`);
@ -233,8 +235,7 @@ $$x_{1,2} = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$$
#!/usr/local/bin/dc -f
# GIVEN A QUADRATIC EQUATION OF THE FORM
# AX^2 + BX + C = 0
# COMPUTE ITS REAL ROOTS
# THIS PROGRAM DOES NOT WORK WITH CMPLX NUMBERS
# COMPUTE ITS REAL OR COMPLEX ROOTS
# DEVELOPED BY MARCO CETICA 2023
#
3 k

67
man.md
View File

@ -3,7 +3,7 @@ title: dc
section: 1
header: General Commands Manual
footer: Marco Cetica
date: April 8, 2024
date: April 19, 2024
---
@ -21,8 +21,8 @@ RPN desktop calculator with macro support. Usage:
# DESCRIPTION
**dc** is an advanced, scientific and programmable RPN desktop calculator with macro support (re)written in C++.
By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions.
Its capabilities can be further extended by writing user-defined programs using the embedded, turing-complete, macro system.
By default, dc supports a wide range of arithmetical, trigonometrical and numeric functions and
its capabilities can be further extended by writing user-defined programs using the embedded, turing-complete, macro system.
**dc** uses the reverse polish notation(**RPN**) to parse mathematical expressions. Unlike the infix notation, where operators
are placed _between_ operands, the polish notation(also called prefix notation) places operators _before_ the operands. The **reverse**
@ -198,6 +198,10 @@ The random value is generated using a 64-bit Mersenne Twister pseudorandom numbe
Pops one value from the stack and convert it to the nearest integer of lesser magnitute.
**y**
Pops one value from the stack and compute the _common_(base-10) logarithm of that number.
## Trigonometrical
**sin**
@ -224,6 +228,61 @@ Pops one value, computes its `acos`, and pushes that.
Pops one value, computes its `atan`, and pushes that.
## Complex Numbers
**dc** allows you to operate on complex numbers in an easy and straightforward way:
most arithmetical functions that work with real numbers also work with complex numbers. Complex
numbers are stored on the stack just like any other real, rational, integer or natural number. To enter
a new complex number of the form
$$
z = a + ib
$$
write the _real_ part($a$) into the _second-to-top_ of the stack, the _imaginary_ part($b$) into the `x` _head_ of the stack and then issue the `b` command. For example, to enter $(9 + 4i)$, write:
```
9 4 b p
(9,4)
```
As you can see, complex numbers are represented as a tuple $(Re,Im)$ and can be printed just like
any other `dc` type.
You can then operate on them just like any other number. Consider the following example:
```
(6 + 4i) - (5 - 2i)
-------------------
(7 + i)^2
```
This can easily be calculated using the following sequence of commands:
```
10 k
6 4 b 5 -2 b - 7 1 b 2 ^ / p
(0.0528000000,0.1096000000)
```
And if you want to extract only the real or imaginary part of the number, issue one of the
following command, respectively:
```
re p .x im p
0.0528000000
0.1096000000
```
That is:
**re**
Pops one value, if it is a complex number retrieve its real part and pushes that.
**im**
Pops one value, if it is a complex number retrieve its imaginary part and pushes that.
## Statistics Operations
**dc** supports various common statistics operations, such as permutations, combinations, mean, standard deviation,
summation, sum of squares and linear regression. Most statistics functions are limited to **non-negative integers**.
@ -233,7 +292,7 @@ the `Y` registers.
**gP**
Pops two non-negative integers(that is, >=0) and computes `P_{y, x}`, that is the number of possible different arrangements of
`y` different items taken in quantities of `x` items at a time. No item shall occur more than once in an arrangmement and different orders of same `x`
`y` different items taken in quantities of `x` items at a time. No item shall occur more than once in an arrangement and different orders of same `x`
items are counted separately. The `y` parameter correspond to the second one value popped while the `x` parameter correspond to the first one popped.
**gC**

View File

@ -45,6 +45,10 @@ void Evaluate::init_environment() {
this->op_factory.emplace("e", MAKE_UNIQUE_PTR(Mathematics, OPType::E));
this->op_factory.emplace("@", MAKE_UNIQUE_PTR(Mathematics, OPType::RND));
this->op_factory.emplace("$", MAKE_UNIQUE_PTR(Mathematics, OPType::INT));
this->op_factory.emplace("b", MAKE_UNIQUE_PTR(Mathematics, OPType::TO_CMPLX));
this->op_factory.emplace("re", MAKE_UNIQUE_PTR(Mathematics, OPType::GET_RE));
this->op_factory.emplace("im", MAKE_UNIQUE_PTR(Mathematics, OPType::GET_IM));
this->op_factory.emplace("y", MAKE_UNIQUE_PTR(Mathematics, OPType::LOG));
// Statistical operations
this->op_factory.emplace("gP", MAKE_UNIQUE_PTR(Statistics, OPType::PERM));
this->op_factory.emplace("gC", MAKE_UNIQUE_PTR(Statistics, OPType::COMB));

View File

@ -1,5 +1,6 @@
#include <cmath>
#include <numbers>
#include <complex>
#include <iomanip>
#include <random>
@ -31,6 +32,10 @@ std::optional<std::string> Mathematics::exec(dc::Stack<std::string> &stack, dc::
case OPType::E: err = fn_e(stack, parameters); break;
case OPType::RND: err = fn_random(stack, parameters); break;
case OPType::INT: err = fn_integer(stack, parameters); break;
case OPType::TO_CMPLX: err = fn_to_complex(stack, parameters); break;
case OPType::GET_RE: err = fn_get_real(stack, parameters); break;
case OPType::GET_IM: err = fn_get_imaginary(stack, parameters); break;
case OPType::LOG: err = fn_log10(stack, parameters); break;
default: break;
}
@ -49,6 +54,8 @@ std::optional<std::string> Mathematics::fn_add(dc::Stack<std::string> &stack, co
auto y = stack[len-1];
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
auto is_x_cmplx = is_complex(x);
auto is_y_cmplx = is_complex(y);
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
@ -58,6 +65,23 @@ std::optional<std::string> Mathematics::fn_add(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits((lhs + rhs), parameters.precision));
} else if(is_x_cmplx || is_y_cmplx) {
stack.copy_xyz();
// Convert complex dc objects(ie strings) to std::complex
std::complex<double> rhs, lhs;
std::istringstream ss1(stack.pop(true));
std::istringstream ss2(stack.pop(true));
ss1 >> rhs, ss2 >> lhs;
std::complex<double> sum = (rhs + lhs);
// trim their digits
auto real_trimmed = trim_digits(sum.real(), parameters.precision);
auto imag_trimmed = trim_digits(sum.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'+' requires numeric values";
}
@ -77,6 +101,8 @@ std::optional<std::string> Mathematics::fn_sub(dc::Stack<std::string> &stack, co
auto y = stack[len-1];
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
auto is_x_cmplx = is_complex(x);
auto is_y_cmplx = is_complex(y);
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
@ -96,6 +122,23 @@ std::optional<std::string> Mathematics::fn_sub(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits(result, parameters.precision));
} else if(is_x_cmplx || is_y_cmplx) {
stack.copy_xyz();
// Convert complex dc objects(ie strings) to std::complex
std::complex<double> rhs, lhs;
std::istringstream ss1(stack.pop(true));
std::istringstream ss2(stack.pop(true));
ss1 >> rhs, ss2 >> lhs;
std::complex<double> diff = (lhs - rhs);
// trim their digits
auto real_trimmed = trim_digits(diff.real(), parameters.precision);
auto imag_trimmed = trim_digits(diff.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'-' requires numeric values";
}
@ -115,6 +158,8 @@ std::optional<std::string> Mathematics::fn_mul(dc::Stack<std::string> &stack, co
auto y = stack[len-1];
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
auto is_x_cmplx = is_complex(x);
auto is_y_cmplx = is_complex(y);
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
@ -124,6 +169,23 @@ std::optional<std::string> Mathematics::fn_mul(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits((lhs * rhs), parameters.precision));
} else if(is_x_cmplx || is_y_cmplx) {
stack.copy_xyz();
// Convert complex dc objects(ie strings) to std::complex
std::complex<double> rhs, lhs;
std::istringstream ss1(stack.pop(true));
std::istringstream ss2(stack.pop(true));
ss1 >> rhs, ss2 >> lhs;
std::complex<double> mul = (lhs * rhs);
// trim their digits
auto real_trimmed = trim_digits(mul.real(), parameters.precision);
auto imag_trimmed = trim_digits(mul.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'*' requires numeric values";
}
@ -143,6 +205,8 @@ std::optional<std::string> Mathematics::fn_div(dc::Stack<std::string> &stack, co
auto y = stack[len-1];
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
auto is_x_cmplx = is_complex(x);
auto is_y_cmplx = is_complex(y);
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
@ -157,6 +221,28 @@ std::optional<std::string> Mathematics::fn_div(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits((dividend / divisor), parameters.precision));
} else if(is_x_cmplx || is_y_cmplx) {
stack.copy_xyz();
// Convert complex dc objects(ie strings) to std::complex
std::complex<double> divisor, dividend;
std::istringstream ss1(stack.pop(true));
std::istringstream ss2(stack.pop(true));
ss1 >> divisor, ss2 >> dividend;
// Check whether divisor is equal to zero
if(divisor == 0.0) {
return "Cannot divide by zero";
}
std::complex<double> div = (dividend / divisor);
// trim their digits
auto real_trimmed = trim_digits(div.real(), parameters.precision);
auto imag_trimmed = trim_digits(div.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'/' requires numeric values";
}
@ -293,6 +379,8 @@ std::optional<std::string> Mathematics::fn_exp(dc::Stack<std::string> &stack, co
auto y = stack[len-1];
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
auto is_x_cmplx = is_complex(x);
auto is_y_cmplx = is_complex(y);
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
@ -302,6 +390,23 @@ std::optional<std::string> Mathematics::fn_exp(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits(pow(base, exp), parameters.precision));
} else if(is_x_cmplx || is_y_cmplx) {
stack.copy_xyz();
// Convert complex dc objects(ie strings) to std::complex
std::complex<double> exp, base;
std::istringstream ss1(stack.pop(true));
std::istringstream ss2(stack.pop(true));
ss1 >> exp, ss2 >> base;
std::complex<double> power = std::pow(base, exp);
// trim their digits
auto real_trimmed = trim_digits(power.real(), parameters.precision);
auto imag_trimmed = trim_digits(power.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'^' requires numeric values";
}
@ -319,18 +424,31 @@ std::optional<std::string> Mathematics::fn_sqrt(dc::Stack<std::string> &stack, c
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_y_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
if(is_x_num || is_y_cmplx) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
auto val = stack.pop(true);
if(val <= 0.0) {
return "'v' domain error";
if(is_complex(val) || std::stod(val) < 0) {
std::complex<double> c_val;
std::istringstream ss1(val);
ss1 >> c_val;
std::complex<double> sq = std::sqrt(c_val);
// trim their digits
auto real_trimmed = trim_digits(sq.real(), parameters.precision);
auto imag_trimmed = trim_digits(sq.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
stack.push(trim_digits(sqrt(std::stod(val)), parameters.precision));
}
// Push back the result as a string
stack.push(trim_digits(sqrt(val), parameters.precision));
} else {
return "'v' requires numeric values";
}
@ -348,6 +466,7 @@ std::optional<std::string> Mathematics::fn_sin(dc::Stack<std::string> &stack, co
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -356,6 +475,21 @@ std::optional<std::string> Mathematics::fn_sin(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits(sin(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = sin(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'sin' requires numeric values";
}
@ -373,6 +507,7 @@ std::optional<std::string> Mathematics::fn_cos(dc::Stack<std::string> &stack, co
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -381,6 +516,21 @@ std::optional<std::string> Mathematics::fn_cos(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits(cos(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = cos(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'cos' requires numeric values";
}
@ -398,6 +548,7 @@ std::optional<std::string> Mathematics::fn_tan(dc::Stack<std::string> &stack, co
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -406,6 +557,21 @@ std::optional<std::string> Mathematics::fn_tan(dc::Stack<std::string> &stack, co
// Push back the result as a string
stack.push(trim_digits(tan(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = tan(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'tan' requires numeric values";
}
@ -423,6 +589,7 @@ std::optional<std::string> Mathematics::fn_asin(dc::Stack<std::string> &stack, c
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -431,6 +598,21 @@ std::optional<std::string> Mathematics::fn_asin(dc::Stack<std::string> &stack, c
// Push back the result as a string
stack.push(trim_digits(asin(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = asin(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'asin' requires numeric values";
}
@ -448,6 +630,7 @@ std::optional<std::string> Mathematics::fn_acos(dc::Stack<std::string> &stack, c
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -456,6 +639,21 @@ std::optional<std::string> Mathematics::fn_acos(dc::Stack<std::string> &stack, c
// Push back the result as a string
stack.push(trim_digits(acos(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = acos(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'acos' requires numeric values";
}
@ -473,6 +671,7 @@ std::optional<std::string> Mathematics::fn_atan(dc::Stack<std::string> &stack, c
auto len = stack.size()-1;
auto x = stack[len];
auto is_x_num = is_num<double>(x);
auto is_x_cmplx = is_complex(x);
// Check whether the entry is a number
if(is_x_num) {
@ -481,6 +680,21 @@ std::optional<std::string> Mathematics::fn_atan(dc::Stack<std::string> &stack, c
// Push back the result as a string
stack.push(trim_digits(atan(val), parameters.precision));
} else if(is_x_cmplx) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> s = atan(c_val);
// trim their digits
auto real_trimmed = trim_digits(s.real(), parameters.precision);
auto imag_trimmed = trim_digits(s.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'atan' requires numeric values";
}
@ -590,6 +804,41 @@ std::optional<std::string> Mathematics::fn_integer(dc::Stack<std::string> &stack
return std::nullopt;
}
std::optional<std::string> Mathematics::fn_to_complex(dc::Stack<std::string> &stack, const dc::Parameters &parameters) {
// Check if stack has enough elements
if(stack.size() < 2) {
return "'b' requires two values";
}
auto len = stack.size()-1;
auto x = stack.at(len);
auto y = stack.at(len-1);
auto is_x_num = is_num<double>(x);
auto is_y_num = is_num<double>(y);
// Check whether both values are numbers
if(is_x_num && is_y_num) {
// Extract the real and imaginary part of the complex number
auto imag = std::stod(stack.pop(true));
auto real = std::stod(stack.pop(true));
// trim their digits
auto real_trimmed = trim_digits(real, parameters.precision);
auto imag_trimmed = trim_digits(imag, parameters.precision);
// Complex numbers are represented on the stack as "(Re,Im)"
// They are then converted to std::complex before computations
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
stack.push(complex_str);
} else {
return "'b' requires numeric values";
}
return std::nullopt;
}
std::string Mathematics::trim_digits(double number, unsigned int precision) {
std::ostringstream oss;
@ -603,3 +852,109 @@ std::string Mathematics::trim_digits(double number, unsigned int precision) {
return s;
}
std::optional<std::string> Mathematics::fn_get_real(dc::Stack<std::string> &stack, const dc::Parameters &parameters) {
// Check if stack has enough elements
if(stack.empty()) {
return "'re' requires one value";
}
auto head = stack.pop(false);
auto is_head_complex = is_complex(head);
if(is_head_complex) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
// Get real part
auto real = c_val.real();
// Push the result back onto the stack
stack.push(trim_digits(real, parameters.precision));
} else {
return "'re' requires complex values";
}
return std::nullopt;
}
std::optional<std::string> Mathematics::fn_get_imaginary(dc::Stack<std::string> &stack, const dc::Parameters &parameters) {
// Check if stack has enough elements
if(stack.empty()) {
return "'im' requires one value";
}
auto head = stack.pop(false);
auto is_head_complex = is_complex(head);
if(is_head_complex) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
// Get imaginary part
auto imag = c_val.imag();
// Push the result back onto the stack
stack.push(trim_digits(imag, parameters.precision));
} else {
return "'im' requires complex values";
}
return std::nullopt;
}
std::optional<std::string> Mathematics::fn_log10(dc::Stack<std::string> &stack, const dc::Parameters &parameters) {
// Check if stack has enough elements
if(stack.empty()) {
return "'y' requires one value";
}
auto head = stack.pop(false);
auto is_head_num = is_num<double>(head);
auto is_head_complex = is_complex(head);
if(is_head_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
stack.push(trim_digits(log10(val), parameters.precision));
} else if(is_head_complex) {
stack.copy_xyz();
std::complex<double> c_val;
std::istringstream ss1(stack.pop(true));
ss1 >> c_val;
std::complex<double> lg = log(c_val);
// trim their digits
auto real_trimmed = trim_digits(lg.real(), parameters.precision);
auto imag_trimmed = trim_digits(lg.imag(), parameters.precision);
auto complex_str = ('(' + real_trimmed + ',' + imag_trimmed + ')');
// Push the result back onto the stack
stack.push(complex_str);
} else {
return "'y' requires numeric values";
}
return std::nullopt;
}
bool Mathematics::is_complex(const std::string &str) {
// Complex numbers are represented on the stack as "(Re,Im)"
if(str.front() != '(' || str.back() != ')') {
return false;
}
// Extract "Re,Im" without parenthesis
std::istringstream ss(str.substr(1, str.size() - 2));
double real, imag;
char comma;
return (ss >> real >> comma >> imag) && ss.eof();
}

View File

@ -28,7 +28,12 @@ private:
static std::optional<std::string> fn_e(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_random(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_integer(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_to_complex(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_get_real(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_get_imaginary(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::optional<std::string> fn_log10(dc::Stack<std::string> &stack, const dc::Parameters &parameters);
static std::string trim_digits(double number, unsigned int precision);
static bool is_complex(const std::string &str);
OPType op_type;
};

View File

@ -13,7 +13,7 @@ enum class OPType {
// Numerical operations
ADD, SUB, MUL, DIV, MOD, DIV_MOD, MOD_EXP, EXP,
SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, FACT, PI,
E, RND, INT,
E, RND, INT, TO_CMPLX, GET_RE, GET_IM, LOG,
// Statistical operations
PERM, COMB, SUMX, SUMXX, MEAN, SDEV, LREG,
// Bitwise operations

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'acos' requires numeric values"
ACTUAL=$("$PROGRAM" -e '[ foo ] acos' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(0,3.14)"
ACTUAL=$("$PROGRAM" -e '11.57 -0 b acos p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'asin' requires numeric values"
ACTUAL=$("$PROGRAM" -e '[ foo ] asin' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(0,3.14)"
ACTUAL=$("$PROGRAM" -e '0 11.53 b asin p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'atan' requires numeric values"
ACTUAL=$("$PROGRAM" -e '[ foo ] atan' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(0,inf)"
ACTUAL=$("$PROGRAM" -e '0 1 b atan p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'cos' requires numeric values"
ACTUAL=$("$PROGRAM" -e '[ foo ] cos' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(11.57,-0)"
ACTUAL=$("$PROGRAM" -e '0 pi b cos p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -20,5 +20,10 @@ utest() {
EXPECTED="Cannot divide by zero"
ACTUAL=$("$PROGRAM" -e '5 0 /' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(2.20,1.60)"
ACTUAL=$("$PROGRAM" -e '5 7 b 3 1 b / p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'^' requires numeric values"
ACTUAL=$("$PROGRAM" -e '5 [ foo ] ^' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(-1,0.00)"
ACTUAL=$("$PROGRAM" -e '0 1 b 2 ^ p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

24
tests/test_im Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
EXPECTED="3.14"
ACTUAL=$("$PROGRAM" -e '1 pi b im p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test empty stack
EXPECTED="'im' requires one value"
ACTUAL=$("$PROGRAM" -e 'im' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test non complex values
EXPECTED="'im' requires complex values"
ACTUAL=$("$PROGRAM" -e '1 im' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test with macro
EXPECTED="'im' requires complex values"
ACTUAL=$("$PROGRAM" -e '[ foo ] im' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

24
tests/test_log Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
EXPECTED="2"
ACTUAL=$("$PROGRAM" -e '100 y p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test empty stack
EXPECTED="'y' requires one value"
ACTUAL=$("$PROGRAM" -e 'y' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test non numerical values
EXPECTED="'y' requires numeric values"
ACTUAL=$("$PROGRAM" -e '5 [ foo ] y' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(0,3.14)"
ACTUAL=$("$PROGRAM" -e '0 1 b y 2 * p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -16,5 +16,9 @@ utest() {
ACTUAL=$("$PROGRAM" -e '[ foo ] 5 *' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(13,-18)"
ACTUAL=$("$PROGRAM" -e '2 -5 b 4 1 b * p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

24
tests/test_re Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
EXPECTED="1"
ACTUAL=$("$PROGRAM" -e '1 0 b re p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test empty stack
EXPECTED="'re' requires one value"
ACTUAL=$("$PROGRAM" -e 're' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test non complex values
EXPECTED="'re' requires complex values"
ACTUAL=$("$PROGRAM" -e '1 re' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test with macro
EXPECTED="'re' requires complex values"
ACTUAL=$("$PROGRAM" -e '[ foo ] re' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -16,5 +16,9 @@ utest() {
ACTUAL=$("$PROGRAM" -e '[ foo ] sin' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(0,11.53)"
ACTUAL=$("$PROGRAM" -e '0 pi b sin p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -16,9 +16,9 @@ utest() {
ACTUAL=$("$PROGRAM" -e '[ foo ] v' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test domain error
EXPECTED="'v' domain error"
ACTUAL=$("$PROGRAM" -e '-1 v' 2>&1) || true
# Test complex number
EXPECTED="(0,1)"
ACTUAL=$("$PROGRAM" -e '-1 v p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -16,5 +16,9 @@ utest() {
ACTUAL=$("$PROGRAM" -e '[ foo ] 5 -' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(-2,-6)"
ACTUAL=$("$PROGRAM" -e '2 -5 b 4 1 b - p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -16,5 +16,9 @@ utest() {
ACTUAL=$("$PROGRAM" -e '[ foo ] 5 +' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex values
EXPECTED="(6,-4)"
ACTUAL=$("$PROGRAM" -e '2 -5 b 4 1 b + p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

View File

@ -15,5 +15,10 @@ utest() {
EXPECTED="'tan' requires numeric values"
ACTUAL=$("$PROGRAM" -e '[ foo ] tan' 2>&1) || true
assert_eq "$EXPECTED" "$ACTUAL"
# Test complex number
EXPECTED="(0,1.00)"
ACTUAL=$("$PROGRAM" -e '0 pi b tan p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab: