1#![allow(clippy::type_complexity)]
2
3use crate::value::unsafe_compute_values;
4use derive_new::new;
5use nu_errors::ShellError;
6use nu_protocol::hir::Operator;
7use nu_protocol::{UntaggedValue, Value};
8use nu_source::{SpannedItem, Tag, TaggedItem};
9use nu_value_ext::ValueExt;
10
11#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
12pub struct Labels {
13 pub x: Vec<String>,
14 pub y: Vec<String>,
15}
16
17impl Labels {
18 pub fn at(&self, idx: usize) -> Option<&str> {
19 self.x.get(idx).map(|k| &k[..])
20 }
21
22 pub fn at_split(&self, idx: usize) -> Option<&str> {
23 self.y.get(idx).map(|k| &k[..])
24 }
25
26 pub fn grouped(&self) -> impl Iterator<Item = &String> {
27 self.x.iter()
28 }
29
30 pub fn grouping_total(&self) -> Value {
31 UntaggedValue::int(self.x.len() as i64).into_untagged_value()
32 }
33
34 pub fn splits(&self) -> impl Iterator<Item = &String> {
35 self.y.iter()
36 }
37
38 pub fn splits_total(&self) -> Value {
39 UntaggedValue::big_int(self.y.len()).into_untagged_value()
40 }
41}
42
43#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Clone, new)]
44pub struct Range {
45 pub start: Value,
46 pub end: Value,
47}
48
49fn formula(
50 acc_begin: Value,
51 calculator: Box<dyn Fn(Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static>,
52) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
53 Box::new(move |acc, datax| -> Result<Value, ShellError> {
54 let result = match unsafe_compute_values(Operator::Multiply, acc, &acc_begin) {
55 Ok(v) => v.into_untagged_value(),
56 Err((left_type, right_type)) => {
57 return Err(ShellError::coerce_error(
58 left_type.spanned_unknown(),
59 right_type.spanned_unknown(),
60 ))
61 }
62 };
63
64 match calculator(datax) {
65 Ok(total) => Ok(
66 match unsafe_compute_values(Operator::Plus, &result, &total) {
67 Ok(v) => v.into_untagged_value(),
68 Err((left_type, right_type)) => {
69 return Err(ShellError::coerce_error(
70 left_type.spanned_unknown(),
71 right_type.spanned_unknown(),
72 ))
73 }
74 },
75 ),
76 Err(reason) => Err(reason),
77 }
78 })
79}
80
81pub fn reducer_for(
82 command: &Reduction,
83) -> Box<dyn Fn(&Value, Vec<&Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
84 match command {
85 Reduction::Accumulate => Box::new(formula(
86 UntaggedValue::int(1).into_untagged_value(),
87 Box::new(sum),
88 )),
89 Reduction::Count => Box::new(formula(
90 UntaggedValue::int(0).into_untagged_value(),
91 Box::new(sum),
92 )),
93 }
94}
95
96pub fn max(values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
97 let tag = tag.into();
98
99 let mut x = UntaggedValue::int(0);
100
101 for split in values.table_entries() {
102 match split.value {
103 UntaggedValue::Table(ref values) => {
104 let inner = inner_max(values)?;
105
106 if let Ok(greater_than) =
107 crate::value::compare_values(Operator::GreaterThan, &inner.value, &x)
108 {
109 if greater_than {
110 x = inner.value.clone();
111 }
112 } else {
113 return Err(ShellError::unexpected(format!(
114 "Could not compare\nleft: {:?}\nright: {:?}",
115 inner.value, x
116 )));
117 }
118 }
119 _ => {
120 return Err(ShellError::labeled_error(
121 "Attempted to compute the sum of a value that cannot be summed.",
122 "value appears here",
123 split.tag.span,
124 ))
125 }
126 }
127 }
128
129 Ok(x.into_value(&tag))
130}
131
132pub fn inner_max(data: &[Value]) -> Result<Value, ShellError> {
133 let mut biggest = data
134 .first()
135 .ok_or_else(|| {
136 ShellError::unexpected("Cannot perform aggregate math operation on empty data")
137 })?
138 .value
139 .clone();
140
141 for value in data {
142 if let Ok(greater_than) =
143 crate::value::compare_values(Operator::GreaterThan, &value.value, &biggest)
144 {
145 if greater_than {
146 biggest = value.value.clone();
147 }
148 } else {
149 return Err(ShellError::unexpected(format!(
150 "Could not compare\nleft: {:?}\nright: {:?}",
151 biggest, value.value
152 )));
153 }
154 }
155 Ok(Value {
156 value: biggest,
157 tag: Tag::unknown(),
158 })
159}
160
161pub fn sum(data: Vec<&Value>) -> Result<Value, ShellError> {
162 let mut acc = UntaggedValue::int(0);
163
164 for value in data {
165 match value.value {
166 UntaggedValue::Primitive(_) => {
167 acc = match unsafe_compute_values(Operator::Plus, &acc, value) {
168 Ok(v) => v,
169 Err((left_type, right_type)) => {
170 return Err(ShellError::coerce_error(
171 left_type.spanned_unknown(),
172 right_type.spanned_unknown(),
173 ))
174 }
175 };
176 }
177 _ => {
178 return Err(ShellError::labeled_error(
179 "Attempted to compute the sum of a value that cannot be summed.",
180 "value appears here",
181 value.tag.span,
182 ))
183 }
184 }
185 }
186 Ok(acc.into_untagged_value())
187}
188
189pub fn sort_columns(
190 values: &[String],
191 format: &Option<Box<dyn Fn(&Value, String) -> Result<String, ShellError>>>,
192) -> Result<Vec<String>, ShellError> {
193 let mut keys = values.to_vec();
194
195 if format.is_none() {
196 keys.sort();
197 }
198
199 Ok(keys)
200}
201
202pub fn sort(planes: &Labels, values: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
203 let tag = tag.into();
204
205 let mut x = vec![];
206 for column in planes.splits() {
207 let key = column.clone().tagged_unknown();
208 let groups = values
209 .get_data_by_key(key.borrow_spanned())
210 .ok_or_else(|| {
211 ShellError::labeled_error("unknown column", "unknown column", key.span())
212 })?;
213
214 let mut y = vec![];
215 for inner_column in planes.grouped() {
216 let key = inner_column.clone().tagged_unknown();
217 let grouped = groups.get_data_by_key(key.borrow_spanned());
218
219 if let Some(grouped) = grouped {
220 y.push(grouped);
221 } else {
222 y.push(UntaggedValue::Table(vec![]).into_value(&tag));
223 }
224 }
225
226 x.push(UntaggedValue::table(&y).into_value(&tag));
227 }
228
229 Ok(UntaggedValue::table(&x).into_value(&tag))
230}
231
232pub fn evaluate(
233 values: &Value,
234 evaluator: &Option<Box<dyn Fn(usize, &Value) -> Result<Value, ShellError> + Send>>,
235 tag: impl Into<Tag>,
236) -> Result<Value, ShellError> {
237 let tag = tag.into();
238
239 let mut x = vec![];
240 for split in values.table_entries() {
241 let mut y = vec![];
242
243 for (idx, subset) in split.table_entries().enumerate() {
244 if let UntaggedValue::Table(values) = &subset.value {
245 if let Some(ref evaluator) = evaluator {
246 let mut evaluations = vec![];
247
248 for set in values {
249 evaluations.push(evaluator(idx, set)?);
250 }
251
252 y.push(UntaggedValue::Table(evaluations).into_value(&tag));
253 } else {
254 y.push(
255 UntaggedValue::Table(
256 values
257 .iter()
258 .map(|_| UntaggedValue::int(1).into_value(&tag))
259 .collect::<Vec<_>>(),
260 )
261 .into_value(&tag),
262 );
263 }
264 }
265 }
266
267 x.push(UntaggedValue::table(&y).into_value(&tag));
268 }
269
270 Ok(UntaggedValue::table(&x).into_value(&tag))
271}
272
273pub enum Reduction {
274 Count,
275 Accumulate,
276}
277
278pub fn reduce(
279 values: &Value,
280 reduction_with: &Reduction,
281 tag: impl Into<Tag>,
282) -> Result<Value, ShellError> {
283 let tag = tag.into();
284 let reduce_with = reducer_for(reduction_with);
285
286 let mut datasets = vec![];
287 for dataset in values.table_entries() {
288 let mut acc = UntaggedValue::int(0).into_value(&tag);
289
290 let mut subsets = vec![];
291 for subset in dataset.table_entries() {
292 acc = reduce_with(&acc, subset.table_entries().collect::<Vec<_>>())?;
293 subsets.push(acc.clone());
294 }
295 datasets.push(UntaggedValue::table(&subsets).into_value(&tag));
296 }
297
298 Ok(UntaggedValue::table(&datasets).into_value(&tag))
299}
300
301pub fn percentages(
302 maxima: &Value,
303 values: &Value,
304 tag: impl Into<Tag>,
305) -> Result<Value, ShellError> {
306 let tag = tag.into();
307
308 let mut x = vec![];
309 for split in values.table_entries() {
310 x.push(
311 UntaggedValue::table(
312 &split
313 .table_entries()
314 .filter_map(|s| {
315 let hundred = UntaggedValue::decimal_from_float(100.0, tag.span);
316
317 match unsafe_compute_values(Operator::Divide, &hundred, maxima) {
318 Ok(v) => match unsafe_compute_values(Operator::Multiply, s, &v) {
319 Ok(v) => Some(v.into_untagged_value()),
320 Err(_) => None,
321 },
322 Err(_) => None,
323 }
324 })
325 .collect::<Vec<_>>(),
326 )
327 .into_value(&tag),
328 );
329 }
330
331 Ok(UntaggedValue::table(&x).into_value(&tag))
332}