Skip to main content

vtk_pure_rs/data/
data_array.rs

1use std::sync::Arc;
2use crate::types::{Scalar, ScalarType};
3
4/// A contiguous array of tuples, where each tuple has `num_components` values.
5///
6/// This is the fundamental data container, analogous to VTK's `vtkDataArray`.
7/// Uses `Arc<Vec<T>>` for zero-copy clone with copy-on-write semantics.
8///
9/// # Examples
10///
11/// ```
12/// use crate::data::DataArray;
13///
14/// // Create a 3-component array (e.g., for normals or positions)
15/// let mut normals = DataArray::<f64>::new("Normals", 3);
16/// normals.push_tuple(&[0.0, 0.0, 1.0]);
17/// normals.push_tuple(&[0.0, 1.0, 0.0]);
18/// assert_eq!(normals.num_tuples(), 2);
19///
20/// // Create from a Vec
21/// let scalars = DataArray::<f64>::from_vec("Temperature", vec![10.0, 20.0, 30.0], 1);
22/// assert_eq!(scalars.num_tuples(), 3);
23/// ```
24#[derive(Debug)]
25pub struct DataArray<T: Scalar> {
26    data: Arc<Vec<T>>,
27    num_components: usize,
28    name: String,
29}
30
31impl<T: Scalar> Clone for DataArray<T> {
32    fn clone(&self) -> Self {
33        Self {
34            data: Arc::clone(&self.data),
35            num_components: self.num_components,
36            name: self.name.clone(),
37        }
38    }
39}
40
41impl<T: Scalar> PartialEq for DataArray<T> {
42    fn eq(&self, other: &Self) -> bool {
43        self.num_components == other.num_components
44            && self.name == other.name
45            && (Arc::ptr_eq(&self.data, &other.data) || *self.data == *other.data)
46    }
47}
48
49impl<T: Scalar> DataArray<T> {
50    pub fn new(name: impl Into<String>, num_components: usize) -> Self {
51        assert!(num_components > 0, "num_components must be > 0");
52        Self {
53            data: Arc::new(Vec::new()),
54            num_components,
55            name: name.into(),
56        }
57    }
58
59    pub fn from_vec(name: impl Into<String>, data: Vec<T>, num_components: usize) -> Self {
60        assert!(num_components > 0, "num_components must be > 0");
61        assert!(
62            data.len().is_multiple_of(num_components),
63            "data length {} is not divisible by num_components {}",
64            data.len(),
65            num_components
66        );
67        Self {
68            data: Arc::new(data),
69            num_components,
70            name: name.into(),
71        }
72    }
73
74    /// Create from a borrowed slice (copies data).
75    pub fn from_slice(name: impl Into<String>, data: &[T], num_components: usize) -> Self {
76        Self::from_vec(name, data.to_vec(), num_components)
77    }
78
79    pub fn name(&self) -> &str {
80        &self.name
81    }
82
83    pub fn set_name(&mut self, name: impl Into<String>) {
84        self.name = name.into();
85    }
86
87    pub fn scalar_type(&self) -> ScalarType {
88        T::TYPE_ID
89    }
90
91    pub fn num_components(&self) -> usize {
92        self.num_components
93    }
94
95    pub fn num_tuples(&self) -> usize {
96        self.data.len() / self.num_components
97    }
98
99    pub fn tuple(&self, idx: usize) -> &[T] {
100        let start = idx * self.num_components;
101        &self.data[start..start + self.num_components]
102    }
103
104    pub fn tuple_mut(&mut self, idx: usize) -> &mut [T] {
105        let start = idx * self.num_components;
106        let nc = self.num_components;
107        // Arc::make_mut is already optimal: no-op when strong_count == 1
108        let data = Arc::make_mut(&mut self.data);
109        &mut data[start..start + nc]
110    }
111
112    pub fn push_tuple(&mut self, values: &[T]) {
113        debug_assert_eq!(
114            values.len(),
115            self.num_components,
116            "expected {} components, got {}",
117            self.num_components,
118            values.len()
119        );
120        // Fast path: if we're the sole owner, avoid Arc::make_mut's atomic CAS
121        if let Some(v) = Arc::get_mut(&mut self.data) {
122            v.extend_from_slice(values);
123        } else {
124            Arc::make_mut(&mut self.data).extend_from_slice(values);
125        }
126    }
127
128    pub fn as_slice(&self) -> &[T] {
129        &self.data
130    }
131
132    pub fn as_mut_slice(&mut self) -> &mut [T] {
133        Arc::make_mut(&mut self.data).as_mut_slice()
134    }
135
136    pub fn into_vec(self) -> Vec<T> {
137        Arc::try_unwrap(self.data).unwrap_or_else(|arc| (*arc).clone())
138    }
139
140    pub fn is_empty(&self) -> bool {
141        self.data.is_empty()
142    }
143
144    /// Create a 1-component DataArray by evaluating a function for each index.
145    pub fn from_fn(name: impl Into<String>, count: usize, f: impl Fn(usize) -> T) -> Self {
146        let data: Vec<T> = (0..count).map(f).collect();
147        Self::from_vec(name, data, 1)
148    }
149
150    /// Create a multi-component DataArray from a function returning a slice.
151    pub fn from_fn_components(
152        name: impl Into<String>,
153        count: usize,
154        num_components: usize,
155        f: impl Fn(usize) -> Vec<T>,
156    ) -> Self {
157        let mut data = Vec::with_capacity(count * num_components);
158        for i in 0..count {
159            let vals = f(i);
160            assert_eq!(vals.len(), num_components);
161            data.extend(vals);
162        }
163        Self::from_vec(name, data, num_components)
164    }
165
166    /// Create a DataArray filled with a constant value.
167    pub fn filled(name: impl Into<String>, value: T, count: usize, num_components: usize) -> Self {
168        Self::from_vec(name, vec![value; count * num_components], num_components)
169    }
170
171    /// Number of total scalar values (tuples * components).
172    pub fn len(&self) -> usize {
173        self.data.len()
174    }
175
176    /// Apply a function to each scalar value in-place.
177    pub fn map_in_place(&mut self, f: impl Fn(T) -> T) {
178        for v in Arc::make_mut(&mut self.data).iter_mut() {
179            *v = f(*v);
180        }
181    }
182
183    /// Create a new DataArray by mapping each scalar value.
184    pub fn map(&self, name: impl Into<String>, f: impl Fn(T) -> T) -> Self {
185        let mapped: Vec<T> = self.data.iter().map(|&v| f(v)).collect();
186        Self::from_vec(name, mapped, self.num_components)
187    }
188
189    /// Create a new 1-component DataArray from a per-tuple function.
190    pub fn map_tuples(&self, name: impl Into<String>, f: impl Fn(&[T]) -> T) -> DataArray<T> {
191        let mut result = DataArray::new(name, 1);
192        for i in 0..self.num_tuples() {
193            let t = self.tuple(i);
194            result.push_tuple(&[f(t)]);
195        }
196        result
197    }
198
199    pub fn clear(&mut self) {
200        Arc::make_mut(&mut self.data).clear();
201    }
202
203    /// Returns true if this array shares storage with another clone.
204    pub fn is_shared(&self) -> bool {
205        Arc::strong_count(&self.data) > 1
206    }
207
208    /// Ensure exclusive ownership. Call before tight mutation loops to
209    /// avoid per-call Arc::make_mut atomic checks.
210    pub fn make_unique(&mut self) {
211        Arc::make_mut(&mut self.data);
212    }
213
214    /// Iterate over tuples as slices.
215    pub fn iter_tuples(&self) -> DataArrayTupleIter<'_, T> {
216        DataArrayTupleIter { array: self, idx: 0 }
217    }
218}
219
220/// Index by tuple index, returns the tuple slice.
221impl<T: Scalar> std::ops::Index<usize> for DataArray<T> {
222    type Output = [T];
223
224    fn index(&self, idx: usize) -> &Self::Output {
225        self.tuple(idx)
226    }
227}
228
229/// Iterator over tuples in a DataArray.
230pub struct DataArrayTupleIter<'a, T: Scalar> {
231    array: &'a DataArray<T>,
232    idx: usize,
233}
234
235impl<'a, T: Scalar> Iterator for DataArrayTupleIter<'a, T> {
236    type Item = &'a [T];
237
238    fn next(&mut self) -> Option<Self::Item> {
239        if self.idx < self.array.num_tuples() {
240            let t = self.array.tuple(self.idx);
241            self.idx += 1;
242            Some(t)
243        } else {
244            None
245        }
246    }
247
248    fn size_hint(&self) -> (usize, Option<usize>) {
249        let r = self.array.num_tuples() - self.idx;
250        (r, Some(r))
251    }
252}
253
254impl<T: Scalar> ExactSizeIterator for DataArrayTupleIter<'_, T> {}
255
256impl DataArray<f64> {
257    /// Scale all values by a factor.
258    pub fn scale(&mut self, factor: f64) {
259        for v in Arc::make_mut(&mut self.data).iter_mut() {
260            *v *= factor;
261        }
262    }
263
264    /// Normalize all values to [0, 1] range.
265    /// Returns the original (min, max) range.
266    pub fn normalize(&mut self) -> (f64, f64) {
267        let mut min = f64::INFINITY;
268        let mut max = f64::NEG_INFINITY;
269        for &v in self.data.iter() {
270            min = min.min(v);
271            max = max.max(v);
272        }
273        let range = max - min;
274        if range > 1e-15 {
275            for v in Arc::make_mut(&mut self.data).iter_mut() {
276                *v = (*v - min) / range;
277            }
278        }
279        (min, max)
280    }
281
282    /// Compute min value (first component only for multi-component).
283    pub fn min_value(&self) -> f64 {
284        let mut min = f64::INFINITY;
285        for i in 0..self.num_tuples() {
286            min = min.min(self.tuple(i)[0]);
287        }
288        min
289    }
290
291    /// Compute max value (first component only for multi-component).
292    pub fn max_value(&self) -> f64 {
293        let mut max = f64::NEG_INFINITY;
294        for i in 0..self.num_tuples() {
295            max = max.max(self.tuple(i)[0]);
296        }
297        max
298    }
299
300    /// Compute magnitude for each tuple (Euclidean norm of components).
301    pub fn magnitude(&self, name: impl Into<String>) -> DataArray<f64> {
302        let mut result = DataArray::new(name, 1);
303        for i in 0..self.num_tuples() {
304            let t = self.tuple(i);
305            let mag: f64 = t.iter().map(|v| v * v).sum::<f64>().sqrt();
306            result.push_tuple(&[mag]);
307        }
308        result
309    }
310}
311
312impl DataArray<f64> {
313    /// Extract a single component as a new 1-component array.
314    pub fn extract_component(&self, component: usize, name: impl Into<String>) -> DataArray<f64> {
315        assert!(component < self.num_components, "component index out of range");
316        let mut result = DataArray::new(name, 1);
317        for i in 0..self.num_tuples() {
318            result.push_tuple(&[self.tuple(i)[component]]);
319        }
320        result
321    }
322
323    /// Concatenate two arrays with the same number of components.
324    pub fn concat(&self, other: &DataArray<f64>) -> DataArray<f64> {
325        assert_eq!(self.num_components, other.num_components,
326            "cannot concatenate arrays with different component counts");
327        let mut data = (*self.data).clone();
328        data.extend_from_slice(&other.data);
329        DataArray::from_vec(self.name(), data, self.num_components)
330    }
331
332    /// Append another array's tuples to this array.
333    pub fn extend(&mut self, other: &DataArray<f64>) {
334        assert_eq!(self.num_components, other.num_components);
335        Arc::make_mut(&mut self.data).extend_from_slice(&other.data);
336    }
337
338    /// Create a new array by combining components from multiple 1-component arrays.
339    pub fn compose(name: impl Into<String>, components: &[&DataArray<f64>]) -> DataArray<f64> {
340        assert!(!components.is_empty());
341        let nt = components[0].num_tuples();
342        for c in components {
343            assert_eq!(c.num_tuples(), nt, "all components must have same tuple count");
344            assert_eq!(c.num_components(), 1, "all inputs must be 1-component");
345        }
346        let nc = components.len();
347        let mut data = Vec::with_capacity(nt * nc);
348        for i in 0..nt {
349            for c in components {
350                data.push(c.tuple(i)[0]);
351            }
352        }
353        DataArray::from_vec(name, data, nc)
354    }
355}
356
357impl DataArray<f32> {
358    /// Scale all values by a factor.
359    pub fn scale(&mut self, factor: f32) {
360        for v in Arc::make_mut(&mut self.data).iter_mut() {
361            *v *= factor;
362        }
363    }
364}
365
366/// Type-erased data array that can hold any scalar type.
367///
368/// Uses an enum rather than trait objects for exhaustive matching and zero vtable overhead.
369#[derive(Debug, Clone, PartialEq)]
370pub enum AnyDataArray {
371    F32(DataArray<f32>),
372    F64(DataArray<f64>),
373    I8(DataArray<i8>),
374    I16(DataArray<i16>),
375    I32(DataArray<i32>),
376    I64(DataArray<i64>),
377    U8(DataArray<u8>),
378    U16(DataArray<u16>),
379    U32(DataArray<u32>),
380    U64(DataArray<u64>),
381}
382
383impl AnyDataArray {
384    pub fn name(&self) -> &str {
385        match self {
386            AnyDataArray::F32(a) => a.name(),
387            AnyDataArray::F64(a) => a.name(),
388            AnyDataArray::I8(a) => a.name(),
389            AnyDataArray::I16(a) => a.name(),
390            AnyDataArray::I32(a) => a.name(),
391            AnyDataArray::I64(a) => a.name(),
392            AnyDataArray::U8(a) => a.name(),
393            AnyDataArray::U16(a) => a.name(),
394            AnyDataArray::U32(a) => a.name(),
395            AnyDataArray::U64(a) => a.name(),
396        }
397    }
398
399    pub fn set_name(&mut self, name: &str) {
400        match self {
401            AnyDataArray::F32(a) => a.set_name(name),
402            AnyDataArray::F64(a) => a.set_name(name),
403            AnyDataArray::I8(a) => a.set_name(name),
404            AnyDataArray::I16(a) => a.set_name(name),
405            AnyDataArray::I32(a) => a.set_name(name),
406            AnyDataArray::I64(a) => a.set_name(name),
407            AnyDataArray::U8(a) => a.set_name(name),
408            AnyDataArray::U16(a) => a.set_name(name),
409            AnyDataArray::U32(a) => a.set_name(name),
410            AnyDataArray::U64(a) => a.set_name(name),
411        }
412    }
413
414    pub fn scalar_type(&self) -> ScalarType {
415        match self {
416            AnyDataArray::F32(_) => ScalarType::F32,
417            AnyDataArray::F64(_) => ScalarType::F64,
418            AnyDataArray::I8(_) => ScalarType::I8,
419            AnyDataArray::I16(_) => ScalarType::I16,
420            AnyDataArray::I32(_) => ScalarType::I32,
421            AnyDataArray::I64(_) => ScalarType::I64,
422            AnyDataArray::U8(_) => ScalarType::U8,
423            AnyDataArray::U16(_) => ScalarType::U16,
424            AnyDataArray::U32(_) => ScalarType::U32,
425            AnyDataArray::U64(_) => ScalarType::U64,
426        }
427    }
428
429    /// Clone this array with a different name.
430    pub fn clone_with_name(&self, name: &str) -> Self {
431        let mut cloned = self.clone();
432        cloned.set_name(name);
433        cloned
434    }
435
436    pub fn num_components(&self) -> usize {
437        match self {
438            AnyDataArray::F32(a) => a.num_components(),
439            AnyDataArray::F64(a) => a.num_components(),
440            AnyDataArray::I8(a) => a.num_components(),
441            AnyDataArray::I16(a) => a.num_components(),
442            AnyDataArray::I32(a) => a.num_components(),
443            AnyDataArray::I64(a) => a.num_components(),
444            AnyDataArray::U8(a) => a.num_components(),
445            AnyDataArray::U16(a) => a.num_components(),
446            AnyDataArray::U32(a) => a.num_components(),
447            AnyDataArray::U64(a) => a.num_components(),
448        }
449    }
450
451    pub fn num_tuples(&self) -> usize {
452        match self {
453            AnyDataArray::F32(a) => a.num_tuples(),
454            AnyDataArray::F64(a) => a.num_tuples(),
455            AnyDataArray::I8(a) => a.num_tuples(),
456            AnyDataArray::I16(a) => a.num_tuples(),
457            AnyDataArray::I32(a) => a.num_tuples(),
458            AnyDataArray::I64(a) => a.num_tuples(),
459            AnyDataArray::U8(a) => a.num_tuples(),
460            AnyDataArray::U16(a) => a.num_tuples(),
461            AnyDataArray::U32(a) => a.num_tuples(),
462            AnyDataArray::U64(a) => a.num_tuples(),
463        }
464    }
465
466    /// Get tuple values as f64 (type-erased access).
467    pub fn tuple_as_f64(&self, idx: usize, out: &mut [f64]) {
468        macro_rules! convert {
469            ($arr:expr) => {{
470                let t = $arr.tuple(idx);
471                for (o, v) in out.iter_mut().zip(t.iter()) {
472                    *o = v.to_f64();
473                }
474            }};
475        }
476        match self {
477            AnyDataArray::F32(a) => convert!(a),
478            AnyDataArray::F64(a) => convert!(a),
479            AnyDataArray::I8(a) => convert!(a),
480            AnyDataArray::I16(a) => convert!(a),
481            AnyDataArray::I32(a) => convert!(a),
482            AnyDataArray::I64(a) => convert!(a),
483            AnyDataArray::U8(a) => convert!(a),
484            AnyDataArray::U16(a) => convert!(a),
485            AnyDataArray::U32(a) => convert!(a),
486            AnyDataArray::U64(a) => convert!(a),
487        }
488    }
489
490    /// Compute statistics (min, max, mean, variance) of the first component.
491    ///
492    /// Returns `(min, max, mean, variance)`. Returns `None` if the array is empty.
493    pub fn statistics(&self) -> Option<ArrayStatistics> {
494        let nt = self.num_tuples();
495        if nt == 0 {
496            return None;
497        }
498        let mut buf = [0.0f64];
499        let mut min = f64::INFINITY;
500        let mut max = f64::NEG_INFINITY;
501        let mut sum = 0.0;
502        let mut sum_sq = 0.0;
503        for i in 0..nt {
504            self.tuple_as_f64(i, &mut buf);
505            let v = buf[0];
506            min = min.min(v);
507            max = max.max(v);
508            sum += v;
509            sum_sq += v * v;
510        }
511        let mean = sum / nt as f64;
512        let variance = sum_sq / nt as f64 - mean * mean;
513        Some(ArrayStatistics { min, max, mean, variance: variance.max(0.0), count: nt })
514    }
515
516    /// Compute the range [min, max] of the first component.
517    pub fn range(&self) -> Option<[f64; 2]> {
518        self.statistics().map(|s| [s.min, s.max])
519    }
520
521    /// Extract all values as a Vec<f64> (first component of each tuple).
522    ///
523    /// Convenient for analysis workflows. For multi-component arrays,
524    /// only the first component is returned.
525    pub fn to_f64_vec(&self) -> Vec<f64> {
526        let nt = self.num_tuples();
527        let mut result = Vec::with_capacity(nt);
528        let mut buf = [0.0f64];
529        for i in 0..nt {
530            self.tuple_as_f64(i, &mut buf);
531            result.push(buf[0]);
532        }
533        result
534    }
535
536    /// Extract all values as a flat Vec<f64> (all components).
537    pub fn to_f64_vec_flat(&self) -> Vec<f64> {
538        let nt = self.num_tuples();
539        let nc = self.num_components();
540        let mut result = Vec::with_capacity(nt * nc);
541        let mut buf = vec![0.0f64; nc];
542        for i in 0..nt {
543            self.tuple_as_f64(i, &mut buf);
544            result.extend_from_slice(&buf);
545        }
546        result
547    }
548
549    /// Try to get the inner DataArray<f64> if this is the F64 variant.
550    pub fn as_f64(&self) -> Option<&DataArray<f64>> {
551        match self {
552            AnyDataArray::F64(a) => Some(a),
553            _ => None,
554        }
555    }
556
557    /// Try to get the inner DataArray<f32> if this is the F32 variant.
558    pub fn as_f32(&self) -> Option<&DataArray<f32>> {
559        match self {
560            AnyDataArray::F32(a) => Some(a),
561            _ => None,
562        }
563    }
564}
565
566impl std::fmt::Display for AnyDataArray {
567    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568        let type_name = match self.scalar_type() {
569            ScalarType::F32 => "Float32",
570            ScalarType::F64 => "Float64",
571            ScalarType::I8 => "Int8",
572            ScalarType::I16 => "Int16",
573            ScalarType::I32 => "Int32",
574            ScalarType::I64 => "Int64",
575            ScalarType::U8 => "UInt8",
576            ScalarType::U16 => "UInt16",
577            ScalarType::U32 => "UInt32",
578            ScalarType::U64 => "UInt64",
579        };
580        write!(f, "DataArray<{}>(\"{}\", {} tuples, {} components)",
581            type_name, self.name(), self.num_tuples(), self.num_components())
582    }
583}
584
585/// Statistics computed from a data array.
586#[derive(Debug, Clone, Copy)]
587pub struct ArrayStatistics {
588    pub min: f64,
589    pub max: f64,
590    pub mean: f64,
591    pub variance: f64,
592    pub count: usize,
593}
594
595impl ArrayStatistics {
596    /// Standard deviation.
597    pub fn std_dev(&self) -> f64 {
598        self.variance.sqrt()
599    }
600}
601
602// Specific From impls for each scalar type.
603macro_rules! impl_from_data_array {
604    ($ty:ty, $variant:ident) => {
605        impl From<DataArray<$ty>> for AnyDataArray {
606            fn from(a: DataArray<$ty>) -> Self {
607                AnyDataArray::$variant(a)
608            }
609        }
610    };
611}
612
613impl_from_data_array!(f32, F32);
614impl_from_data_array!(f64, F64);
615impl_from_data_array!(i8, I8);
616impl_from_data_array!(i16, I16);
617impl_from_data_array!(i32, I32);
618impl_from_data_array!(i64, I64);
619impl_from_data_array!(u8, U8);
620impl_from_data_array!(u16, U16);
621impl_from_data_array!(u32, U32);
622impl_from_data_array!(u64, U64);
623
624#[cfg(test)]
625mod tests {
626    use super::*;
627
628    #[test]
629    fn data_array_basics() {
630        let mut arr = DataArray::<f64>::new("test", 3);
631        arr.push_tuple(&[1.0, 2.0, 3.0]);
632        arr.push_tuple(&[4.0, 5.0, 6.0]);
633        assert_eq!(arr.num_tuples(), 2);
634        assert_eq!(arr.num_components(), 3);
635        assert_eq!(arr.tuple(0), &[1.0, 2.0, 3.0]);
636        assert_eq!(arr.tuple(1), &[4.0, 5.0, 6.0]);
637    }
638
639    #[test]
640    fn data_array_from_vec() {
641        let arr = DataArray::from_vec("coords", vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0], 3);
642        assert_eq!(arr.num_tuples(), 2);
643        assert_eq!(arr.scalar_type(), ScalarType::F32);
644    }
645
646    #[test]
647    fn any_data_array_type_erased() {
648        let arr = DataArray::from_vec("scalars", vec![10i32, 20, 30], 1);
649        let any: AnyDataArray = arr.into();
650        assert_eq!(any.name(), "scalars");
651        assert_eq!(any.scalar_type(), ScalarType::I32);
652        assert_eq!(any.num_tuples(), 3);
653
654        let mut val = [0.0f64];
655        any.tuple_as_f64(1, &mut val);
656        assert_eq!(val[0], 20.0);
657    }
658
659    #[test]
660    fn statistics_basic() {
661        let arr = AnyDataArray::F64(DataArray::from_vec("test", vec![1.0, 2.0, 3.0, 4.0, 5.0], 1));
662        let stats = arr.statistics().unwrap();
663        assert_eq!(stats.min, 1.0);
664        assert_eq!(stats.max, 5.0);
665        assert!((stats.mean - 3.0).abs() < 1e-12);
666        assert_eq!(stats.count, 5);
667        assert!(stats.variance > 0.0);
668        assert!((stats.std_dev() - 2.0f64.sqrt()).abs() < 1e-10);
669    }
670
671    #[test]
672    fn statistics_empty() {
673        let arr = AnyDataArray::F64(DataArray::new("test", 1));
674        assert!(arr.statistics().is_none());
675    }
676
677    #[test]
678    fn range() {
679        let arr = AnyDataArray::I32(DataArray::from_vec("test", vec![10, -5, 20, 0], 1));
680        let r = arr.range().unwrap();
681        assert_eq!(r[0], -5.0);
682        assert_eq!(r[1], 20.0);
683    }
684
685    #[test]
686    fn map_in_place() {
687        let mut arr = DataArray::from_vec("test", vec![1.0f64, 2.0, 3.0], 1);
688        arr.map_in_place(|v| v * 2.0);
689        assert_eq!(arr.tuple(0), &[2.0]);
690        assert_eq!(arr.tuple(2), &[6.0]);
691    }
692
693    #[test]
694    fn map_new() {
695        let arr = DataArray::from_vec("in", vec![1.0f64, 4.0, 9.0], 1);
696        let sqrt = arr.map("sqrt", |v| v.sqrt());
697        assert_eq!(sqrt.name(), "sqrt");
698        assert!((sqrt.tuple(1)[0] - 2.0).abs() < 1e-12);
699    }
700
701    #[test]
702    fn map_tuples_magnitude() {
703        let arr = DataArray::from_vec("vec", vec![3.0f64, 4.0, 0.0], 3);
704        let mag = arr.map_tuples("mag", |t| (t[0]*t[0] + t[1]*t[1] + t[2]*t[2]).sqrt());
705        assert!((mag.tuple(0)[0] - 5.0).abs() < 1e-12);
706    }
707
708    #[test]
709    fn scale_f64() {
710        let mut arr = DataArray::from_vec("test", vec![1.0f64, 2.0, 3.0], 1);
711        arr.scale(10.0);
712        assert_eq!(arr.tuple(0), &[10.0]);
713        assert_eq!(arr.tuple(2), &[30.0]);
714    }
715
716    #[test]
717    fn normalize_f64() {
718        let mut arr = DataArray::from_vec("test", vec![0.0f64, 50.0, 100.0], 1);
719        let (min, max) = arr.normalize();
720        assert_eq!(min, 0.0);
721        assert_eq!(max, 100.0);
722        assert!((arr.tuple(0)[0]).abs() < 1e-12);
723        assert!((arr.tuple(1)[0] - 0.5).abs() < 1e-12);
724        assert!((arr.tuple(2)[0] - 1.0).abs() < 1e-12);
725    }
726
727    #[test]
728    fn min_max_value() {
729        let arr = DataArray::from_vec("test", vec![5.0f64, -3.0, 8.0, 1.0], 1);
730        assert_eq!(arr.min_value(), -3.0);
731        assert_eq!(arr.max_value(), 8.0);
732    }
733
734    #[test]
735    fn magnitude_3d() {
736        let arr = DataArray::from_vec("v", vec![1.0f64, 0.0, 0.0, 0.0, 3.0, 4.0], 3);
737        let mag = arr.magnitude("mag");
738        assert!((mag.tuple(0)[0] - 1.0).abs() < 1e-12);
739        assert!((mag.tuple(1)[0] - 5.0).abs() < 1e-12);
740    }
741
742    #[test]
743    fn extract_component() {
744        let arr = DataArray::from_vec("vec", vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0], 3);
745        let y = arr.extract_component(1, "y");
746        assert_eq!(y.num_tuples(), 2);
747        assert_eq!(y.num_components(), 1);
748        assert_eq!(y.tuple(0), &[2.0]);
749        assert_eq!(y.tuple(1), &[5.0]);
750    }
751
752    #[test]
753    fn concat_arrays() {
754        let a = DataArray::from_vec("a", vec![1.0f64, 2.0], 1);
755        let b = DataArray::from_vec("b", vec![3.0f64, 4.0], 1);
756        let c = a.concat(&b);
757        assert_eq!(c.num_tuples(), 4);
758        assert_eq!(c.tuple(2), &[3.0]);
759    }
760
761    #[test]
762    fn extend_array() {
763        let mut a = DataArray::from_vec("a", vec![1.0f64, 2.0], 1);
764        let b = DataArray::from_vec("b", vec![3.0f64, 4.0], 1);
765        a.extend(&b);
766        assert_eq!(a.num_tuples(), 4);
767    }
768
769    #[test]
770    fn compose_components() {
771        let x = DataArray::from_vec("x", vec![1.0f64, 4.0], 1);
772        let y = DataArray::from_vec("y", vec![2.0f64, 5.0], 1);
773        let z = DataArray::from_vec("z", vec![3.0f64, 6.0], 1);
774        let v = DataArray::compose("vec", &[&x, &y, &z]);
775        assert_eq!(v.num_tuples(), 2);
776        assert_eq!(v.num_components(), 3);
777        assert_eq!(v.tuple(0), &[1.0, 2.0, 3.0]);
778        assert_eq!(v.tuple(1), &[4.0, 5.0, 6.0]);
779    }
780
781    #[test]
782    fn from_fn() {
783        let arr = DataArray::<f64>::from_fn("sq", 5, |i| (i * i) as f64);
784        assert_eq!(arr.num_tuples(), 5);
785        assert_eq!(arr.tuple(3), &[9.0]);
786    }
787
788    #[test]
789    fn from_fn_components() {
790        let arr = DataArray::<f64>::from_fn_components("pos", 3, 3, |i| {
791            vec![i as f64, (i * 2) as f64, (i * 3) as f64]
792        });
793        assert_eq!(arr.num_tuples(), 3);
794        assert_eq!(arr.num_components(), 3);
795        assert_eq!(arr.tuple(1), &[1.0, 2.0, 3.0]);
796    }
797
798    #[test]
799    fn filled() {
800        let arr = DataArray::<f64>::filled("zeros", 0.0, 10, 1);
801        assert_eq!(arr.num_tuples(), 10);
802        assert_eq!(arr.tuple(5), &[0.0]);
803    }
804
805    #[test]
806    fn iter_tuples() {
807        let arr = DataArray::<f64>::from_vec("test", vec![1.0, 2.0, 3.0], 1);
808        let vals: Vec<&[f64]> = arr.iter_tuples().collect();
809        assert_eq!(vals.len(), 3);
810        assert_eq!(vals[1], &[2.0]);
811    }
812
813    #[test]
814    fn any_data_array_display() {
815        let arr = AnyDataArray::F64(DataArray::from_vec("temperature", vec![1.0, 2.0, 3.0], 1));
816        let s = format!("{arr}");
817        assert!(s.contains("Float64"));
818        assert!(s.contains("temperature"));
819        assert!(s.contains("3 tuples"));
820    }
821
822    #[test]
823    fn to_f64_vec() {
824        let arr = AnyDataArray::F64(DataArray::from_vec("t", vec![1.0, 2.0, 3.0], 1));
825        let v = arr.to_f64_vec();
826        assert_eq!(v, vec![1.0, 2.0, 3.0]);
827    }
828
829    #[test]
830    fn to_f64_vec_flat() {
831        let arr = AnyDataArray::F64(DataArray::from_vec("v", vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], 3));
832        let v = arr.to_f64_vec_flat();
833        assert_eq!(v, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
834    }
835
836    #[test]
837    fn as_f64_variant() {
838        let arr = AnyDataArray::F64(DataArray::from_vec("t", vec![1.0], 1));
839        assert!(arr.as_f64().is_some());
840        assert!(arr.as_f32().is_none());
841    }
842
843    #[test]
844    fn index_trait() {
845        let arr = DataArray::from_vec("v", vec![1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0], 3);
846        assert_eq!(arr[0], [1.0, 2.0, 3.0]);
847        assert_eq!(arr[1], [4.0, 5.0, 6.0]);
848    }
849}