1use crate::builtins::{self, err, text_of};
15use crate::CellValue;
16use improv_core_model::FuncId;
17
18mod analysis;
19mod array;
20mod financial;
21mod lookup;
22mod statistical;
23
24#[derive(Debug, Clone, PartialEq)]
26pub struct Rows {
27 pub pos: Vec<Vec<u32>>,
30 pub vals: Vec<Vec<CellValue>>,
32 pub capacity: usize,
35}
36
37impl Rows {
38 pub fn col(&self, i: usize) -> impl Iterator<Item = &CellValue> + '_ {
40 self.vals.iter().filter_map(move |r| r.get(i))
41 }
42
43 pub fn first_error(&self) -> Option<CellValue> {
45 self.vals
46 .iter()
47 .flatten()
48 .find(|v| matches!(v, CellValue::Err(_)))
49 .cloned()
50 }
51
52 pub fn len(&self) -> usize {
54 self.vals.len()
55 }
56
57 pub fn is_empty(&self) -> bool {
59 self.vals.is_empty()
60 }
61}
62
63pub fn reduce(func: FuncId, rows: &Rows, params: &[CellValue]) -> CellValue {
65 if (1200..=1249).contains(&func.0) {
66 return lookup::reduce(func.0, rows, params);
69 }
70 if let Some(bad) = params.iter().find(|v| matches!(v, CellValue::Err(_))) {
71 return bad.clone();
72 }
73 match func.0 {
74 1..=9 => core(func.0, rows),
75 906..=909 => ordered(func.0, rows, params),
76 1000..=1149 => statistical::reduce(func.0, rows, params),
77 1150..=1199 => financial::reduce(func.0, rows, params),
78 1850..=1999 => analysis::reduce(func.0, rows, params),
79 _ => CellValue::Err(err::NAME),
80 }
81}
82
83pub fn per_item(func: FuncId, rows: &Rows, params: &[CellValue]) -> Vec<CellValue> {
86 if let Some(bad) = params.iter().find(|v| matches!(v, CellValue::Err(_))) {
87 return vec![bad.clone(); rows.len()];
88 }
89 match func.0 {
91 1000..=1149 => statistical::per_item(func.0, rows, params),
92 1850..=1999 => analysis::per_item(func.0, rows, params),
93 _ => vec![CellValue::Err(err::NAME); rows.len()],
94 }
95}
96
97pub fn array(func: FuncId, rows: &Rows, params: &[CellValue]) -> Vec<CellValue> {
100 if let Some(bad) = params.iter().find(|v| matches!(v, CellValue::Err(_))) {
101 return vec![bad.clone()];
102 }
103 match func.0 {
104 1250..=1299 => array::array(func.0, rows, params),
105 1850..=1999 => analysis::array(func.0, rows, params),
106 _ => vec![CellValue::Err(err::NAME)],
107 }
108}
109
110fn core(id: u32, rows: &Rows) -> CellValue {
113 if let Some(bad) = rows.first_error() {
114 return bad;
115 }
116 let twin = match id {
117 1 => 22,
118 2 => 23,
119 3 => 20,
120 4 => 21,
121 5 => 24,
122 6 => 25,
123 7 => 26,
124 8 => 27,
125 _ => 28,
126 };
127 let vals: Vec<CellValue> = rows.col(0).cloned().collect();
128 builtins::eval(FuncId(twin), &vals)
129}
130
131fn ordered(id: u32, rows: &Rows, params: &[CellValue]) -> CellValue {
133 match id {
134 906 | 908 => {
135 let Some(v) = rows.col(0).next().cloned() else {
136 return CellValue::Err(err::NA);
137 };
138 if id == 908 {
139 return match v {
140 CellValue::Text(_) | CellValue::Err(_) => v,
141 _ => CellValue::Text(String::new()),
142 };
143 }
144 v
145 }
146 907 => rows
147 .col(0)
148 .last()
149 .cloned()
150 .unwrap_or(CellValue::Err(err::NA)),
151 _ => {
152 let sep = match params.first().map(text_of) {
154 Some(Ok(s)) => s,
155 Some(Err(c)) => return CellValue::Err(c),
156 None => String::new(),
157 };
158 let skip = matches!(params.get(1), Some(CellValue::Bool(true)));
159 let mut parts = Vec::new();
160 for v in rows.col(0) {
161 match text_of(v) {
162 Ok(s) if skip && s.is_empty() => {}
163 Ok(s) => parts.push(s),
164 Err(c) => return CellValue::Err(c),
165 }
166 }
167 let joined = parts.join(&sep);
168 if joined.chars().count() > 32_767 {
169 return CellValue::Err(err::VALUE);
170 }
171 CellValue::Text(joined)
172 }
173 }
174}
175
176#[cfg(test)]
177#[cfg_attr(coverage_nightly, coverage(off))]
178mod tests {
179 use super::*;
180
181 fn rows(vals: Vec<CellValue>) -> Rows {
182 Rows {
183 pos: (0..vals.len() as u32).map(|i| vec![i]).collect(),
184 vals: vals.into_iter().map(|v| vec![v]).collect(),
185 capacity: 4,
186 }
187 }
188
189 #[test]
190 fn core_and_ordered() {
191 let n = CellValue::num;
192 let r = rows(vec![n(4.0), n(1.0), n(7.0)]);
193 assert_eq!(reduce(FuncId(1), &r, &[]), n(12.0));
194 assert_eq!(reduce(FuncId(906), &r, &[]), n(4.0));
195 assert_eq!(reduce(FuncId(907), &r, &[]), n(7.0));
196 let t = rows(vec![
197 CellValue::Text("a".into()),
198 CellValue::Text(String::new()),
199 n(2.0),
200 ]);
201 let sep = CellValue::Text(", ".into());
202 assert_eq!(
203 reduce(FuncId(909), &t, &[sep.clone(), CellValue::Bool(true)]),
204 CellValue::Text("a, 2".into())
205 );
206 assert_eq!(
207 reduce(FuncId(909), &t, &[sep, CellValue::Bool(false)]),
208 CellValue::Text("a, , 2".into())
209 );
210 assert_eq!(reduce(FuncId(908), &r, &[]), CellValue::Text(String::new()));
211 assert_eq!(reduce(FuncId(908), &t, &[]), CellValue::Text("a".into()));
212 let e = rows(vec![n(1.0), CellValue::Err(err::NA)]);
213 assert_eq!(reduce(FuncId(1), &e, &[]), CellValue::Err(err::NA));
214 assert_eq!(
215 reduce(FuncId(1), &r, &[CellValue::Err(err::DIV0)]),
216 CellValue::Err(err::DIV0)
217 );
218 assert_eq!(reduce(FuncId(999), &r, &[]), CellValue::Err(err::NAME));
219 assert_eq!(
220 per_item(FuncId(999), &r, &[]),
221 vec![CellValue::Err(err::NAME); 3]
222 );
223 assert_eq!(
224 per_item(FuncId(999), &r, &[CellValue::Err(err::NA)]),
225 vec![CellValue::Err(err::NA); 3]
226 );
227 assert_eq!(r.len(), 3);
228 assert!(!r.is_empty());
229 assert_eq!(
231 array(FuncId(1250), &r, &[CellValue::Err(err::NA)]),
232 vec![CellValue::Err(err::NA)]
233 );
234 let te = rows(vec![CellValue::Text("a".into()), CellValue::Err(err::NUM)]);
236 assert_eq!(
237 reduce(FuncId(909), &te, &[CellValue::Text(",".into())]),
238 CellValue::Err(err::NUM)
239 );
240 assert_eq!(reduce(FuncId(909), &t, &[]), CellValue::Text("a2".into()));
241 let long = rows(vec![CellValue::Text("x".repeat(20_000)); 2]);
242 assert_eq!(
243 reduce(FuncId(909), &long, &[CellValue::Text(String::new())]),
244 CellValue::Err(err::VALUE)
245 );
246 assert_eq!(reduce(FuncId(908), &e, &[]), CellValue::Text(String::new()));
247 let er = rows(vec![CellValue::Err(err::NA)]);
248 assert_eq!(reduce(FuncId(908), &er, &[]), CellValue::Err(err::NA));
249 assert_eq!(e.first_error(), Some(CellValue::Err(err::NA)));
250 let empty = rows(vec![]);
251 assert_eq!(reduce(FuncId(906), &empty, &[]), CellValue::Err(err::NA));
252 assert_eq!(reduce(FuncId(907), &empty, &[]), CellValue::Err(err::NA));
253 }
254}