xdl_core/
array.rs

1//! XDL array data structures and operations
2
3use crate::{Dimension, GdlData, GdlType, XdlError, XdlResult, XdlValue};
4use ndarray::{ArrayD, IxDyn};
5use num_complex::{Complex32, Complex64};
6use std::fmt;
7
8/// Generic XDL array container
9#[derive(Debug, Clone)]
10pub struct XdlArray<T> {
11    data: ArrayD<T>,
12    dimensions: Dimension,
13    gdl_type: GdlType,
14}
15
16impl<T> XdlArray<T>
17where
18    T: Clone + Default + fmt::Debug + Send + Sync + 'static,
19{
20    /// Create new array with given dimensions
21    pub fn new(dimensions: Dimension, gdl_type: GdlType) -> Result<Self, XdlError>
22    where
23        T: Default,
24    {
25        let shape: Vec<usize> = if dimensions.is_scalar() {
26            vec![]
27        } else {
28            dimensions.dims().to_vec()
29        };
30
31        let data = ArrayD::default(IxDyn(&shape));
32
33        Ok(Self {
34            data,
35            dimensions,
36            gdl_type,
37        })
38    }
39
40    /// Create array from data vector
41    pub fn from_vec(
42        data: Vec<T>,
43        dimensions: Dimension,
44        gdl_type: GdlType,
45    ) -> Result<Self, XdlError> {
46        if data.len() != dimensions.n_elements() {
47            return Err(XdlError::DimensionError(format!(
48                "Data length {} doesn't match dimension size {}",
49                data.len(),
50                dimensions.n_elements()
51            )));
52        }
53
54        let shape: Vec<usize> = if dimensions.is_scalar() {
55            vec![]
56        } else {
57            dimensions.dims().to_vec()
58        };
59
60        let array_data = ArrayD::from_shape_vec(IxDyn(&shape), data)
61            .map_err(|e| XdlError::DimensionError(format!("Shape error: {}", e)))?;
62
63        Ok(Self {
64            data: array_data,
65            dimensions,
66            gdl_type,
67        })
68    }
69
70    /// Create scalar array
71    pub fn scalar(value: T, gdl_type: GdlType) -> Self {
72        let data = ArrayD::from_elem(IxDyn(&[]), value);
73        Self {
74            data,
75            dimensions: Dimension::scalar(),
76            gdl_type,
77        }
78    }
79
80    /// Get element at index
81    pub fn get(&self, indices: &[usize]) -> Result<&T, XdlError> {
82        if self.dimensions.is_scalar() && indices.is_empty() {
83            return Ok(&self.data[IxDyn(&[])]);
84        }
85
86        if indices.len() != self.dimensions.rank() {
87            return Err(XdlError::IndexError(format!(
88                "Index rank {} doesn't match array rank {}",
89                indices.len(),
90                self.dimensions.rank()
91            )));
92        }
93
94        let ix = IxDyn(indices);
95        self.data
96            .get(ix)
97            .ok_or_else(|| XdlError::IndexError(format!("Index {:?} out of bounds", indices)))
98    }
99
100    /// Set element at index
101    pub fn set(&mut self, indices: &[usize], value: T) -> Result<(), XdlError> {
102        if self.dimensions.is_scalar() && indices.is_empty() {
103            self.data[IxDyn(&[])] = value;
104            return Ok(());
105        }
106
107        if indices.len() != self.dimensions.rank() {
108            return Err(XdlError::IndexError(format!(
109                "Index rank {} doesn't match array rank {}",
110                indices.len(),
111                self.dimensions.rank()
112            )));
113        }
114
115        let ix = IxDyn(indices);
116        if let Some(elem) = self.data.get_mut(ix) {
117            *elem = value;
118            Ok(())
119        } else {
120            Err(XdlError::IndexError(format!(
121                "Index {:?} out of bounds",
122                indices
123            )))
124        }
125    }
126
127    /// Get linear element access
128    pub fn get_linear(&self, index: usize) -> Result<&T, XdlError> {
129        if index >= self.dimensions.n_elements() {
130            return Err(XdlError::IndexError(format!(
131                "Linear index {} out of range",
132                index
133            )));
134        }
135
136        // Convert to multi-dimensional index
137        let multi_idx = self.dimensions.multi_index(index)?;
138        self.get(&multi_idx)
139    }
140
141    /// Set linear element access
142    pub fn set_linear(&mut self, index: usize, value: T) -> Result<(), XdlError> {
143        if index >= self.dimensions.n_elements() {
144            return Err(XdlError::IndexError(format!(
145                "Linear index {} out of range",
146                index
147            )));
148        }
149
150        let multi_idx = self.dimensions.multi_index(index)?;
151        self.set(&multi_idx, value)
152    }
153
154    /// Transpose array
155    pub fn transpose(&self, axes: Option<&[usize]>) -> Result<Self, XdlError>
156    where
157        T: Clone,
158    {
159        let new_dims = self.dimensions.transpose(axes)?;
160        let new_data = if let Some(ax) = axes {
161            self.data.clone().permuted_axes(ax).to_owned()
162        } else {
163            // Default transpose (reverse axes)
164            let reversed_axes: Vec<usize> = (0..self.dimensions.rank()).rev().collect();
165            self.data
166                .clone()
167                .permuted_axes(reversed_axes.as_slice())
168                .to_owned()
169        };
170
171        Ok(Self {
172            data: new_data,
173            dimensions: new_dims,
174            gdl_type: self.gdl_type,
175        })
176    }
177
178    /// Reform array to new shape
179    pub fn reform(&self, new_dims: Vec<usize>) -> Result<Self, XdlError>
180    where
181        T: Clone,
182    {
183        let new_dimensions = self.dimensions.reform(new_dims)?;
184        let shape: Vec<usize> = if new_dimensions.is_scalar() {
185            vec![]
186        } else {
187            new_dimensions.dims().to_vec()
188        };
189
190        let new_data = self
191            .data
192            .clone()
193            .into_shape(IxDyn(&shape))
194            .map_err(|e| XdlError::DimensionError(format!("Reform error: {}", e)))?;
195
196        Ok(Self {
197            data: new_data,
198            dimensions: new_dimensions,
199            gdl_type: self.gdl_type,
200        })
201    }
202
203    /// Get dimensions
204    pub fn dimensions(&self) -> &Dimension {
205        &self.dimensions
206    }
207
208    /// Get XDL type
209    pub fn gdl_type(&self) -> GdlType {
210        self.gdl_type
211    }
212
213    /// Get raw data slice (for linear access)
214    pub fn as_slice(&self) -> Option<&[T]> {
215        self.data.as_slice()
216    }
217
218    /// Convert to vector
219    pub fn to_vec(&self) -> Vec<T>
220    where
221        T: Clone,
222    {
223        if let Some(slice) = self.as_slice() {
224            slice.to_vec()
225        } else {
226            self.data.iter().cloned().collect()
227        }
228    }
229}
230
231// Implementations for specific types
232pub type ByteArray = XdlArray<u8>;
233pub type IntArray = XdlArray<i16>;
234pub type LongArray = XdlArray<i32>;
235pub type FloatArray = XdlArray<f32>;
236pub type DoubleArray = XdlArray<f64>;
237pub type ComplexArray = XdlArray<Complex32>;
238pub type DComplexArray = XdlArray<Complex64>;
239pub type StringArray = XdlArray<String>;
240
241// Implement GdlData trait for typed arrays
242macro_rules! impl_gdl_data {
243    ($array_type:ty, $gdl_type:expr) => {
244        impl GdlData for $array_type {
245            fn gdl_type(&self) -> GdlType {
246                $gdl_type
247            }
248
249            fn dimensions(&self) -> &Dimension {
250                &self.dimensions
251            }
252
253            fn n_elements(&self) -> usize {
254                self.dimensions.n_elements()
255            }
256
257            fn size_bytes(&self) -> usize {
258                self.n_elements() * $gdl_type.size()
259            }
260
261            fn clone_boxed(&self) -> Box<dyn GdlData> {
262                Box::new(self.clone())
263            }
264
265            fn to_string_repr(&self) -> String {
266                if self.dimensions.is_scalar() {
267                    format!("{:?}", self.get(&[]).unwrap())
268                } else {
269                    format!("Array[{}]: {}", self.gdl_type(), self.dimensions)
270                }
271            }
272        }
273    };
274}
275
276impl_gdl_data!(ByteArray, GdlType::Byte);
277impl_gdl_data!(IntArray, GdlType::Int);
278impl_gdl_data!(LongArray, GdlType::Long);
279impl_gdl_data!(FloatArray, GdlType::Float);
280impl_gdl_data!(DoubleArray, GdlType::Double);
281impl_gdl_data!(ComplexArray, GdlType::Complex);
282impl_gdl_data!(DComplexArray, GdlType::DComplex);
283impl_gdl_data!(StringArray, GdlType::String);
284
285/// Helper functions for creating arrays from XdlValue
286impl XdlArray<f64> {
287    /// Create from XdlValue (converting to double)
288    pub fn from_gdl_value(value: &XdlValue) -> XdlResult<Self> {
289        let double_val = value.to_double()?;
290        Ok(Self::scalar(double_val, GdlType::Double))
291    }
292}
293
294#[cfg(test)]
295mod tests {
296    use super::*;
297
298    #[test]
299    fn test_scalar_array() {
300        let arr = FloatArray::scalar(3.5, GdlType::Float);
301        assert!(arr.dimensions().is_scalar());
302        assert_eq!(arr.n_elements(), 1);
303        assert_eq!(*arr.get(&[]).unwrap(), 3.5);
304    }
305
306    #[test]
307    fn test_vector_array() {
308        let dim = Dimension::from_size(5).unwrap();
309        let data = vec![1, 2, 3, 4, 5];
310        let arr = LongArray::from_vec(data, dim, GdlType::Long).unwrap();
311
312        assert!(arr.dimensions().is_vector());
313        assert_eq!(arr.n_elements(), 5);
314        assert_eq!(*arr.get(&[2]).unwrap(), 3);
315    }
316
317    #[test]
318    fn test_multi_dim_array() {
319        let dim = Dimension::from_vec(vec![2, 3]).unwrap();
320        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
321        let arr = DoubleArray::from_vec(data, dim, GdlType::Double).unwrap();
322
323        assert_eq!(arr.dimensions().rank(), 2);
324        assert_eq!(arr.n_elements(), 6);
325        assert_eq!(*arr.get(&[1, 2]).unwrap(), 6.0);
326    }
327
328    #[test]
329    fn test_linear_indexing() {
330        let dim = Dimension::from_vec(vec![2, 3]).unwrap();
331        let data = vec![1, 2, 3, 4, 5, 6];
332        let mut arr = LongArray::from_vec(data, dim, GdlType::Long).unwrap();
333
334        assert_eq!(*arr.get_linear(5).unwrap(), 6);
335        arr.set_linear(0, 10).unwrap();
336        assert_eq!(*arr.get(&[0, 0]).unwrap(), 10);
337    }
338
339    #[test]
340    fn test_transpose() {
341        let dim = Dimension::from_vec(vec![2, 3]).unwrap();
342        let data = vec![1, 2, 3, 4, 5, 6];
343        let arr = LongArray::from_vec(data, dim, GdlType::Long).unwrap();
344
345        let transposed = arr.transpose(None).unwrap();
346        assert_eq!(transposed.dimensions().dims(), &[3, 2]);
347    }
348
349    #[test]
350    fn test_reform() {
351        let dim = Dimension::from_vec(vec![2, 3]).unwrap();
352        let data = vec![1, 2, 3, 4, 5, 6];
353        let arr = LongArray::from_vec(data, dim, GdlType::Long).unwrap();
354
355        let reformed = arr.reform(vec![3, 2]).unwrap();
356        assert_eq!(reformed.dimensions().dims(), &[3, 2]);
357        assert_eq!(reformed.n_elements(), 6);
358    }
359}