Added math product support (#2249)

* added math product: working initial impl

* resolving merge conflicts

* impl std::ops::Mul for Filesize

* rebased with main; refactored product implementation

* fixed error msg, added docs for math product
This commit is contained in:
Gurpreet Singh 2020-08-26 22:58:01 -07:00 committed by GitHub
parent a64270829e
commit 303a9defd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 219 additions and 41 deletions

88
Cargo.lock generated
View file

@ -277,9 +277,9 @@ checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
[[package]]
name = "autocfg"
version = "1.0.0"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "backtrace"
@ -896,6 +896,16 @@ dependencies = [
"crossbeam-utils 0.6.6",
]
[[package]]
name = "crossbeam-channel"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ee0cc8804d5393478d743b035099520087a5186f3b93fa58cec08fa62407b6"
dependencies = [
"cfg-if",
"crossbeam-utils 0.7.2",
]
[[package]]
name = "crossbeam-deque"
version = "0.7.3"
@ -913,7 +923,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"cfg-if",
"crossbeam-utils 0.7.2",
"lazy_static 1.4.0",
@ -949,7 +959,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"cfg-if",
"lazy_static 1.4.0",
]
@ -1343,9 +1353,9 @@ checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
[[package]]
name = "encoding_rs"
version = "0.8.23"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8ac63f94732332f44fe654443c46f6375d1939684c17b0afb6cb56b0456e171"
checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
dependencies = [
"cfg-if",
]
@ -1384,9 +1394,9 @@ dependencies = [
[[package]]
name = "event-listener"
version = "2.3.3"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f14646a9e0430150a87951622ba9675472b68e384b7701b8423b30560805c7a"
checksum = "e1cd41440ae7e4734bbd42302f63eaba892afc93a3912dad84006247f0dedb0e"
[[package]]
name = "failure"
@ -1807,9 +1817,9 @@ checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "git2"
version = "0.13.9"
version = "0.13.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a18853c521bcf553a4e3c05ad21f3ad89c1a78a0485adc97829a4c88066a36"
checksum = "86d97249f21e9542caeee9f8e1d150905cd875bf723f5ff771bdb4852eb83a24"
dependencies = [
"bitflags",
"libc",
@ -1863,7 +1873,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
]
[[package]]
@ -2252,7 +2262,7 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"hashbrown",
"serde 1.0.115",
]
@ -2310,7 +2320,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b77027f12e53ae59a379f7074259d32eb10867e6183388020e922832d9c3fb"
dependencies = [
"bytes 0.4.12",
"crossbeam-channel",
"crossbeam-channel 0.3.9",
"crossbeam-utils 0.6.6",
"curl",
"curl-sys",
@ -2451,15 +2461,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.74"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10"
checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
[[package]]
name = "libgit2-sys"
version = "0.12.11+1.0.1"
version = "0.12.12+1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4755167bef8a2959fe4eedeece2ba0f8eb9923b209d46139031f1281d569a2"
checksum = "0100ae90655025134424939f1f60e27e879460d451dff6afedde4f8226cbebfc"
dependencies = [
"cc",
"libc",
@ -2710,7 +2720,7 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c198b026e1bbf08a937e94c6c60f9ec4a2267f5b0d2eec9c1b21b061ce2be55f"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
]
[[package]]
@ -2741,9 +2751,9 @@ dependencies = [
[[package]]
name = "miniz_oxide"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f"
checksum = "4d7559a8a40d0f97e1edea3220f698f78b1c5ab67532e49f68fde3910323b722"
dependencies = [
"adler",
]
@ -3478,7 +3488,7 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"num-integer",
"num-traits 0.2.12",
"serde 1.0.115",
@ -3490,7 +3500,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"num-traits 0.2.12",
]
@ -3511,7 +3521,7 @@ version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"num-traits 0.2.12",
]
@ -3521,7 +3531,7 @@ version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"num-integer",
"num-traits 0.2.12",
]
@ -3532,7 +3542,7 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"num-bigint",
"num-integer",
"num-traits 0.2.12",
@ -3553,7 +3563,7 @@ version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
]
[[package]]
@ -3698,7 +3708,7 @@ version = "0.9.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"cc",
"libc",
"pkg-config",
@ -4388,11 +4398,11 @@ dependencies = [
[[package]]
name = "rayon"
version = "1.3.1"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f02856753d04e03e26929f820d0a0a337ebe71f849801eea335d464b349080"
checksum = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270"
dependencies = [
"autocfg 1.0.0",
"autocfg 1.0.1",
"crossbeam-deque",
"either",
"rayon-core",
@ -4400,12 +4410,12 @@ dependencies = [
[[package]]
name = "rayon-core"
version = "1.7.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e92e15d89083484e11353891f1af602cc661426deb9564c298b270c726973280"
checksum = "91739a34c4355b5434ce54c9086c5895604a9c278586d1f1aa95e04f66b525a0"
dependencies = [
"crossbeam-channel 0.4.3",
"crossbeam-deque",
"crossbeam-queue",
"crossbeam-utils 0.7.2",
"lazy_static 1.4.0",
"num_cpus",
@ -5187,9 +5197,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.38"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e69abc24912995b3038597a7a593be5053eb0fb44f3cc5beec0deb421790c1f4"
checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9"
dependencies = [
"proc-macro2",
"quote",
@ -5632,9 +5642,9 @@ dependencies = [
[[package]]
name = "trash"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcd4ea126e9b491aa0ee8043f5519cd56498413997a6c3859c8a9812aa300803"
checksum = "329be7bb48445d16bf4c241ba9514af46f2189c715bf5fd854e38f7c95f60194"
dependencies = [
"winapi 0.3.9",
]
@ -5875,9 +5885,9 @@ checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
[[package]]
name = "vec-arena"
version = "0.5.0"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17dfb54bf57c9043f4616cb03dab30eff012cc26631b797d8354b916708db919"
checksum = "8cb18268690309760d59ee1a9b21132c126ba384f374c59a94db4bc03adeb561"
[[package]]
name = "vec_map"

View file

@ -419,6 +419,7 @@ pub fn create_default_context(
whole_stream_command(MathStddev),
whole_stream_command(MathSummation),
whole_stream_command(MathVariance),
whole_stream_command(MathProduct),
// File format output
whole_stream_command(To),
whole_stream_command(ToCSV),

View file

@ -199,8 +199,8 @@ pub(crate) use last::Last;
pub(crate) use lines::Lines;
pub(crate) use ls::Ls;
pub(crate) use math::{
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathStddev,
MathSummation, MathVariance,
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathProduct,
MathStddev, MathSummation, MathVariance,
};
pub(crate) use merge::Merge;
pub(crate) use mkdir::Mkdir;

View file

@ -5,6 +5,7 @@ pub mod max;
pub mod median;
pub mod min;
pub mod mode;
pub mod product;
pub mod stddev;
pub mod sum;
pub mod variance;
@ -19,6 +20,7 @@ pub use max::SubCommand as MathMaximum;
pub use median::SubCommand as MathMedian;
pub use min::SubCommand as MathMinimum;
pub use mode::SubCommand as MathMode;
pub use product::SubCommand as MathProduct;
pub use stddev::SubCommand as MathStddev;
pub use sum::SubCommand as MathSummation;
pub use variance::SubCommand as MathVariance;

View file

@ -0,0 +1,126 @@
use crate::commands::math::reducers::{reducer_for, Reduce};
use crate::commands::math::utils::run_with_function;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
hir::{convert_number_to_u64, Number},
Primitive, Signature, UntaggedValue, Value,
};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"math product"
}
fn signature(&self) -> Signature {
Signature::build("math product")
}
fn usage(&self) -> &str {
"Finds the product of a list of numbers or tables"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
run_with_function(
RunnableContext {
input: args.input,
registry: registry.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
raw_input: args.raw_input,
},
product,
)
.await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get the product of a list of numbers",
example: "echo [2 3 3 4] | math product",
result: Some(vec![UntaggedValue::int(72).into()]),
}]
}
}
fn to_byte(value: &Value) -> Option<Value> {
match &value.value {
UntaggedValue::Primitive(Primitive::Int(num)) => Some(
UntaggedValue::Primitive(Primitive::Filesize(convert_number_to_u64(&Number::Int(
num.clone(),
))))
.into_untagged_value(),
),
_ => None,
}
}
/// Calculate product of given values
pub fn product(values: &[Value], name: &Tag) -> Result<Value, ShellError> {
let prod = reducer_for(Reduce::Product);
let first = values.get(0).ok_or_else(|| {
ShellError::unexpected("Cannot perform aggregate math operation on empty data")
})?;
match first {
v if v.is_filesize() => to_byte(&prod(
UntaggedValue::int(1).into_untagged_value(),
values
.iter()
.map(|v| match v {
Value {
value: UntaggedValue::Primitive(Primitive::Filesize(num)),
..
} => UntaggedValue::int(*num as usize).into_untagged_value(),
other => other.clone(),
})
.collect::<Vec<_>>(),
)?)
.ok_or_else(|| {
ShellError::labeled_error(
"could not convert to decimal",
"could not convert to decimal",
&name.span,
)
}),
v if v.is_none() => prod(
UntaggedValue::int(1).into_untagged_value(),
values
.iter()
.map(|v| match v {
Value {
value: UntaggedValue::Primitive(Primitive::Nothing),
..
} => UntaggedValue::int(1).into_untagged_value(),
other => other.clone(),
})
.collect::<Vec<_>>(),
),
_ => prod(UntaggedValue::int(1).into_untagged_value(), values.to_vec()),
}
}
#[cfg(test)]
mod tests {
use super::SubCommand;
#[test]
fn examples_work_as_expected() {
use crate::examples::test as test_examples;
test_examples(SubCommand {})
}
}

View file

@ -47,6 +47,7 @@ pub fn reducer_for(
)),
Reduce::Minimum => Box::new(|_, values| min(values)),
Reduce::Maximum => Box::new(|_, values| max(values)),
Reduce::Product => Box::new(|_, values| product(values)),
}
}
@ -54,6 +55,7 @@ pub enum Reduce {
Summation,
Minimum,
Maximum,
Product,
Default,
}
@ -133,3 +135,34 @@ pub fn min(data: Vec<Value>) -> Result<Value, ShellError> {
tag: Tag::unknown(),
})
}
pub fn product(data: Vec<Value>) -> Result<Value, ShellError> {
if data.is_empty() {
return Err(ShellError::unexpected(ERR_EMPTY_DATA));
}
let mut prod = UntaggedValue::int(1).into_untagged_value();
for value in data {
match value.value {
UntaggedValue::Primitive(_) => {
prod = match compute_values(Operator::Multiply, &prod, &value) {
Ok(v) => v.into_untagged_value(),
Err((left_type, right_type)) => {
return Err(ShellError::coerce_error(
left_type.spanned_unknown(),
right_type.spanned_unknown(),
))
}
};
}
_ => {
return Err(ShellError::labeled_error(
"Attempted to compute the product of a value that cannot be multiplied.",
"value appears here",
value.tag.span,
))
}
}
}
Ok(prod)
}

View file

@ -9,6 +9,7 @@ Currently the following functions are implemented:
* `math max`: Finds the maximum within a list of numbers or tables
* `math median`: Finds the median of a list of numbers or tables
* `math sum`: Finds the sum of a list of numbers or tables
* `math product`: Finds the product of a list of numbers or tables
However, the mathematical functions like `min` and `max` are more permissive and also work on `Dates`.
@ -90,6 +91,11 @@ To get the average of the file sizes in a directory, simply pipe the size column
───┴──────────
```
```shell
> echo [2 3 3 4] | math product
72
```
### Dates
```shell