Added support for complex numbers and added logarithm function
dc / build (push) Successful in 18s
Details
dc / build (push) Successful in 18s
Details
This commit is contained in:
parent
f3a83580e8
commit
9d5aeebef3
|
@ -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")
|
||||
|
|
|
@ -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
67
man.md
|
@ -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**
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 ¶meters) {
|
||||
// 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 ¶meters) {
|
||||
// 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 ¶meters) {
|
||||
// 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 ¶meters) {
|
||||
// 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();
|
||||
}
|
|
@ -28,7 +28,12 @@ private:
|
|||
static std::optional<std::string> fn_e(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_random(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_integer(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_to_complex(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_get_real(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_get_imaginary(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::optional<std::string> fn_log10(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||
static std::string trim_digits(double number, unsigned int precision);
|
||||
static bool is_complex(const std::string &str);
|
||||
|
||||
OPType op_type;
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue