This commit is contained in:
parent
19ceac3e88
commit
89b7825885
41
man.md
41
man.md
@ -3,7 +3,7 @@ title: dc
|
|||||||
section: 1
|
section: 1
|
||||||
header: General Commands Manual
|
header: General Commands Manual
|
||||||
footer: Marco Cetica
|
footer: Marco Cetica
|
||||||
date: March 25, 2024
|
date: March 26, 2024
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ Pops one value, computes its square root, and pushes that.
|
|||||||
|
|
||||||
**!**
|
**!**
|
||||||
|
|
||||||
Pops one value, computes its factorial, and pushes that.
|
Pops one non-negative integer, computes its factorial, and pushes that.
|
||||||
|
|
||||||
**pi**
|
**pi**
|
||||||
|
|
||||||
@ -224,6 +224,43 @@ Pops one value, computes its `acos`, and pushes that.
|
|||||||
|
|
||||||
Pops one value, computes its `atan`, and pushes that.
|
Pops one value, computes its `atan`, 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. All statistics functions are limited to **non-negative integers**.
|
||||||
|
Accumulating functions use the `X` register.
|
||||||
|
|
||||||
|
**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`
|
||||||
|
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**
|
||||||
|
|
||||||
|
Pops two non-negative integers(that is, >=0) and computes `C_{y, x}`, that is the number of possible sets of
|
||||||
|
`y` different items taken in quantities of `x` items at a time. No item shall occur more than once in a set and different orders of the same `x`
|
||||||
|
items are not counted separately. The `y` parameter correspond to the second one value popped while the `x` parameter correspond to the first one popped.
|
||||||
|
|
||||||
|
**gN**
|
||||||
|
|
||||||
|
Counts the number of accumulated elements of the `X` register's stack and pushes that.
|
||||||
|
|
||||||
|
**gs**
|
||||||
|
|
||||||
|
Computes Σx of the `X` register's stack and pushes that.
|
||||||
|
|
||||||
|
**gS**
|
||||||
|
|
||||||
|
Computes Σx^2 of the `X` register's stack and pushes that.
|
||||||
|
|
||||||
|
**gM**
|
||||||
|
|
||||||
|
Computes x̄(mean) of the `X` register's stack and pushes that.
|
||||||
|
|
||||||
|
**gD**
|
||||||
|
|
||||||
|
Computes σ(standard deviation) of the `X` register's stack and pushes that.
|
||||||
|
|
||||||
## Base Conversion
|
## Base Conversion
|
||||||
|
|
||||||
**pb**
|
**pb**
|
||||||
|
@ -4,6 +4,7 @@ set(HEADER_FILES
|
|||||||
eval.h
|
eval.h
|
||||||
macro.h
|
macro.h
|
||||||
mathematics.h
|
mathematics.h
|
||||||
|
statistics.h
|
||||||
bitwise.h
|
bitwise.h
|
||||||
operation.h
|
operation.h
|
||||||
stack.h
|
stack.h
|
||||||
@ -14,6 +15,7 @@ set(SOURCE_FILES
|
|||||||
eval.cpp
|
eval.cpp
|
||||||
macro.cpp
|
macro.cpp
|
||||||
mathematics.cpp
|
mathematics.cpp
|
||||||
|
statistics.cpp
|
||||||
bitwise.cpp
|
bitwise.cpp
|
||||||
stack.cpp
|
stack.cpp
|
||||||
adt.cpp
|
adt.cpp
|
||||||
|
38
src/adt.cpp
38
src/adt.cpp
@ -1,3 +1,4 @@
|
|||||||
|
#include <numeric>
|
||||||
#include "adt.h"
|
#include "adt.h"
|
||||||
|
|
||||||
#define GET_X this->stack.back()
|
#define GET_X this->stack.back()
|
||||||
@ -121,6 +122,43 @@ namespace dc {
|
|||||||
return this->stack.size();
|
return this->stack.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the summation of all items
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
requires is_num_or_str<T>
|
||||||
|
double Stack<T>::summation() {
|
||||||
|
auto sum = std::accumulate(this->stack.begin(), this->stack.end(), 0.0,
|
||||||
|
[](auto accumulator, const T& val) -> double {
|
||||||
|
if constexpr(std::is_same_v<T, std::string>) {
|
||||||
|
return accumulator + std::stod(val);
|
||||||
|
} else {
|
||||||
|
return accumulator + val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the summation of squares of all items
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
requires is_num_or_str<T>
|
||||||
|
double Stack<T>::summation_squared() {
|
||||||
|
auto sum = std::accumulate(this->stack.begin(), this->stack.end(), 0.0,
|
||||||
|
[](auto accumulator, const T& val) -> double {
|
||||||
|
if constexpr(std::is_same_v<T, std::string>) {
|
||||||
|
return accumulator + (std::stod(val) * std::stod(val));
|
||||||
|
} else {
|
||||||
|
return accumulator + (val * val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if stack is empty
|
* Returns true if stack is empty
|
||||||
* false otherwise
|
* false otherwise
|
||||||
|
@ -22,6 +22,8 @@ namespace dc {
|
|||||||
T& at(std::size_t index);
|
T& at(std::size_t index);
|
||||||
T& operator[](std::size_t index);
|
T& operator[](std::size_t index);
|
||||||
std::size_t size();
|
std::size_t size();
|
||||||
|
double summation();
|
||||||
|
double summation_squared();
|
||||||
bool empty();
|
bool empty();
|
||||||
[[nodiscard]] const std::vector<T>& get_ref() const;
|
[[nodiscard]] const std::vector<T>& get_ref() const;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include "adt.cpp"
|
#include "adt.cpp"
|
||||||
#include "eval.h"
|
#include "eval.h"
|
||||||
#include "mathematics.h"
|
#include "mathematics.h"
|
||||||
|
#include "statistics.h"
|
||||||
#include "bitwise.h"
|
#include "bitwise.h"
|
||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
@ -45,7 +46,12 @@ void Evaluate::init_environment() {
|
|||||||
this->op_factory.emplace("@", MAKE_UNIQUE_PTR(Mathematics, OPType::RND));
|
this->op_factory.emplace("@", MAKE_UNIQUE_PTR(Mathematics, OPType::RND));
|
||||||
this->op_factory.emplace("$", MAKE_UNIQUE_PTR(Mathematics, OPType::INT));
|
this->op_factory.emplace("$", MAKE_UNIQUE_PTR(Mathematics, OPType::INT));
|
||||||
// Statistical operations
|
// Statistical operations
|
||||||
|
this->op_factory.emplace("gP", MAKE_UNIQUE_PTR(Statistics, OPType::PERM));
|
||||||
|
this->op_factory.emplace("gC", MAKE_UNIQUE_PTR(Statistics, OPType::COMB));
|
||||||
|
this->op_factory.emplace("gs", MAKE_UNIQUE_PTR(Statistics, OPType::SUMX));
|
||||||
|
this->op_factory.emplace("gS", MAKE_UNIQUE_PTR(Statistics, OPType::SUMXX));
|
||||||
|
this->op_factory.emplace("gM", MAKE_UNIQUE_PTR(Statistics, OPType::MEAN));
|
||||||
|
this->op_factory.emplace("gD", MAKE_UNIQUE_PTR(Statistics, OPType::SDEV));
|
||||||
// Bitwise operations
|
// Bitwise operations
|
||||||
this->op_factory.emplace("{", MAKE_UNIQUE_PTR(Bitwise, OPType::BAND));
|
this->op_factory.emplace("{", MAKE_UNIQUE_PTR(Bitwise, OPType::BAND));
|
||||||
this->op_factory.emplace("}", MAKE_UNIQUE_PTR(Bitwise, OPType::BOR));
|
this->op_factory.emplace("}", MAKE_UNIQUE_PTR(Bitwise, OPType::BOR));
|
||||||
|
@ -245,17 +245,17 @@ std::optional<std::string> Mathematics::fn_mod_exp(dc::Stack<std::string> &stack
|
|||||||
auto n = stack[len];
|
auto n = stack[len];
|
||||||
auto e = stack[len-1];
|
auto e = stack[len-1];
|
||||||
auto b = stack[len-2];
|
auto b = stack[len-2];
|
||||||
auto is_n_num = is_num<double>(n);
|
auto is_n_num = is_num<long long>(n);
|
||||||
auto is_e_num = is_num<double>(e);
|
auto is_e_num = is_num<long long>(e);
|
||||||
auto is_b_num = is_num<double>(b);
|
auto is_b_num = is_num<long long>(b);
|
||||||
|
|
||||||
// This functions computes
|
// This functions computes
|
||||||
// c ≡ b^e (mod n)
|
// c ≡ b^e (mod n)
|
||||||
if(is_n_num && is_e_num && is_b_num) {
|
if(is_n_num && is_e_num && is_b_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
auto modulus = std::stol(stack.pop(true));
|
auto modulus = std::stoll(stack.pop(true));
|
||||||
auto exponent = std::stol(stack.pop(true));
|
auto exponent = std::stoll(stack.pop(true));
|
||||||
auto base = std::stol(stack.pop(true));
|
auto base = std::stoll(stack.pop(true));
|
||||||
|
|
||||||
if(modulus == 1) {
|
if(modulus == 1) {
|
||||||
stack.push("0");
|
stack.push("0");
|
||||||
@ -502,14 +502,14 @@ std::optional<std::string> Mathematics::fn_fact(dc::Stack<std::string> &stack, c
|
|||||||
// Check whether the entry is a number
|
// Check whether the entry is a number
|
||||||
if(is_x_num) {
|
if(is_x_num) {
|
||||||
stack.copy_xyz();
|
stack.copy_xyz();
|
||||||
unsigned long factorial = 1;
|
unsigned long long factorial = 1;
|
||||||
auto val = std::stod(stack.pop(true));
|
auto val = std::stod(stack.pop(true));
|
||||||
|
|
||||||
if(val == 0.0) {
|
if(val < 0.0) {
|
||||||
factorial = 1;
|
return "'!' is not defined for negative numbers";
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::size_t i = 2; i <= (std::size_t)val; i++) {
|
for(unsigned long long i = 2; i <= val; i++) {
|
||||||
factorial *= i;
|
factorial *= i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
214
src/statistics.cpp
Normal file
214
src/statistics.cpp
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include "adt.cpp"
|
||||||
|
#include "statistics.h"
|
||||||
|
#include "is_num.h"
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::exec(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||||
|
std::optional<std::string> err = std::nullopt;
|
||||||
|
|
||||||
|
switch(this->op_type) {
|
||||||
|
case OPType::PERM: err = fn_perm(stack, parameters); break;
|
||||||
|
case OPType::COMB: err = fn_comb(stack, parameters); break;
|
||||||
|
case OPType::SUMX: err = fn_sum(stack, parameters, regs); break;
|
||||||
|
case OPType::SUMXX: err = fn_sum_squared(stack, parameters, regs); break;
|
||||||
|
case OPType::MEAN: err = fn_mean(stack, parameters, regs); break;
|
||||||
|
case OPType::SDEV: err = fn_sdev(stack, parameters, regs); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_perm(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||||
|
// Check if stack has enough elements
|
||||||
|
if(stack.size() < 2) {
|
||||||
|
return "'gP' requires two operands";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract two entries from the stack
|
||||||
|
auto len = stack.size()-1;
|
||||||
|
auto head = stack[len];
|
||||||
|
auto second = stack[len-1];
|
||||||
|
auto is_head_num = is_num<long long>(head);
|
||||||
|
auto is_second_num = is_num<long long>(second);
|
||||||
|
|
||||||
|
// Check whether both entries are integers
|
||||||
|
if(is_head_num && is_second_num) {
|
||||||
|
stack.copy_xyz();
|
||||||
|
auto x = std::stoll(stack.pop(true));
|
||||||
|
auto y = std::stoll(stack.pop(true));
|
||||||
|
|
||||||
|
// Compute factorial of numerator and denominator
|
||||||
|
// and check if result is a non-negative integer
|
||||||
|
auto numerator_opt = factorial(y);
|
||||||
|
auto denominator_opt = factorial(y - x);
|
||||||
|
if(numerator_opt == std::nullopt || denominator_opt == std::nullopt) {
|
||||||
|
return "'gP' requires positive integers";
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long permutation = numerator_opt.value() / denominator_opt.value();
|
||||||
|
stack.push(trim_digits(permutation, parameters.precision));
|
||||||
|
} else {
|
||||||
|
return "'gP' requires integers values";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_comb(dc::Stack<std::string> &stack, const dc::Parameters ¶meters) {
|
||||||
|
// Check if stack has enough elements
|
||||||
|
if(stack.size() < 2) {
|
||||||
|
return "'gC' requires two operands";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract two entries from the stack
|
||||||
|
auto len = stack.size()-1;
|
||||||
|
auto head = stack[len];
|
||||||
|
auto second = stack[len-1];
|
||||||
|
auto is_head_num = is_num<long long>(head);
|
||||||
|
auto is_second_num = is_num<long long>(second);
|
||||||
|
|
||||||
|
// Check whether both entries are integers
|
||||||
|
if(is_head_num && is_second_num) {
|
||||||
|
stack.copy_xyz();
|
||||||
|
auto n = std::stoull(stack.pop(true));
|
||||||
|
auto k = std::stoull(stack.pop(true));
|
||||||
|
|
||||||
|
// Check if combination is non-negative
|
||||||
|
if(n > k) {
|
||||||
|
return "'gC' requires positive integers";
|
||||||
|
}
|
||||||
|
|
||||||
|
// From Knuth's "Seminumerical Algorithms" book
|
||||||
|
unsigned long long combination = 1;
|
||||||
|
for(unsigned long long i = 1; i <= n; i++) {
|
||||||
|
combination *= k--;
|
||||||
|
combination /= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack.push(trim_digits(combination, parameters.precision));
|
||||||
|
} else {
|
||||||
|
return "'gC' requires integers values";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_sum(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||||
|
// Check whether 'x' register exists
|
||||||
|
if(regs.find('X') == regs.end()) {
|
||||||
|
return "Register 'X' is undefined";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if register's stack is empty
|
||||||
|
if(regs['X'].stack.empty()) {
|
||||||
|
return "The stack of register 'X' is empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Othewise retrieve summation of register's stack
|
||||||
|
auto summation = regs['X'].stack.summation();
|
||||||
|
stack.push(trim_digits(summation, parameters.precision));
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_sum_squared(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||||
|
// Check whether 'x' register exists
|
||||||
|
if(regs.find('X') == regs.end()) {
|
||||||
|
return "Register 'X' is undefined";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if register's stack is empty
|
||||||
|
if(regs['X'].stack.empty()) {
|
||||||
|
return "The stack of register 'X' is empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Othewise retrieve summation of squares of register's stack
|
||||||
|
auto summation_squared = regs['X'].stack.summation_squared();
|
||||||
|
stack.push(trim_digits(summation_squared, parameters.precision));
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_mean(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||||
|
// Check whether 'x' register exists
|
||||||
|
if(regs.find('X') == regs.end()) {
|
||||||
|
return "Register 'X' is undefined";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if register's stack is empty
|
||||||
|
if(regs['X'].stack.empty()) {
|
||||||
|
return "The stack of register 'X' is empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Othewise compute mean
|
||||||
|
auto summation = regs['X'].stack.summation();
|
||||||
|
auto size = regs['X'].stack.size();
|
||||||
|
auto mean = summation / size;
|
||||||
|
stack.push(trim_digits(mean, parameters.precision));
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> Statistics::fn_sdev(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) {
|
||||||
|
// Check whether 'x' register exists
|
||||||
|
if(regs.find('X') == regs.end()) {
|
||||||
|
return "Register 'X' is undefined";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if register's stack is empty
|
||||||
|
if(regs['X'].stack.empty()) {
|
||||||
|
return "The stack of register 'X' is empty";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Othewise, compute the mean
|
||||||
|
auto summation = regs['X'].stack.summation();
|
||||||
|
auto count = regs['X'].stack.size();
|
||||||
|
auto mean = summation / count;
|
||||||
|
|
||||||
|
// Then compute the sum of the deviations from the mean and square the result
|
||||||
|
const auto& const_vec = regs['X'].stack.get_ref();
|
||||||
|
double sum_of_deviations = std::accumulate(const_vec.begin(), const_vec.end(), 0.0,
|
||||||
|
[&](double acc, const std::string& val) {
|
||||||
|
double deviation = std::stod(val) - mean;
|
||||||
|
return acc + std::pow(deviation, 2);
|
||||||
|
});
|
||||||
|
// Then compute the mean of previos values(variance)
|
||||||
|
auto variance = sum_of_deviations / count;
|
||||||
|
|
||||||
|
// Finally, compute the square root of the variance(standard deviation)
|
||||||
|
auto s_dev = sqrt(variance);
|
||||||
|
|
||||||
|
stack.push(trim_digits(s_dev, parameters.precision));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<unsigned long long> Statistics::factorial(const long long n) {
|
||||||
|
if(n < 0) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long long factorial = 1;
|
||||||
|
|
||||||
|
for(long long i = 2; i <= n; i++) {
|
||||||
|
factorial *= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return factorial;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Statistics::trim_digits(double number, unsigned int precision) {
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
// Preserve non-zero decimal numbers even when precision is zero
|
||||||
|
if(precision == 0 && std::fmod(number, 1.0) != 0.0) {
|
||||||
|
precision = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << std::fixed << std::setprecision(static_cast<int>(precision)) << number;
|
||||||
|
std::string s = oss.str();
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
21
src/statistics.h
Normal file
21
src/statistics.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "operation.h"
|
||||||
|
|
||||||
|
class Statistics : public IOperation {
|
||||||
|
public:
|
||||||
|
explicit Statistics(const OPType op_t) : op_type(op_t) {}
|
||||||
|
std::optional<std::string> exec(dc::Stack<std::string> &stack, dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::optional<std::string> fn_perm(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||||
|
std::optional<std::string> fn_comb(dc::Stack<std::string> &stack, const dc::Parameters ¶meters);
|
||||||
|
std::optional<std::string> fn_sum(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||||
|
std::optional<std::string> fn_sum_squared(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||||
|
std::optional<std::string> fn_mean(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||||
|
std::optional<std::string> fn_sdev(dc::Stack<std::string> &stack, const dc::Parameters ¶meters, std::unordered_map<char, dc::Register> ®s);
|
||||||
|
std::optional<unsigned long long> factorial(const long long n);
|
||||||
|
std::string trim_digits(double number, unsigned int precision);
|
||||||
|
|
||||||
|
OPType op_type;
|
||||||
|
};
|
@ -11,6 +11,11 @@ utest() {
|
|||||||
ACTUAL=$("$PROGRAM" -e '!' 2>&1) || true
|
ACTUAL=$("$PROGRAM" -e '!' 2>&1) || true
|
||||||
assert_eq "$EXPECTED" "$ACTUAL"
|
assert_eq "$EXPECTED" "$ACTUAL"
|
||||||
|
|
||||||
|
# Test negative numbers
|
||||||
|
EXPECTED="'!' is not defined for negative numbers"
|
||||||
|
ACTUAL=$("$PROGRAM" -e '-5 !' 2>&1) || true
|
||||||
|
assert_eq "$EXPECTED" "$ACTUAL"
|
||||||
|
|
||||||
# Test non numerical values
|
# Test non numerical values
|
||||||
EXPECTED="'!' requires numeric values"
|
EXPECTED="'!' requires numeric values"
|
||||||
ACTUAL=$("$PROGRAM" -e '[ foo ] !' 2>&1) || true
|
ACTUAL=$("$PROGRAM" -e '[ foo ] !' 2>&1) || true
|
||||||
|
Loading…
Reference in New Issue
Block a user