formualizer_eval/engine/arena/
scalar.rs

1/// Scalar arena for efficient storage of numeric values
2/// Stores f64 numbers and i64 integers in separate dense arrays
3use std::fmt;
4
5/// Reference to a value in the scalar arena
6#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
7pub struct ScalarRef {
8    /// Bit 31: 0 = float, 1 = integer
9    /// Bits 30-0: Index into respective array
10    pub raw: u32,
11}
12
13impl ScalarRef {
14    const TYPE_MASK: u32 = 1 << 31;
15    const INDEX_MASK: u32 = !Self::TYPE_MASK;
16
17    fn float(index: u32) -> Self {
18        debug_assert!(index <= Self::INDEX_MASK, "Float index overflow");
19        Self { raw: index }
20    }
21
22    fn integer(index: u32) -> Self {
23        debug_assert!(index <= Self::INDEX_MASK, "Integer index overflow");
24        Self {
25            raw: index | Self::TYPE_MASK,
26        }
27    }
28
29    pub fn from_integer_index(index: u32) -> Self {
30        Self {
31            raw: index | Self::TYPE_MASK,
32        }
33    }
34
35    pub fn from_float_index(index: u32) -> Self {
36        Self { raw: index }
37    }
38
39    pub fn is_float(self) -> bool {
40        self.raw & Self::TYPE_MASK == 0
41    }
42
43    pub fn is_integer(self) -> bool {
44        !self.is_float()
45    }
46
47    fn index(self) -> usize {
48        (self.raw & Self::INDEX_MASK) as usize
49    }
50}
51
52/// Arena for storing scalar values (numbers and integers)
53#[derive(Debug)]
54pub struct ScalarArena {
55    floats: Vec<f64>,
56    integers: Vec<i64>,
57}
58
59impl ScalarArena {
60    pub fn new() -> Self {
61        Self {
62            floats: Vec::new(),
63            integers: Vec::new(),
64        }
65    }
66
67    pub fn with_capacity(cap: usize) -> Self {
68        Self {
69            floats: Vec::with_capacity(cap / 2),
70            integers: Vec::with_capacity(cap / 2),
71        }
72    }
73
74    /// Insert a floating-point number
75    pub fn insert_float(&mut self, value: f64) -> ScalarRef {
76        let index = self.floats.len() as u32;
77        if index > ScalarRef::INDEX_MASK {
78            panic!("Scalar arena float overflow: too many values");
79        }
80        self.floats.push(value);
81        ScalarRef::float(index)
82    }
83
84    /// Insert an integer
85    pub fn insert_integer(&mut self, value: i64) -> ScalarRef {
86        let index = self.integers.len() as u32;
87        if index > ScalarRef::INDEX_MASK {
88            panic!("Scalar arena integer overflow: too many values");
89        }
90        self.integers.push(value);
91        ScalarRef::integer(index)
92    }
93
94    /// Get a float value by reference
95    #[inline]
96    pub fn get_float(&self, r: ScalarRef) -> Option<f64> {
97        if r.is_float() {
98            self.floats.get(r.index()).copied()
99        } else {
100            None
101        }
102    }
103
104    /// Get an integer value by reference
105    #[inline]
106    pub fn get_integer(&self, r: ScalarRef) -> Option<i64> {
107        if r.is_integer() {
108            self.integers.get(r.index()).copied()
109        } else {
110            None
111        }
112    }
113
114    /// Get any scalar value as f64 (integers are converted)
115    #[inline]
116    pub fn get_as_float(&self, r: ScalarRef) -> Option<f64> {
117        if r.is_float() {
118            self.floats.get(r.index()).copied()
119        } else {
120            self.integers.get(r.index()).map(|i| *i as f64)
121        }
122    }
123
124    /// Returns the total number of scalars stored
125    pub fn len(&self) -> usize {
126        self.floats.len() + self.integers.len()
127    }
128
129    /// Returns true if the arena is empty
130    pub fn is_empty(&self) -> bool {
131        self.floats.is_empty() && self.integers.is_empty()
132    }
133
134    /// Returns memory usage in bytes
135    pub fn memory_usage(&self) -> usize {
136        self.floats.capacity() * std::mem::size_of::<f64>()
137            + self.integers.capacity() * std::mem::size_of::<i64>()
138    }
139
140    /// Clear all values from the arena
141    pub fn clear(&mut self) {
142        self.floats.clear();
143        self.integers.clear();
144    }
145}
146
147impl Default for ScalarArena {
148    fn default() -> Self {
149        Self::new()
150    }
151}
152
153impl fmt::Display for ScalarRef {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155        if self.is_float() {
156            write!(f, "Float({})", self.index())
157        } else {
158            write!(f, "Int({})", self.index())
159        }
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166
167    #[test]
168    fn test_scalar_arena_float_alloc() {
169        let mut arena = ScalarArena::new();
170        let ref1 = arena.insert_float(42.0);
171        let ref2 = arena.insert_float(3.14);
172
173        assert_eq!(arena.get_float(ref1), Some(42.0));
174        assert_eq!(arena.get_float(ref2), Some(3.14));
175        assert_eq!(arena.len(), 2);
176    }
177
178    #[test]
179    fn test_scalar_arena_integer_alloc() {
180        let mut arena = ScalarArena::new();
181        let ref1 = arena.insert_integer(42);
182        let ref2 = arena.insert_integer(-100);
183
184        assert_eq!(arena.get_integer(ref1), Some(42));
185        assert_eq!(arena.get_integer(ref2), Some(-100));
186        assert_eq!(arena.len(), 2);
187    }
188
189    #[test]
190    fn test_scalar_arena_mixed_types() {
191        let mut arena = ScalarArena::new();
192        let float_ref = arena.insert_float(3.14);
193        let int_ref = arena.insert_integer(42);
194
195        assert!(float_ref.is_float());
196        assert!(!float_ref.is_integer());
197        assert!(int_ref.is_integer());
198        assert!(!int_ref.is_float());
199
200        assert_eq!(arena.get_float(float_ref), Some(3.14));
201        assert_eq!(arena.get_integer(int_ref), Some(42));
202
203        // Wrong type access returns None
204        assert_eq!(arena.get_float(int_ref), None);
205        assert_eq!(arena.get_integer(float_ref), None);
206    }
207
208    #[test]
209    fn test_scalar_arena_capacity() {
210        let mut arena = ScalarArena::with_capacity(1000);
211        let refs: Vec<_> = (0..10_000)
212            .map(|i| {
213                if i % 2 == 0 {
214                    arena.insert_float(i as f64)
215                } else {
216                    arena.insert_integer(i as i64)
217                }
218            })
219            .collect();
220
221        // Verify all values retained
222        for (i, r) in refs.iter().enumerate() {
223            if i % 2 == 0 {
224                assert_eq!(arena.get_float(*r), Some(i as f64));
225            } else {
226                assert_eq!(arena.get_integer(*r), Some(i as i64));
227            }
228        }
229
230        assert_eq!(arena.len(), 10_000);
231    }
232
233    #[test]
234    fn test_scalar_arena_get_as_float() {
235        let mut arena = ScalarArena::new();
236        let float_ref = arena.insert_float(3.14);
237        let int_ref = arena.insert_integer(42);
238
239        assert_eq!(arena.get_as_float(float_ref), Some(3.14));
240        assert_eq!(arena.get_as_float(int_ref), Some(42.0));
241    }
242
243    #[test]
244    fn test_scalar_arena_memory_usage() {
245        let mut arena = ScalarArena::new();
246        let initial_memory = arena.memory_usage();
247
248        for i in 0..100 {
249            arena.insert_float(i as f64);
250            arena.insert_integer(i);
251        }
252
253        let final_memory = arena.memory_usage();
254        assert!(final_memory > initial_memory);
255
256        // Should be at least 200 * 8 bytes for the values
257        assert!(final_memory >= 1600);
258    }
259
260    #[test]
261    fn test_scalar_ref_display() {
262        let mut arena = ScalarArena::new();
263        let float_ref = arena.insert_float(3.14);
264        let int_ref = arena.insert_integer(42);
265
266        assert_eq!(format!("{float_ref}"), "Float(0)");
267        assert_eq!(format!("{int_ref}"), "Int(0)");
268    }
269
270    #[test]
271    fn test_scalar_arena_clear() {
272        let mut arena = ScalarArena::new();
273        arena.insert_float(3.14);
274        arena.insert_integer(42);
275
276        assert_eq!(arena.len(), 2);
277        arena.clear();
278        assert_eq!(arena.len(), 0);
279        assert!(arena.is_empty());
280    }
281
282    #[test]
283    #[should_panic(expected = "Float index overflow")]
284    fn test_scalar_arena_float_overflow() {
285        // This test is theoretical since we can't actually allocate 2^31 values
286        // But we can test the assertion
287        let _ = ScalarRef::float(ScalarRef::INDEX_MASK + 1);
288    }
289}