1use crate::broadcast::{Shape2D, broadcast_shape, project_index};
3use crate::traits::{ArgumentHandle, FunctionContext};
4use formualizer_common::{ExcelError, ExcelErrorKind, LiteralValue};
5
6pub struct SimpleMapCtx<'a, 'b> {
14 args: &'a [ArgumentHandle<'a, 'b>],
15 ctx: &'a dyn FunctionContext,
16 shape: (usize, usize),
17 output_rows: Vec<Vec<LiteralValue>>,
18}
19
20impl<'a, 'b> SimpleMapCtx<'a, 'b> {
21 pub fn new(args: &'a [ArgumentHandle<'a, 'b>], ctx: &'a dyn FunctionContext) -> Self {
22 let mut shapes: Vec<Shape2D> = Vec::with_capacity(args.len().max(1));
24 if args.is_empty() {
25 shapes.push((1, 1));
26 } else {
27 for a in args.iter() {
28 if let Ok(rv) = a.range_view() {
29 shapes.push(rv.dims());
30 } else if let Ok(v) = a.value() {
31 if let LiteralValue::Array(arr) = v.as_ref() {
32 let rows = arr.len();
33 let cols = arr.first().map(|r| r.len()).unwrap_or(0);
34 shapes.push((rows, cols));
35 } else {
36 shapes.push((1, 1));
37 }
38 } else {
39 shapes.push((1, 1));
40 }
41 }
42 }
43 let shape = broadcast_shape(&shapes).unwrap_or((1, 1));
44 Self {
45 args,
46 ctx,
47 shape,
48 output_rows: Vec::new(),
49 }
50 }
51
52 pub fn input_count(&self) -> usize {
53 self.args.len()
54 }
55
56 pub fn broadcast_shape(&self) -> (usize, usize) {
57 self.shape
58 }
59
60 pub fn is_array_context(&self) -> bool {
61 self.shape != (1, 1)
62 }
63
64 pub fn map_unary_numeric<F>(&mut self, mut f: F) -> Result<(), ExcelError>
67 where
68 F: FnMut(f64) -> Result<LiteralValue, ExcelError>,
69 {
70 if self.args.is_empty() {
71 return Err(ExcelError::new(ExcelErrorKind::Value)
72 .with_message("No arguments provided to elementwise function"));
73 }
74
75 let first = &self.args[0];
78 if let Ok(view) = first.range_view() {
79 let (rows, _cols) = self.shape;
80 let mut row_idx = 0usize;
81 self.output_rows.clear();
82 view.for_each_row(&mut |row| {
83 let mut out_row: Vec<LiteralValue> = Vec::with_capacity(row.len());
84 for cell in row.iter() {
85 let num_opt = match cell {
86 LiteralValue::Error(e) => return Err(e.clone()),
87 other => crate::coercion::to_number_lenient_with_locale(
88 other,
89 &self.ctx.locale(),
90 )
91 .ok(),
92 };
93 match num_opt {
94 Some(n) => out_row.push(f(n)?),
95 None => out_row.push(LiteralValue::Error(
96 ExcelError::new(ExcelErrorKind::Value)
97 .with_message("Element is not coercible to number"),
98 )),
99 }
100 }
101 self.output_rows.push(out_row);
102 row_idx += 1;
103 Ok(())
104 })?;
105 if self.output_rows.is_empty() && rows == 0 {
107 self.output_rows = Vec::new();
108 }
109 return Ok(());
110 }
111
112 if let Ok(v) = first.value() {
114 if let LiteralValue::Array(arr) = v.clone().into_owned() {
115 self.output_rows.clear();
116 for row in arr.into_iter() {
117 let mut out_row: Vec<LiteralValue> = Vec::with_capacity(row.len());
118 for cell in row.into_iter() {
119 let num_opt = match cell {
120 LiteralValue::Error(e) => return Err(e),
121 other => crate::coercion::to_number_lenient_with_locale(
122 &other,
123 &self.ctx.locale(),
124 )
125 .ok(),
126 };
127 match num_opt {
128 Some(n) => out_row.push(f(n)?),
129 None => out_row.push(LiteralValue::Error(
130 ExcelError::new(ExcelErrorKind::Value)
131 .with_message("Element is not coercible to number"),
132 )),
133 }
134 }
135 self.output_rows.push(out_row);
136 }
137 return Ok(());
138 }
139 match v.as_ref() {
141 LiteralValue::Error(e) => return Err(e.clone()),
142 other => {
143 let as_num =
144 crate::coercion::to_number_lenient_with_locale(other, &self.ctx.locale())
145 .ok();
146 let out = match as_num {
147 Some(n) => f(n)?,
148 None => LiteralValue::Error(
149 ExcelError::new(ExcelErrorKind::Value)
150 .with_message("Value is not coercible to number"),
151 ),
152 };
153 self.output_rows.clear();
154 self.output_rows.push(vec![out]);
155 self.shape = (1, 1);
156 return Ok(());
157 }
158 }
159 }
160
161 Err(ExcelError::new(ExcelErrorKind::Value)
163 .with_message("No array or scalar value provided for elementwise map"))
164 }
165
166 pub fn map_binary_numeric<F>(&mut self, mut f: F) -> Result<(), ExcelError>
168 where
169 F: FnMut(f64, f64) -> Result<LiteralValue, ExcelError>,
170 {
171 if self.args.len() < 2 {
172 return Err(ExcelError::new(ExcelErrorKind::Value)
173 .with_message("Binary elementwise function requires two args"));
174 }
175 let a0 = &self.args[0];
176 let a1 = &self.args[1];
177 let target = self.shape;
178 self.output_rows.clear();
179
180 let to_array = |ah: &ArgumentHandle| -> Result<Vec<Vec<LiteralValue>>, ExcelError> {
182 if let Ok(rv) = ah.range_view() {
183 let mut rows: Vec<Vec<LiteralValue>> = Vec::new();
184 rv.for_each_row(&mut |row| {
185 rows.push(row.to_vec());
186 Ok(())
187 })?;
188 Ok(rows)
189 } else {
190 let v = ah.value()?;
191 Ok(match v.as_ref() {
192 LiteralValue::Array(arr) => arr.clone(),
193 other => vec![vec![other.clone()]],
194 })
195 }
196 };
197
198 let arr0 = to_array(a0)?;
199 let arr1 = to_array(a1)?;
200 let shape0 = (arr0.len(), arr0.first().map(|r| r.len()).unwrap_or(0));
201 let shape1 = (arr1.len(), arr1.first().map(|r| r.len()).unwrap_or(0));
202 let _ = broadcast_shape(&[shape0, shape1])?; for r in 0..target.0 {
205 let mut out_row = Vec::with_capacity(target.1);
206 for c in 0..target.1 {
207 let (r0, c0) = project_index((r, c), shape0);
208 let (r1, c1) = project_index((r, c), shape1);
209 let lv0 = arr0
210 .get(r0)
211 .and_then(|row| row.get(c0))
212 .cloned()
213 .unwrap_or(LiteralValue::Empty);
214 let lv1 = arr1
215 .get(r1)
216 .and_then(|row| row.get(c1))
217 .cloned()
218 .unwrap_or(LiteralValue::Empty);
219
220 let n0 = match &lv0 {
221 LiteralValue::Number(n) => Some(*n),
222 LiteralValue::Int(i) => Some(*i as f64),
223 LiteralValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
224 LiteralValue::Empty => Some(0.0),
225 LiteralValue::Text(s) => s.trim().parse::<f64>().ok(),
226 LiteralValue::Error(e) => return Err(e.clone()),
227 _ => None,
228 };
229 let n1 = match &lv1 {
230 LiteralValue::Number(n) => Some(*n),
231 LiteralValue::Int(i) => Some(*i as f64),
232 LiteralValue::Boolean(b) => Some(if *b { 1.0 } else { 0.0 }),
233 LiteralValue::Empty => Some(0.0),
234 LiteralValue::Text(s) => s.trim().parse::<f64>().ok(),
235 LiteralValue::Error(e) => return Err(e.clone()),
236 _ => None,
237 };
238 let out_cell = match (n0, n1) {
239 (Some(a), Some(b)) => f(a, b)?,
240 _ => LiteralValue::Error(
241 ExcelError::new(ExcelErrorKind::Value)
242 .with_message("Elements are not coercible to numbers"),
243 ),
244 };
245 out_row.push(out_cell);
246 }
247 self.output_rows.push(out_row);
248 }
249 Ok(())
250 }
251
252 pub fn take_output(self) -> LiteralValue {
253 if self.shape == (1, 1) {
254 if let Some(row) = self.output_rows.first() {
255 if let Some(cell) = row.first() {
256 return cell.clone();
257 }
258 }
259 LiteralValue::Empty
260 } else {
261 LiteralValue::Array(self.output_rows)
262 }
263 }
264}
265
266impl<'a, 'b> crate::function::FnMapCtx for SimpleMapCtx<'a, 'b> {
267 fn is_array_context(&self) -> bool {
268 self.is_array_context()
269 }
270
271 fn map_unary_numeric(
272 &mut self,
273 f: &mut dyn FnMut(f64) -> Result<LiteralValue, ExcelError>,
274 ) -> Result<(), ExcelError> {
275 self.map_unary_numeric(f)
276 }
277
278 fn map_binary_numeric(
279 &mut self,
280 f: &mut dyn FnMut(f64, f64) -> Result<LiteralValue, ExcelError>,
281 ) -> Result<(), ExcelError> {
282 self.map_binary_numeric(f)
283 }
284
285 fn finalize(&mut self) -> LiteralValue {
286 let rows = std::mem::take(&mut self.output_rows);
288 if self.shape == (1, 1) {
289 LiteralValue::Empty
290 } else {
291 LiteralValue::Array(rows)
292 }
293 }
294}