Added 'lastX', 'lastY', 'lastZ' operations and their tests
dc / build (push) Successful in 1m2s Details

This commit is contained in:
Marco Cetica 2024-03-12 11:50:13 +01:00
parent aebfec4fb3
commit 674d6deef6
Signed by: marco
GPG Key ID: 45060A949E90D0FD
13 changed files with 242 additions and 19 deletions

View File

@ -33,6 +33,7 @@ Some of the supported features are:
- Swap order of top two elements(`r`);
- Duplicate top element(`d`);
- Dump the whole stack(`f`);
- Last head, 2nd, 3rd element of the stack(`.x`, `.y`, `.z`);
- Parameters:
- Set precision(`k`);
- Set input and output radix(`i` and `o`);
@ -151,7 +152,15 @@ lA lB # Push 'A' and 'B' content onto the stack
p # Print top element(output: 5)
```
7. Print out numbers from 1 through user-defined upper bound:
7. Recall last value of top three elements of the stack:
```sh
10 1 / # <- Wrong operation: division instead of sum
.y # Call last value of y register
.x # Call last value of x register
+ p # Sum and print head(output: 11)
```
8. Print out numbers from 1 through user-defined upper bound:
```sh
[ p 1 + d lN >L ] sL # Print numbers from 1 through 'N'
@ -160,20 +169,20 @@ p # Print top element(output: 5)
c 1 lL x # Clear the stack, add lower bound, load and execute macro
```
8. Sum the first 36 natural numbers(😈), i.e.,
9. Sum the first 36 natural numbers(😈), i.e.,
$$\sum_{i=1}^{37} i = 666$$
```sh
$> dc -e "36 [ d 1 - d 1 <F + ] d sF x p"
```
5. Print the first 20 values of `n!`:
10. Print the first 20 values of `n!`:
```
[ la 1 + d sa * p la 20 >y ] sy
0 sa 1
ly x
```
9. Compute the factorial of a given number:
11. Compute the factorial of a given number:
```
[ ln 1 - sn ln la * sa ln 1 !=f ] sf
[ Enter value: ] P ? sn
@ -182,7 +191,7 @@ lf x
la p
```
10. Compute the sum $8AB6F + B783E$ in base 16. Print the result in base 10 and in base 2:
12. Compute the sum $8AB6F + B783E$ in base 16. Print the result in base 10 and in base 2:
```
16 i
8AB6F B783E +
@ -190,7 +199,7 @@ la p
[ Result in base 2: ] P R pb
```
11. Compute the Greatest Common Divisor(GCD) between two user-defined numbers `A` and `B`:
13. Compute the Greatest Common Divisor(GCD) between two user-defined numbers `A` and `B`:
```
[ Enter A: ] P R ?
[ Enter B: ] P R ?
@ -198,7 +207,7 @@ la p
[ GCD(A,B)= ] P R p
```
12. Compute the Least Common Multiple(LCM) between two user-defined numbers `A` and `B`:
14. Compute the Least Common Multiple(LCM) between two user-defined numbers `A` and `B`:
```
[ Enter A: ] P R ? d sA
[ Enter B: ] P R ? d SA
@ -207,7 +216,7 @@ LA lA * r /
[ LCM(A,B)= ] P R p
```
13. Find the roots of a quadratic equation of the form:
15. Find the roots of a quadratic equation of the form:
$$ax^2 + bx + c = 0$$
with $$a,b,c \in \mathbb{R}, a \neq 0$$

19
man.md
View File

@ -3,7 +3,7 @@ title: dc
section: 1
header: General Commands Manual
footer: Marco Cetica
date: November 28, 2023
date: March 12, 2024
---
@ -91,7 +91,8 @@ register and the _value_ is a **private** instance of a stack and a **private**
cannot operate directly on the auxiliary stacks or on the auxiliary arrays. In order to use a value stored on an auxiliary stack, you need to pop it
and push it onto the main stack(see the register section).
Both the _main stack_ and the _auxiliary stack_ implement the same abstract data type, therefore any kind of data type supported by the main stack - as well as any other property or feature supported by the main stack - is also supported by the register's stack.
Both the _main stack_ and the _auxiliary stack_ implement the same abstract data type, therefore any kind of data type supported by the main
stack - as well as any other property or feature supported by the main stack - is also supported by the register's stack.
_Arrays_ are dynamic, homogeneous and private abstract data type associated with a register.
The underlying data type of a dc array is a hashmap where the index is represented by
@ -230,6 +231,20 @@ Reverses the order of the top two values of the stack. This can also be accompli
Pops the top-of-stack without printing it
**.x**
Retrieve the value that was last the top-of-stack *before execution of any operation*(stack, macro, arithmetical, etc.).
This function can be used to quickly key back numbers when you are recovering from errors, such as typing in the wrong number
or executing the wrong operation.
**.y**
Retrieve the value that was last the second-to-top of the stack *before execution of any operation*(stack, macro, arithmetical, etc.).
**.z**
Retrieve the value that was last the third-to-top of the stack *before execution of any operation*(stack, macro, arithmetical, etc.).
## Parameters
**dc** has three parameters that control its operation: the *precision*, the *input radix* and the *output radix*.
The precision specifies the number of fraction digits to keep in the result of most arithmetical operations. The

View File

@ -1,7 +1,11 @@
#include "adt.h"
#define GET_X this->stack.back()
#define GET_Y this->stack.at(this->stack.size() - 2)
#define GET_Z this->stack.at(this->stack.size() - 3)
/**
* Add new element to the stack
* Add @value to the stack
*/
template<typename T>
requires is_num_or_str<T>
@ -36,8 +40,59 @@ T DCStack<T>::pop(bool remove) {
return value;
}
/**
* Read stack at index
* Make a copy of head, 2nd and 3rd element
* of the stack
*/
template<typename T>
requires is_num_or_str<T>
void DCStack<T>::copy_xyz() {
if(this->stack.size() >= 1) {
this->last_x = GET_X;
}
if(this->stack.size() >= 2) {
this->last_y = GET_Y;
}
if(this->stack.size() >= 3) {
this->last_z = GET_Z;
}
}
/**
* Returns last x of the stack
* If stack is empty returns '0' or empty string
*/
template<typename T>
requires is_num_or_str<T>
T DCStack<T>::get_last_x() {
return this->last_x;
}
/**
* Returns last y of the stack
* If stack is empty returns '0' or empty string
*/
template<typename T>
requires is_num_or_str<T>
T DCStack<T>::get_last_y() {
return this->last_y;
}
/**
* Returns last z of the stack
* If stack is empty returns '0' or empty string
*/
template<typename T>
requires is_num_or_str<T>
T DCStack<T>::get_last_z() {
return this->last_z;
}
/**
* Reads stack at @index
*/
template<typename T>
requires is_num_or_str<T>
@ -73,4 +128,4 @@ template<typename T>
requires is_num_or_str<T>
const std::vector<T>& DCStack<T>::get_ref() const {
return this->stack;
}
}

View File

@ -11,10 +11,13 @@ template<typename T>
requires is_num_or_str<T>
class DCStack {
public:
DCStack() = default;
void push(T value);
void clear();
void copy_xyz();
T pop(bool remove);
T get_last_x();
T get_last_y();
T get_last_z();
T& at(size_t index);
size_t size();
bool empty();
@ -22,9 +25,9 @@ public:
private:
std::vector<T> stack;
T last_x;
T last_y;
T last_z;
T last_x{};
T last_y{};
T last_z{};
};
typedef struct {

View File

@ -59,6 +59,9 @@ void Evaluate::init_environment() {
this->op_factory.emplace("O", MAKE_UNIQUE_PTR(Stack, OPType::GOR));
this->op_factory.emplace("i", MAKE_UNIQUE_PTR(Stack, OPType::SIR));
this->op_factory.emplace("I", MAKE_UNIQUE_PTR(Stack, OPType::GIR));
this->op_factory.emplace(".x", MAKE_UNIQUE_PTR(Stack, OPType::LX));
this->op_factory.emplace(".y", MAKE_UNIQUE_PTR(Stack, OPType::LY));
this->op_factory.emplace(".z", MAKE_UNIQUE_PTR(Stack, OPType::LZ));
// Macro operations
this->op_factory.emplace("x", MAKE_UNIQUE_PTR(Macro, OPType::EX));
this->op_factory.emplace("?", MAKE_UNIQUE_PTR(Macro, OPType::RI));
@ -239,6 +242,7 @@ std::optional<std::string> Evaluate::parse_register_command(std::string token) {
// Otherwise pop an element from main stack and store it into
// the register's top-of-the-stack. Any previous value gets overwritten
this->stack.copy_xyz();
auto reg_name = token.at(1);
auto head = this->stack.pop(true);
@ -265,6 +269,7 @@ std::optional<std::string> Evaluate::parse_register_command(std::string token) {
return "This operation does not work on empty stack";
}
this->stack.copy_xyz();
auto reg_name = token.at(1);
auto head = this->stack.pop(true);
@ -334,6 +339,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string token) {
}
// Extract two elements from the main stack
this->stack.copy_xyz();
auto idx_str = this->stack.pop(true);
auto arr_val = this->stack.pop(true);
@ -369,6 +375,7 @@ std::optional<std::string> Evaluate::parse_array_command(std::string token) {
}
// Extract the index from the stack
this->stack.copy_xyz();
auto idx_str = this->stack.pop(true);
// Check if index is an integer

View File

@ -31,6 +31,7 @@ std::optional<std::string> Macro::fn_execute(DCStack<std::string> &stack, Parame
// pop it and execute it as a macro
auto head = stack.pop(false);
if(!is_num<double>(head)) {
stack.copy_xyz();
stack.pop(true);
std::vector<std::string> tokens = split(head);
Evaluate evaluator(tokens, regs, stack, parameters);
@ -56,6 +57,7 @@ std::optional<std::string> Macro::fn_evaluate_macro(DCStack<std::string> &stack,
}
// Extract macro and top two values of the stack
stack.copy_xyz();
auto head_str = stack.pop(true);
auto second_str = stack.pop(true);
auto dc_macro = regs[this->dc_register].stack.pop(false);

View File

@ -49,6 +49,7 @@ std::optional<std::string> Math::fn_add(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto lhs = std::stod(stack.pop(true));
auto rhs = std::stod(stack.pop(true));
@ -76,6 +77,7 @@ std::optional<std::string> Math::fn_sub(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto lhs = std::stod(stack.pop(true));
auto rhs = std::stod(stack.pop(true));
@ -113,6 +115,7 @@ std::optional<std::string> Math::fn_mul(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto lhs = std::stod(stack.pop(true));
auto rhs = std::stod(stack.pop(true));
@ -140,6 +143,7 @@ std::optional<std::string> Math::fn_div(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto divisor = std::stod(stack.pop(true));
auto dividend = std::stod(stack.pop(true));
@ -172,6 +176,7 @@ std::optional<std::string> Math::fn_mod(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto rhs = std::stod(stack.pop(true));
auto lhs = std::stod(stack.pop(true));
@ -204,6 +209,7 @@ std::optional<std::string> Math::fn_div_mod(DCStack<std::string> &stack, Paramet
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto divisor = std::stod(stack.pop(true));
auto dividend = std::stod(stack.pop(true));
@ -243,6 +249,7 @@ std::optional<std::string> Math::fn_mod_exp(DCStack<std::string> &stack, Paramet
// This functions computes
// c ≡ b^e (mod n)
if(is_n_num && is_e_num && is_b_num) {
stack.copy_xyz();
auto modulus = std::stoi(stack.pop(true));
auto exponent = std::stoi(stack.pop(true));
auto base = std::stoi(stack.pop(true));
@ -286,6 +293,7 @@ std::optional<std::string> Math::fn_exp(DCStack<std::string> &stack, Parameters
// Check whether both entries are numbers
if(is_x_num && is_y_num) {
stack.copy_xyz();
auto exp = std::stod(stack.pop(true));
auto base = std::stod(stack.pop(true));
@ -311,6 +319,7 @@ std::optional<std::string> Math::fn_sqrt(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
if(val <= 0.0) {
@ -339,6 +348,7 @@ std::optional<std::string> Math::fn_sin(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -363,6 +373,7 @@ std::optional<std::string> Math::fn_cos(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -387,6 +398,7 @@ std::optional<std::string> Math::fn_tan(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -411,6 +423,7 @@ std::optional<std::string> Math::fn_asin(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -435,6 +448,7 @@ std::optional<std::string> Math::fn_acos(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -459,6 +473,7 @@ std::optional<std::string> Math::fn_atan(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
auto val = std::stod(stack.pop(true));
// Push back the result as a string
@ -483,6 +498,7 @@ std::optional<std::string> Math::fn_fact(DCStack<std::string> &stack, Parameters
// Check whether the entry is a number
if(is_x_num) {
stack.copy_xyz();
unsigned long factorial = 1;
auto val = std::stod(stack.pop(true));

View File

@ -15,7 +15,7 @@ enum class OPType {
SQRT, SIN, COS, TAN, ASIN, ACOS, ATAN, FACT, PI, E,
// Stack operations
PCG, P, PBB, PBH, PBO, CLR, PH, SO, DP, PS, CH, CS,
SP, GP, SOR, GOR, SIR, GIR,
SP, GP, SOR, GOR, SIR, GIR, LX, LY, LZ,
// Macro operations
EX, CMP, RI
};

View File

@ -37,6 +37,9 @@ std::optional<std::string> Stack::exec(DCStack<std::string> &stack, Parameters &
case OPType::GOR: err = fn_get_oradix(stack, parameters); break;
case OPType::SIR: err = fn_set_iradix(stack, parameters); break;
case OPType::GIR: err = fn_get_iradix(stack, parameters); break;
case OPType::LX: err = fn_get_lastx(stack); break;
case OPType::LY: err = fn_get_lasty(stack); break;
case OPType::LZ: err = fn_get_lastz(stack); break;
default: break;
}
@ -91,6 +94,7 @@ std::optional<std::string> Stack::fn_pop_head(DCStack<std::string> &stack) {
return "'R' does not work on empty stack";
}
stack.copy_xyz();
stack.pop(true);
return std::nullopt;
@ -106,6 +110,7 @@ std::optional<std::string> Stack::fn_swap_xy(DCStack<std::string> &stack) {
auto len = stack.size()-1;
auto x = stack.at(len);
stack.copy_xyz();
stack.at(len) = stack.at(len-1);
stack.at(len-1) = x;
@ -170,6 +175,8 @@ std::optional<std::string> Stack::fn_head_size(DCStack<std::string> &stack) {
// If it's an integer, count its digits
if(is_num<int>(head)) {
auto num = std::stoi(head);
stack.copy_xyz();
stack.pop(true);
size_t len = 0;
@ -181,6 +188,7 @@ std::optional<std::string> Stack::fn_head_size(DCStack<std::string> &stack) {
stack.push(std::to_string(len));
} else {
// Otherwise, treat the value as a string and count its length
stack.copy_xyz();
stack.pop(true);
head.erase(std::remove(head.begin(), head.end(), '.'), head.end());
stack.push(std::to_string(head.length()));
@ -209,6 +217,7 @@ std::optional<std::string> Stack::fn_set_precision(DCStack<std::string> &stack,
// Otherwise extract head of the stack and use it
// to set precision parameter
stack.copy_xyz();
stack.pop(true);
parameters.precision = std::stoi(head);
@ -228,7 +237,8 @@ std::optional<std::string> Stack::fn_set_oradix(DCStack<std::string> &stack, Par
}
// Check whether the head is a number
auto head = stack.pop(false);
stack.copy_xyz();
auto head = stack.pop(true);
if(!is_num<int>(head)) {
return "'o' requires numeric values only";
}
@ -266,6 +276,7 @@ std::optional<std::string> Stack::fn_set_iradix(DCStack<std::string> &stack, Par
// Otherwise extract head of the stack and use it
// to set input base
stack.copy_xyz();
stack.pop(true);
parameters.iradix = std::stoi(head);
@ -278,6 +289,30 @@ std::optional<std::string> Stack::fn_get_iradix(DCStack<std::string> &stack, Par
return std::nullopt;
}
std::optional<std::string> Stack::fn_get_lastx(DCStack<std::string> &stack) {
// Retrieve last x from the stack and push it back
auto last_x = stack.get_last_x();
last_x.empty() ? stack.push("0") : stack.push(last_x);
return std::nullopt;
}
std::optional<std::string> Stack::fn_get_lasty(DCStack<std::string> &stack) {
// Retrieve last y from the stack and push it back
auto last_y = stack.get_last_y();
last_y.empty() ? stack.push("0") : stack.push(last_y);
return std::nullopt;
}
std::optional<std::string> Stack::fn_get_lastz(DCStack<std::string> &stack) {
// Retrieve last y from the stack and push it back
auto last_z = stack.get_last_z();
last_z.empty() ? stack.push("0") : stack.push(last_z);
return std::nullopt;
}
constexpr std::string Stack::to_bin(auto num) {
if(num == 0) {
return "0";

View File

@ -21,6 +21,9 @@ private:
static std::optional<std::string> fn_get_oradix(DCStack<std::string> &stack, Parameters &parameters);
static std::optional<std::string> fn_set_iradix(DCStack<std::string> &stack, Parameters &parameters);
static std::optional<std::string> fn_get_iradix(DCStack<std::string> &stack, Parameters &parameters);
std::optional<std::string> fn_get_lastx(DCStack<std::string> &stack);
std::optional<std::string> fn_get_lasty(DCStack<std::string> &stack);
std::optional<std::string> fn_get_lastz(DCStack<std::string> &stack);
constexpr std::string to_bin(auto num);
OPType op_type;

26
tests/test_lastx Normal file
View File

@ -0,0 +1,26 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
# Test with arithmetical operations
EXPECTED="4"
ACTUAL=$("$PROGRAM" -e '5 4 + .x p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with default value
EXPECTED="0"
ACTUAL=$("$PROGRAM" -e '.x p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with macro opeartions
EXPECTED="foo"
ACTUAL=$("$PROGRAM" -e '[ foo ] sx .x p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with stack operations
EXPECTED="2"
ACTUAL=$("$PROGRAM" -e '1 2 r .x p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

26
tests/test_lasty Normal file
View File

@ -0,0 +1,26 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
# Test with arithmetical operations
EXPECTED="5"
ACTUAL=$("$PROGRAM" -e '5 4 + .y p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with default value
EXPECTED="0"
ACTUAL=$("$PROGRAM" -e '.y p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with macro opeartions
EXPECTED="6"
ACTUAL=$("$PROGRAM" -e '6 [ foo ] sx sy .y p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with stack operations
EXPECTED="1"
ACTUAL=$("$PROGRAM" -e '1 2 r .y p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab:

26
tests/test_lastz Normal file
View File

@ -0,0 +1,26 @@
#!/bin/sh
utest() {
PROGRAM="$PWD/build/dc"
# Test with arithmetical operations
EXPECTED="1"
ACTUAL=$("$PROGRAM" -e '1 5 4 + + .z p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with default value
EXPECTED="0"
ACTUAL=$("$PROGRAM" -e '.z p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with macro opeartions
EXPECTED="5"
ACTUAL=$("$PROGRAM" -e '5 6 [ foo ] sx sy sz .z p')
assert_eq "$EXPECTED" "$ACTUAL"
# Test with stack operations
EXPECTED="10"
ACTUAL=$("$PROGRAM" -e '10 1 2 R .z p')
assert_eq "$EXPECTED" "$ACTUAL"
}
# vim: ts=4 sw=4 softtabstop=4 expandtab: