formualizer_eval/engine/arena/
array.rs

1/// Array arena for efficient storage of 2D arrays
2/// Arrays are stored in flattened form with separate dimension tracking
3use super::value_ref::ValueRef;
4use std::fmt;
5
6/// Reference to an array in the arena
7#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
8pub struct ArrayRef(pub u32);
9
10impl ArrayRef {
11    pub fn as_u32(self) -> u32 {
12        self.0
13    }
14}
15
16impl fmt::Display for ArrayRef {
17    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18        write!(f, "ArrayRef({})", self.0)
19    }
20}
21
22/// Arena for storing 2D arrays
23#[derive(Debug)]
24pub struct ArrayArena {
25    /// Array dimensions (rows, cols)
26    dimensions: Vec<(u32, u32)>,
27    /// Flattened array elements (all arrays concatenated)
28    elements: Vec<ValueRef>,
29    /// Offset into elements for each array
30    offsets: Vec<u32>,
31}
32
33impl ArrayArena {
34    pub fn new() -> Self {
35        Self {
36            dimensions: Vec::new(),
37            elements: Vec::new(),
38            offsets: vec![0], // Start with offset 0
39        }
40    }
41
42    pub fn with_capacity(array_count: usize) -> Self {
43        Self {
44            dimensions: Vec::with_capacity(array_count),
45            elements: Vec::with_capacity(array_count * 10), // Assume avg 10 elements per array
46            offsets: {
47                let mut v = Vec::with_capacity(array_count + 1);
48                v.push(0);
49                v
50            },
51        }
52    }
53
54    /// Insert a 2D array, returning its reference
55    pub fn insert(&mut self, rows: u32, cols: u32, elements: Vec<ValueRef>) -> ArrayRef {
56        let expected_len = (rows * cols) as usize;
57        assert_eq!(
58            elements.len(),
59            expected_len,
60            "Array dimensions {}x{} don't match element count {}",
61            rows,
62            cols,
63            elements.len()
64        );
65
66        let index = self.dimensions.len() as u32;
67        if index == u32::MAX {
68            panic!("Array arena overflow: too many arrays");
69        }
70
71        // Store dimensions
72        self.dimensions.push((rows, cols));
73
74        // Store elements
75        self.elements.extend(elements);
76
77        // Update offsets
78        let new_offset = self.elements.len() as u32;
79        self.offsets.push(new_offset);
80
81        ArrayRef(index)
82    }
83
84    /// Insert a 2D array from nested vectors
85    pub fn insert_2d(&mut self, array: Vec<Vec<ValueRef>>) -> ArrayRef {
86        if array.is_empty() {
87            return self.insert(0, 0, Vec::new());
88        }
89
90        let rows = array.len() as u32;
91        let cols = array[0].len() as u32;
92
93        // Verify all rows have same length
94        for (i, row) in array.iter().enumerate() {
95            assert_eq!(
96                row.len(),
97                cols as usize,
98                "Row {} has {} columns, expected {}",
99                i,
100                row.len(),
101                cols
102            );
103        }
104
105        // Flatten the array
106        let elements: Vec<ValueRef> = array.into_iter().flatten().collect();
107        self.insert(rows, cols, elements)
108    }
109
110    /// Get array dimensions
111    #[inline]
112    pub fn dimensions(&self, r: ArrayRef) -> Option<(u32, u32)> {
113        self.dimensions.get(r.0 as usize).copied()
114    }
115
116    /// Get array elements as a slice
117    #[inline]
118    pub fn elements(&self, r: ArrayRef) -> Option<&[ValueRef]> {
119        let index = r.0 as usize;
120        if index >= self.dimensions.len() {
121            return None;
122        }
123
124        let start = self.offsets[index] as usize;
125        let end = self.offsets[index + 1] as usize;
126        Some(&self.elements[start..end])
127    }
128
129    /// Get a specific element from an array
130    #[inline]
131    pub fn get_element(&self, r: ArrayRef, row: u32, col: u32) -> Option<ValueRef> {
132        let (rows, cols) = self.dimensions(r)?;
133        if row >= rows || col >= cols {
134            return None;
135        }
136
137        let elements = self.elements(r)?;
138        let index = (row * cols + col) as usize;
139        elements.get(index).copied()
140    }
141
142    /// Reconstruct a 2D array from its reference
143    pub fn get_2d(&self, r: ArrayRef) -> Option<Vec<Vec<ValueRef>>> {
144        let (rows, cols) = self.dimensions(r)?;
145        let elements = self.elements(r)?;
146
147        if rows == 0 || cols == 0 {
148            return Some(Vec::new());
149        }
150
151        let mut result = Vec::with_capacity(rows as usize);
152        for row in 0..rows {
153            let start = (row * cols) as usize;
154            let end = start + cols as usize;
155            result.push(elements[start..end].to_vec());
156        }
157
158        Some(result)
159    }
160
161    /// Returns the number of arrays stored
162    pub fn len(&self) -> usize {
163        self.dimensions.len()
164    }
165
166    /// Returns true if the arena is empty
167    pub fn is_empty(&self) -> bool {
168        self.dimensions.is_empty()
169    }
170
171    /// Returns the total number of elements across all arrays
172    pub fn total_elements(&self) -> usize {
173        self.elements.len()
174    }
175
176    /// Returns memory usage in bytes (approximate)
177    pub fn memory_usage(&self) -> usize {
178        self.dimensions.capacity() * std::mem::size_of::<(u32, u32)>()
179            + self.elements.capacity() * std::mem::size_of::<ValueRef>()
180            + self.offsets.capacity() * std::mem::size_of::<u32>()
181    }
182
183    /// Clear all arrays from the arena
184    pub fn clear(&mut self) {
185        self.dimensions.clear();
186        self.elements.clear();
187        self.offsets.clear();
188        self.offsets.push(0);
189    }
190}
191
192impl Default for ArrayArena {
193    fn default() -> Self {
194        Self::new()
195    }
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_array_arena_basic() {
204        let mut arena = ArrayArena::new();
205
206        // Create dummy ValueRefs for testing
207        let elements = vec![
208            ValueRef::from_raw(1),
209            ValueRef::from_raw(2),
210            ValueRef::from_raw(3),
211            ValueRef::from_raw(4),
212        ];
213
214        let array_ref = arena.insert(2, 2, elements);
215
216        assert_eq!(arena.dimensions(array_ref), Some((2, 2)));
217        assert_eq!(arena.len(), 1);
218        assert_eq!(arena.total_elements(), 4);
219    }
220
221    #[test]
222    fn test_array_arena_2d_insert() {
223        let mut arena = ArrayArena::new();
224
225        let array = vec![
226            vec![
227                ValueRef::from_raw(1),
228                ValueRef::from_raw(2),
229                ValueRef::from_raw(3),
230            ],
231            vec![
232                ValueRef::from_raw(4),
233                ValueRef::from_raw(5),
234                ValueRef::from_raw(6),
235            ],
236        ];
237
238        let array_ref = arena.insert_2d(array);
239
240        assert_eq!(arena.dimensions(array_ref), Some((2, 3)));
241
242        // Check individual elements
243        assert_eq!(
244            arena.get_element(array_ref, 0, 0),
245            Some(ValueRef::from_raw(1))
246        );
247        assert_eq!(
248            arena.get_element(array_ref, 0, 2),
249            Some(ValueRef::from_raw(3))
250        );
251        assert_eq!(
252            arena.get_element(array_ref, 1, 0),
253            Some(ValueRef::from_raw(4))
254        );
255        assert_eq!(
256            arena.get_element(array_ref, 1, 2),
257            Some(ValueRef::from_raw(6))
258        );
259    }
260
261    #[test]
262    fn test_array_arena_get_2d() {
263        let mut arena = ArrayArena::new();
264
265        let original = vec![
266            vec![ValueRef::from_raw(1), ValueRef::from_raw(2)],
267            vec![ValueRef::from_raw(3), ValueRef::from_raw(4)],
268            vec![ValueRef::from_raw(5), ValueRef::from_raw(6)],
269        ];
270
271        let array_ref = arena.insert_2d(original.clone());
272        let retrieved = arena.get_2d(array_ref).unwrap();
273
274        assert_eq!(retrieved, original);
275    }
276
277    #[test]
278    fn test_array_arena_multiple_arrays() {
279        let mut arena = ArrayArena::new();
280
281        let arr1 = vec![vec![ValueRef::from_raw(1), ValueRef::from_raw(2)]];
282        let arr2 = vec![
283            vec![ValueRef::from_raw(3)],
284            vec![ValueRef::from_raw(4)],
285            vec![ValueRef::from_raw(5)],
286        ];
287        let arr3 = vec![
288            vec![
289                ValueRef::from_raw(6),
290                ValueRef::from_raw(7),
291                ValueRef::from_raw(8),
292            ],
293            vec![
294                ValueRef::from_raw(9),
295                ValueRef::from_raw(10),
296                ValueRef::from_raw(11),
297            ],
298        ];
299
300        let ref1 = arena.insert_2d(arr1.clone());
301        let ref2 = arena.insert_2d(arr2.clone());
302        let ref3 = arena.insert_2d(arr3.clone());
303
304        assert_eq!(arena.len(), 3);
305        assert_eq!(arena.total_elements(), 11);
306
307        assert_eq!(arena.get_2d(ref1), Some(arr1));
308        assert_eq!(arena.get_2d(ref2), Some(arr2));
309        assert_eq!(arena.get_2d(ref3), Some(arr3));
310    }
311
312    #[test]
313    fn test_array_arena_empty_array() {
314        let mut arena = ArrayArena::new();
315
316        let array_ref = arena.insert(0, 0, Vec::new());
317
318        assert_eq!(arena.dimensions(array_ref), Some((0, 0)));
319        assert_eq!(arena.elements(array_ref), Some(&[][..]));
320        assert_eq!(arena.get_2d(array_ref), Some(Vec::new()));
321    }
322
323    #[test]
324    fn test_array_arena_single_element() {
325        let mut arena = ArrayArena::new();
326
327        let array_ref = arena.insert(1, 1, vec![ValueRef::from_raw(42)]);
328
329        assert_eq!(arena.dimensions(array_ref), Some((1, 1)));
330        assert_eq!(
331            arena.get_element(array_ref, 0, 0),
332            Some(ValueRef::from_raw(42))
333        );
334        assert_eq!(
335            arena.get_2d(array_ref),
336            Some(vec![vec![ValueRef::from_raw(42)]])
337        );
338    }
339
340    #[test]
341    fn test_array_arena_out_of_bounds() {
342        let mut arena = ArrayArena::new();
343
344        let array_ref = arena.insert(2, 3, vec![ValueRef::from_raw(0); 6]);
345
346        assert_eq!(
347            arena.get_element(array_ref, 0, 0),
348            Some(ValueRef::from_raw(0))
349        );
350        assert_eq!(
351            arena.get_element(array_ref, 1, 2),
352            Some(ValueRef::from_raw(0))
353        );
354        assert_eq!(arena.get_element(array_ref, 2, 0), None); // Row out of bounds
355        assert_eq!(arena.get_element(array_ref, 0, 3), None); // Col out of bounds
356    }
357
358    #[test]
359    #[should_panic(expected = "Array dimensions 2x3 don't match element count 5")]
360    fn test_array_arena_dimension_mismatch() {
361        let mut arena = ArrayArena::new();
362        arena.insert(2, 3, vec![ValueRef::from_raw(0); 5]);
363    }
364
365    #[test]
366    #[should_panic(expected = "Row 1 has 2 columns, expected 3")]
367    fn test_array_arena_inconsistent_rows() {
368        let mut arena = ArrayArena::new();
369
370        let array = vec![
371            vec![
372                ValueRef::from_raw(1),
373                ValueRef::from_raw(2),
374                ValueRef::from_raw(3),
375            ],
376            vec![ValueRef::from_raw(4), ValueRef::from_raw(5)], // Wrong length
377        ];
378
379        arena.insert_2d(array);
380    }
381
382    #[test]
383    fn test_array_arena_clear() {
384        let mut arena = ArrayArena::new();
385
386        arena.insert_2d(vec![vec![ValueRef::from_raw(1), ValueRef::from_raw(2)]]);
387        arena.insert_2d(vec![vec![ValueRef::from_raw(3), ValueRef::from_raw(4)]]);
388
389        assert_eq!(arena.len(), 2);
390        assert_eq!(arena.total_elements(), 4);
391
392        arena.clear();
393
394        assert_eq!(arena.len(), 0);
395        assert_eq!(arena.total_elements(), 0);
396        assert!(arena.is_empty());
397    }
398
399    #[test]
400    fn test_array_arena_memory_usage() {
401        let mut arena = ArrayArena::with_capacity(10);
402
403        let initial_memory = arena.memory_usage();
404
405        for i in 0..10 {
406            arena.insert(10, 10, vec![ValueRef::from_raw(i); 100]);
407        }
408
409        let final_memory = arena.memory_usage();
410        assert!(final_memory >= initial_memory);
411    }
412}