Skip to main content

minarrow/enums/collections/
numeric_array.rs

1// Copyright 2025 Peter Garfield Bower
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! # **NumericArray Module** - *High-Level Numerical Array Type for Unified Signature Dispatch*
16//!
17//! NumericArray unifies all integer and floating-point arrays
18//! into a single enum for standardised numeric operations.
19//!   
20//! ## Features
21//! - direct variant access
22//! - zero-cost casts when the type is known
23//! - lossless conversions between integer and float types.
24//! - simplifies function signatures by accepting `impl Into<NumericArray>`
25//! - centralises dispatch
26//! - preserves SIMD-aligned buffers across all numeric variants.
27
28use std::{
29    fmt::{Display, Formatter},
30    sync::Arc,
31};
32
33use crate::{Bitmask, FloatArray, IntegerArray, MaskedArray, Vec64};
34use crate::{BooleanArray, StringArray};
35use crate::{
36    enums::{error::MinarrowError, shape_dim::ShapeDim},
37    traits::{concatenate::Concatenate, shape::Shape},
38};
39
40/// # NumericArray
41///
42/// Unified numerical array container
43///
44/// ## Purpose
45/// Exists to unify numerical operations,
46/// simplify API's and streamline user ergonomics.
47///
48/// ## Usage:
49/// - It is accessible from `Array` using `.num()`,
50/// and provides typed variant access via for e.g.,
51/// `.i64()`, so one can drill down to the required
52/// granularity via `myarr.num().i64()`
53/// - This streamlines function implementations,
54/// and, despite the additional `enum` layer,
55/// matching lanes in many real-world scenarios.
56/// This is because one can for e.g., unify a
57/// function signature with `impl Into<NumericArray>`,
58/// and all of the subtypes, plus `Array` and `NumericalArray`,
59/// all qualify.
60/// - Additionally, you can then use one `Integer` implementation
61/// on the enum dispatch arm for all `Integer` variants, or,
62/// in many cases, for the entire numeric arm when they are the same.
63///
64/// ### Typecasting behaviour
65/// - If the enum already holds the given type *(which should be known at compile-time)*,
66/// then using accessors like `.i32()` is zero-cost, as it transfers ownership.
67/// - If you want to keep the original, of course use `.clone()` beforehand.
68/// - If you use an accessor to a different base type, e.g., `.f32()` when it's a
69/// `.int32()` already in the enum, it will convert it. Therefore, be mindful
70/// of performance when this occurs.
71///
72/// ## Also see:
73/// - Under [crate::traits::type_unions] , we additionally
74/// include minimal `Integer`, `Float`, `Numeric` and `Primitive` traits that
75/// for which the base Rust primitive types already qualify.
76/// These are loose wrappers over the `num-traits` crate to help improve
77/// type ergonomics when traits are required, but without requiring
78/// any downcasting.
79#[repr(C, align(64))]
80#[derive(PartialEq, Clone, Debug, Default)]
81pub enum NumericArray {
82    #[cfg(feature = "extended_numeric_types")]
83    Int8(Arc<IntegerArray<i8>>),
84    #[cfg(feature = "extended_numeric_types")]
85    Int16(Arc<IntegerArray<i16>>),
86    Int32(Arc<IntegerArray<i32>>),
87    Int64(Arc<IntegerArray<i64>>),
88    #[cfg(feature = "extended_numeric_types")]
89    UInt8(Arc<IntegerArray<u8>>),
90    #[cfg(feature = "extended_numeric_types")]
91    UInt16(Arc<IntegerArray<u16>>),
92    UInt32(Arc<IntegerArray<u32>>),
93    UInt64(Arc<IntegerArray<u64>>),
94    Float32(Arc<FloatArray<f32>>),
95    Float64(Arc<FloatArray<f64>>),
96    #[default]
97    Null, // Default Marker for mem::take
98}
99
100impl NumericArray {
101    /// Returns the logical length of the numeric array.
102    #[inline]
103    pub fn len(&self) -> usize {
104        match self {
105            #[cfg(feature = "extended_numeric_types")]
106            NumericArray::Int8(arr) => arr.len(),
107            #[cfg(feature = "extended_numeric_types")]
108            NumericArray::Int16(arr) => arr.len(),
109            NumericArray::Int32(arr) => arr.len(),
110            NumericArray::Int64(arr) => arr.len(),
111            #[cfg(feature = "extended_numeric_types")]
112            NumericArray::UInt8(arr) => arr.len(),
113            #[cfg(feature = "extended_numeric_types")]
114            NumericArray::UInt16(arr) => arr.len(),
115            NumericArray::UInt32(arr) => arr.len(),
116            NumericArray::UInt64(arr) => arr.len(),
117            NumericArray::Float32(arr) => arr.len(),
118            NumericArray::Float64(arr) => arr.len(),
119            NumericArray::Null => 0,
120        }
121    }
122
123    /// Returns the underlying null mask, if any.
124    #[inline]
125    pub fn null_mask(&self) -> Option<&Bitmask> {
126        match self {
127            #[cfg(feature = "extended_numeric_types")]
128            NumericArray::Int8(arr) => arr.null_mask.as_ref(),
129            #[cfg(feature = "extended_numeric_types")]
130            NumericArray::Int16(arr) => arr.null_mask.as_ref(),
131            NumericArray::Int32(arr) => arr.null_mask.as_ref(),
132            NumericArray::Int64(arr) => arr.null_mask.as_ref(),
133            #[cfg(feature = "extended_numeric_types")]
134            NumericArray::UInt8(arr) => arr.null_mask.as_ref(),
135            #[cfg(feature = "extended_numeric_types")]
136            NumericArray::UInt16(arr) => arr.null_mask.as_ref(),
137            NumericArray::UInt32(arr) => arr.null_mask.as_ref(),
138            NumericArray::UInt64(arr) => arr.null_mask.as_ref(),
139            NumericArray::Float32(arr) => arr.null_mask.as_ref(),
140            NumericArray::Float64(arr) => arr.null_mask.as_ref(),
141            NumericArray::Null => None,
142        }
143    }
144
145    /// Appends all values (and null mask if present) from `other` into `self`.
146    ///
147    /// Panics if the two arrays are of different variants or incompatible types.
148    ///
149    /// This function uses copy-on-write semantics for arrays wrapped in `Arc`.
150    /// If `self` is the only owner of its data, appends are performed in place without copying.
151    /// If the array data is shared (`Arc` reference count > 1), the data is first cloned
152    /// (so the mutation does not affect other owners), and the append is then performed on the unique copy.
153    ///
154    /// This ensures that calling `append_array` never mutates data referenced elsewhere,
155    /// but also avoids unnecessary cloning when the data is uniquely owned.
156    pub fn append_array(&mut self, other: &Self) {
157        match (self, other) {
158            #[cfg(feature = "extended_numeric_types")]
159            (NumericArray::Int8(a), NumericArray::Int8(b)) => Arc::make_mut(a).append_array(b),
160            #[cfg(feature = "extended_numeric_types")]
161            (NumericArray::Int16(a), NumericArray::Int16(b)) => Arc::make_mut(a).append_array(b),
162            (NumericArray::Int32(a), NumericArray::Int32(b)) => Arc::make_mut(a).append_array(b),
163            (NumericArray::Int64(a), NumericArray::Int64(b)) => Arc::make_mut(a).append_array(b),
164
165            #[cfg(feature = "extended_numeric_types")]
166            (NumericArray::UInt8(a), NumericArray::UInt8(b)) => Arc::make_mut(a).append_array(b),
167            #[cfg(feature = "extended_numeric_types")]
168            (NumericArray::UInt16(a), NumericArray::UInt16(b)) => Arc::make_mut(a).append_array(b),
169            (NumericArray::UInt32(a), NumericArray::UInt32(b)) => Arc::make_mut(a).append_array(b),
170            (NumericArray::UInt64(a), NumericArray::UInt64(b)) => Arc::make_mut(a).append_array(b),
171
172            (NumericArray::Float32(a), NumericArray::Float32(b)) => {
173                Arc::make_mut(a).append_array(b)
174            }
175            (NumericArray::Float64(a), NumericArray::Float64(b)) => {
176                Arc::make_mut(a).append_array(b)
177            }
178
179            (NumericArray::Null, NumericArray::Null) => (),
180            (lhs, rhs) => panic!("Cannot append {:?} into {:?}", rhs, lhs),
181        }
182    }
183
184    pub fn append_range(&mut self, other: &Self, offset: usize, len: usize) -> Result<(), MinarrowError> {
185        match (self, other) {
186            #[cfg(feature = "extended_numeric_types")]
187            (NumericArray::Int8(a), NumericArray::Int8(b)) => Arc::make_mut(a).append_range(b, offset, len),
188            #[cfg(feature = "extended_numeric_types")]
189            (NumericArray::Int16(a), NumericArray::Int16(b)) => Arc::make_mut(a).append_range(b, offset, len),
190            (NumericArray::Int32(a), NumericArray::Int32(b)) => Arc::make_mut(a).append_range(b, offset, len),
191            (NumericArray::Int64(a), NumericArray::Int64(b)) => Arc::make_mut(a).append_range(b, offset, len),
192            #[cfg(feature = "extended_numeric_types")]
193            (NumericArray::UInt8(a), NumericArray::UInt8(b)) => Arc::make_mut(a).append_range(b, offset, len),
194            #[cfg(feature = "extended_numeric_types")]
195            (NumericArray::UInt16(a), NumericArray::UInt16(b)) => Arc::make_mut(a).append_range(b, offset, len),
196            (NumericArray::UInt32(a), NumericArray::UInt32(b)) => Arc::make_mut(a).append_range(b, offset, len),
197            (NumericArray::UInt64(a), NumericArray::UInt64(b)) => Arc::make_mut(a).append_range(b, offset, len),
198            (NumericArray::Float32(a), NumericArray::Float32(b)) => Arc::make_mut(a).append_range(b, offset, len),
199            (NumericArray::Float64(a), NumericArray::Float64(b)) => Arc::make_mut(a).append_range(b, offset, len),
200            (NumericArray::Null, NumericArray::Null) => Ok(()),
201            (lhs, rhs) => Err(MinarrowError::TypeError {
202                from: "NumericArray",
203                to: "NumericArray",
204                message: Some(format!("Cannot append_range {:?} into {:?}", rhs, lhs)),
205            }),
206        }
207    }
208
209    /// Inserts all values (and null mask if present) from `other` into `self` at the specified index.
210    ///
211    /// This is an **O(n)** operation.
212    ///
213    /// Returns an error if the two arrays are of different variants or incompatible types,
214    /// or if the index is out of bounds.
215    ///
216    /// This function uses copy-on-write semantics for arrays wrapped in `Arc`.
217    pub fn insert_rows(&mut self, index: usize, other: &Self) -> Result<(), MinarrowError> {
218        match (self, other) {
219            #[cfg(feature = "extended_numeric_types")]
220            (NumericArray::Int8(a), NumericArray::Int8(b)) => {
221                Arc::make_mut(a).insert_rows(index, b)
222            }
223            #[cfg(feature = "extended_numeric_types")]
224            (NumericArray::Int16(a), NumericArray::Int16(b)) => {
225                Arc::make_mut(a).insert_rows(index, b)
226            }
227            (NumericArray::Int32(a), NumericArray::Int32(b)) => {
228                Arc::make_mut(a).insert_rows(index, b)
229            }
230            (NumericArray::Int64(a), NumericArray::Int64(b)) => {
231                Arc::make_mut(a).insert_rows(index, b)
232            }
233
234            #[cfg(feature = "extended_numeric_types")]
235            (NumericArray::UInt8(a), NumericArray::UInt8(b)) => {
236                Arc::make_mut(a).insert_rows(index, b)
237            }
238            #[cfg(feature = "extended_numeric_types")]
239            (NumericArray::UInt16(a), NumericArray::UInt16(b)) => {
240                Arc::make_mut(a).insert_rows(index, b)
241            }
242            (NumericArray::UInt32(a), NumericArray::UInt32(b)) => {
243                Arc::make_mut(a).insert_rows(index, b)
244            }
245            (NumericArray::UInt64(a), NumericArray::UInt64(b)) => {
246                Arc::make_mut(a).insert_rows(index, b)
247            }
248
249            (NumericArray::Float32(a), NumericArray::Float32(b)) => {
250                Arc::make_mut(a).insert_rows(index, b)
251            }
252            (NumericArray::Float64(a), NumericArray::Float64(b)) => {
253                Arc::make_mut(a).insert_rows(index, b)
254            }
255
256            (NumericArray::Null, NumericArray::Null) => Ok(()),
257            (lhs, rhs) => Err(MinarrowError::TypeError {
258                from: "NumericArray",
259                to: "NumericArray",
260                message: Some(format!(
261                    "Cannot insert {} into {}: incompatible types",
262                    rhs, lhs
263                )),
264            }),
265        }
266    }
267
268    /// Splits the NumericArray at the specified index, consuming self and returning two arrays.
269    pub fn split(self, index: usize) -> Result<(Self, Self), MinarrowError> {
270        use std::sync::Arc;
271
272        match self {
273            #[cfg(feature = "extended_numeric_types")]
274            NumericArray::Int8(a) => {
275                let (left, right) = Arc::try_unwrap(a)
276                    .unwrap_or_else(|arc| (*arc).clone())
277                    .split(index)?;
278                Ok((
279                    NumericArray::Int8(Arc::new(left)),
280                    NumericArray::Int8(Arc::new(right)),
281                ))
282            }
283            #[cfg(feature = "extended_numeric_types")]
284            NumericArray::UInt8(a) => {
285                let (left, right) = Arc::try_unwrap(a)
286                    .unwrap_or_else(|arc| (*arc).clone())
287                    .split(index)?;
288                Ok((
289                    NumericArray::UInt8(Arc::new(left)),
290                    NumericArray::UInt8(Arc::new(right)),
291                ))
292            }
293            #[cfg(feature = "extended_numeric_types")]
294            NumericArray::Int16(a) => {
295                let (left, right) = Arc::try_unwrap(a)
296                    .unwrap_or_else(|arc| (*arc).clone())
297                    .split(index)?;
298                Ok((
299                    NumericArray::Int16(Arc::new(left)),
300                    NumericArray::Int16(Arc::new(right)),
301                ))
302            }
303            #[cfg(feature = "extended_numeric_types")]
304            NumericArray::UInt16(a) => {
305                let (left, right) = Arc::try_unwrap(a)
306                    .unwrap_or_else(|arc| (*arc).clone())
307                    .split(index)?;
308                Ok((
309                    NumericArray::UInt16(Arc::new(left)),
310                    NumericArray::UInt16(Arc::new(right)),
311                ))
312            }
313            NumericArray::Int32(a) => {
314                let (left, right) = Arc::try_unwrap(a)
315                    .unwrap_or_else(|arc| (*arc).clone())
316                    .split(index)?;
317                Ok((
318                    NumericArray::Int32(Arc::new(left)),
319                    NumericArray::Int32(Arc::new(right)),
320                ))
321            }
322            NumericArray::Int64(a) => {
323                let (left, right) = Arc::try_unwrap(a)
324                    .unwrap_or_else(|arc| (*arc).clone())
325                    .split(index)?;
326                Ok((
327                    NumericArray::Int64(Arc::new(left)),
328                    NumericArray::Int64(Arc::new(right)),
329                ))
330            }
331            NumericArray::UInt32(a) => {
332                let (left, right) = Arc::try_unwrap(a)
333                    .unwrap_or_else(|arc| (*arc).clone())
334                    .split(index)?;
335                Ok((
336                    NumericArray::UInt32(Arc::new(left)),
337                    NumericArray::UInt32(Arc::new(right)),
338                ))
339            }
340            NumericArray::UInt64(a) => {
341                let (left, right) = Arc::try_unwrap(a)
342                    .unwrap_or_else(|arc| (*arc).clone())
343                    .split(index)?;
344                Ok((
345                    NumericArray::UInt64(Arc::new(left)),
346                    NumericArray::UInt64(Arc::new(right)),
347                ))
348            }
349            NumericArray::Float32(a) => {
350                let (left, right) = Arc::try_unwrap(a)
351                    .unwrap_or_else(|arc| (*arc).clone())
352                    .split(index)?;
353                Ok((
354                    NumericArray::Float32(Arc::new(left)),
355                    NumericArray::Float32(Arc::new(right)),
356                ))
357            }
358            NumericArray::Float64(a) => {
359                let (left, right) = Arc::try_unwrap(a)
360                    .unwrap_or_else(|arc| (*arc).clone())
361                    .split(index)?;
362                Ok((
363                    NumericArray::Float64(Arc::new(left)),
364                    NumericArray::Float64(Arc::new(right)),
365                ))
366            }
367            NumericArray::Null => Err(MinarrowError::IndexError(
368                "Cannot split Null array".to_string(),
369            )),
370        }
371    }
372
373    /// Returns a reference to the inner `IntegerArray<i32>` if the variant matches.
374    /// No conversion or cloning is performed.
375    pub fn i32_ref(&self) -> Result<&IntegerArray<i32>, MinarrowError> {
376        match self {
377            NumericArray::Int32(a) => Ok(a),
378            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
379            _ => Err(MinarrowError::TypeError {
380                from: "NumericArray",
381                to: "IntegerArray<i32>",
382                message: None,
383            }),
384        }
385    }
386
387    /// Returns a reference to the inner `IntegerArray<i64>` if the variant matches.
388    /// No conversion or cloning is performed.
389    pub fn i64_ref(&self) -> Result<&IntegerArray<i64>, MinarrowError> {
390        match self {
391            NumericArray::Int64(a) => Ok(a),
392            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
393            _ => Err(MinarrowError::TypeError {
394                from: "NumericArray",
395                to: "IntegerArray<i64>",
396                message: None,
397            }),
398        }
399    }
400
401    /// Returns a reference to the inner `IntegerArray<u32>` if the variant matches.
402    /// No conversion or cloning is performed.
403    pub fn u32_ref(&self) -> Result<&IntegerArray<u32>, MinarrowError> {
404        match self {
405            NumericArray::UInt32(a) => Ok(a),
406            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
407            _ => Err(MinarrowError::TypeError {
408                from: "NumericArray",
409                to: "IntegerArray<u32>",
410                message: None,
411            }),
412        }
413    }
414
415    /// Returns a reference to the inner `IntegerArray<u64>` if the variant matches.
416    /// No conversion or cloning is performed.
417    pub fn u64_ref(&self) -> Result<&IntegerArray<u64>, MinarrowError> {
418        match self {
419            NumericArray::UInt64(a) => Ok(a),
420            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
421            _ => Err(MinarrowError::TypeError {
422                from: "NumericArray",
423                to: "IntegerArray<u64>",
424                message: None,
425            }),
426        }
427    }
428
429    /// Returns a reference to the inner `FloatArray<f32>` if the variant matches.
430    /// No conversion or cloning is performed.
431    pub fn f32_ref(&self) -> Result<&FloatArray<f32>, MinarrowError> {
432        match self {
433            NumericArray::Float32(a) => Ok(a),
434            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
435            _ => Err(MinarrowError::TypeError {
436                from: "NumericArray",
437                to: "FloatArray<f32>",
438                message: None,
439            }),
440        }
441    }
442
443    /// Returns a reference to the inner `FloatArray<f64>` if the variant matches.
444    /// No conversion or cloning is performed.
445    pub fn f64_ref(&self) -> Result<&FloatArray<f64>, MinarrowError> {
446        match self {
447            NumericArray::Float64(a) => Ok(a),
448            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
449            _ => Err(MinarrowError::TypeError {
450                from: "NumericArray",
451                to: "FloatArray<f64>",
452                message: None,
453            }),
454        }
455    }
456
457    /// Convert to IntegerArray<i32> using From/TryFrom as appropriate per conversion.
458    pub fn i32(self) -> Result<IntegerArray<i32>, MinarrowError> {
459        match self {
460            #[cfg(feature = "extended_numeric_types")]
461            NumericArray::Int8(a) => Ok(IntegerArray::<i32>::from(&*a)),
462            #[cfg(feature = "extended_numeric_types")]
463            NumericArray::Int16(a) => Ok(IntegerArray::<i32>::from(&*a)),
464            NumericArray::Int32(a) => match Arc::try_unwrap(a) {
465                Ok(inner) => Ok(inner),
466                Err(shared) => Ok((*shared).clone()),
467            },
468            NumericArray::Int64(a) => Ok(IntegerArray::<i32>::try_from(&*a)?),
469            #[cfg(feature = "extended_numeric_types")]
470            NumericArray::UInt8(a) => Ok(IntegerArray::<i32>::from(&*a)),
471            #[cfg(feature = "extended_numeric_types")]
472            NumericArray::UInt16(a) => Ok(IntegerArray::<i32>::from(&*a)),
473            NumericArray::UInt32(a) => Ok(IntegerArray::<i32>::try_from(&*a)?),
474            NumericArray::UInt64(a) => Ok(IntegerArray::<i32>::try_from(&*a)?),
475            NumericArray::Float32(a) => Ok(IntegerArray::<i32>::try_from(&*a)?),
476            NumericArray::Float64(a) => Ok(IntegerArray::<i32>::try_from(&*a)?),
477            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
478        }
479    }
480
481    /// Convert to IntegerArray<i64> using From/TryFrom as appropriate per conversion.
482    pub fn i64(self) -> Result<IntegerArray<i64>, MinarrowError> {
483        match self {
484            #[cfg(feature = "extended_numeric_types")]
485            NumericArray::Int8(a) => Ok(IntegerArray::<i64>::from(&*a)),
486            #[cfg(feature = "extended_numeric_types")]
487            NumericArray::Int16(a) => Ok(IntegerArray::<i64>::from(&*a)),
488            NumericArray::Int32(a) => Ok(IntegerArray::<i64>::from(&*a)),
489            NumericArray::Int64(a) => match Arc::try_unwrap(a) {
490                Ok(inner) => Ok(inner),
491                Err(shared) => Ok((*shared).clone()),
492            },
493            #[cfg(feature = "extended_numeric_types")]
494            NumericArray::UInt8(a) => Ok(IntegerArray::<i64>::from(&*a)),
495            #[cfg(feature = "extended_numeric_types")]
496            NumericArray::UInt16(a) => Ok(IntegerArray::<i64>::from(&*a)),
497            NumericArray::UInt32(a) => Ok(IntegerArray::<i64>::from(&*a)),
498            NumericArray::UInt64(a) => Ok(IntegerArray::<i64>::try_from(&*a)?),
499            NumericArray::Float32(a) => Ok(IntegerArray::<i64>::try_from(&*a)?),
500            NumericArray::Float64(a) => Ok(IntegerArray::<i64>::try_from(&*a)?),
501            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
502        }
503    }
504
505    /// Convert to IntegerArray<u32> using From/TryFrom as appropriate per conversion.
506    pub fn u32(self) -> Result<IntegerArray<u32>, MinarrowError> {
507        match self {
508            #[cfg(feature = "extended_numeric_types")]
509            NumericArray::Int8(a) => Ok(IntegerArray::<u32>::from(&*a)),
510            #[cfg(feature = "extended_numeric_types")]
511            NumericArray::Int16(a) => Ok(IntegerArray::<u32>::from(&*a)),
512            NumericArray::Int32(a) => Ok(IntegerArray::<u32>::try_from(&*a)?),
513            NumericArray::Int64(a) => Ok(IntegerArray::<u32>::try_from(&*a)?),
514            #[cfg(feature = "extended_numeric_types")]
515            NumericArray::UInt8(a) => Ok(IntegerArray::<u32>::from(&*a)),
516            #[cfg(feature = "extended_numeric_types")]
517            NumericArray::UInt16(a) => Ok(IntegerArray::<u32>::from(&*a)),
518            NumericArray::UInt32(a) => match Arc::try_unwrap(a) {
519                Ok(inner) => Ok(inner),
520                Err(shared) => Ok((*shared).clone()),
521            },
522            NumericArray::UInt64(a) => Ok(IntegerArray::<u32>::try_from(&*a)?),
523            NumericArray::Float32(a) => Ok(IntegerArray::<u32>::try_from(&*a)?),
524            NumericArray::Float64(a) => Ok(IntegerArray::<u32>::try_from(&*a)?),
525            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
526        }
527    }
528
529    /// Convert to IntegerArray<u64> using From/TryFrom as appropriate per conversion.
530    pub fn u64(self) -> Result<IntegerArray<u64>, MinarrowError> {
531        match self {
532            #[cfg(feature = "extended_numeric_types")]
533            NumericArray::Int8(a) => Ok(IntegerArray::<u64>::from(&*a)),
534            #[cfg(feature = "extended_numeric_types")]
535            NumericArray::Int16(a) => Ok(IntegerArray::<u64>::from(&*a)),
536            NumericArray::Int32(a) => Ok(IntegerArray::<u64>::from(&*a)),
537            NumericArray::Int64(a) => Ok(IntegerArray::<u64>::try_from(&*a)?),
538            #[cfg(feature = "extended_numeric_types")]
539            NumericArray::UInt8(a) => Ok(IntegerArray::<u64>::from(&*a)),
540            #[cfg(feature = "extended_numeric_types")]
541            NumericArray::UInt16(a) => Ok(IntegerArray::<u64>::from(&*a)),
542            NumericArray::UInt32(a) => Ok(IntegerArray::<u64>::from(&*a)),
543            NumericArray::UInt64(a) => match Arc::try_unwrap(a) {
544                Ok(inner) => Ok(inner),
545                Err(shared) => Ok((*shared).clone()),
546            },
547            NumericArray::Float32(a) => Ok(IntegerArray::<u64>::try_from(&*a)?),
548            NumericArray::Float64(a) => Ok(IntegerArray::<u64>::try_from(&*a)?),
549            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
550        }
551    }
552
553    /// Convert to FloatArray<f32> using From.
554    pub fn f32(self) -> Result<FloatArray<f32>, MinarrowError> {
555        match self {
556            #[cfg(feature = "extended_numeric_types")]
557            NumericArray::Int8(a) => Ok(FloatArray::<f32>::from(&*a)),
558            #[cfg(feature = "extended_numeric_types")]
559            NumericArray::Int16(a) => Ok(FloatArray::<f32>::from(&*a)),
560            NumericArray::Int32(a) => Ok(FloatArray::<f32>::from(&*a)),
561            NumericArray::Int64(a) => Ok(FloatArray::<f32>::from(&*a)),
562            #[cfg(feature = "extended_numeric_types")]
563            NumericArray::UInt8(a) => Ok(FloatArray::<f32>::from(&*a)),
564            #[cfg(feature = "extended_numeric_types")]
565            NumericArray::UInt16(a) => Ok(FloatArray::<f32>::from(&*a)),
566            NumericArray::UInt32(a) => Ok(FloatArray::<f32>::from(&*a)),
567            NumericArray::UInt64(a) => Ok(FloatArray::<f32>::from(&*a)),
568            NumericArray::Float32(a) => match Arc::try_unwrap(a) {
569                Ok(inner) => Ok(inner),
570                Err(shared) => Ok((*shared).clone()),
571            },
572            NumericArray::Float64(a) => Ok(FloatArray::<f32>::from(&*a)),
573            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
574        }
575    }
576
577    /// Cast this NumericArray to Float64, staying wrapped as NumericArray.
578    ///
579    /// If already Float64, returns self unchanged. Otherwise casts element
580    /// data to f64, preserving the null mask. Uses `Arc::try_unwrap` so that
581    /// if this is the sole owner of the backing Arc, the old data is consumed
582    /// and freed rather than cloned.
583    pub fn cow_into_f64(self) -> Self {
584        macro_rules! cast_arc {
585            ($arc:expr) => {
586                match Arc::try_unwrap($arc) {
587                    Ok(owned) => {
588                        let data: Vec64<f64> =
589                            owned.data.as_slice().iter().map(|&v| v as f64).collect();
590                        NumericArray::Float64(Arc::new(FloatArray::new(data, owned.null_mask)))
591                    }
592                    Err(shared) => {
593                        let data: Vec64<f64> =
594                            shared.data.as_slice().iter().map(|&v| v as f64).collect();
595                        NumericArray::Float64(Arc::new(FloatArray::new(
596                            data,
597                            shared.null_mask.clone(),
598                        )))
599                    }
600                }
601            };
602        }
603
604        match self {
605            NumericArray::Float64(_) => self,
606            NumericArray::Float32(arc) => cast_arc!(arc),
607            NumericArray::Int32(arc) => cast_arc!(arc),
608            NumericArray::Int64(arc) => cast_arc!(arc),
609            NumericArray::UInt32(arc) => cast_arc!(arc),
610            NumericArray::UInt64(arc) => cast_arc!(arc),
611            #[cfg(feature = "extended_numeric_types")]
612            NumericArray::Int8(arc) => cast_arc!(arc),
613            #[cfg(feature = "extended_numeric_types")]
614            NumericArray::Int16(arc) => cast_arc!(arc),
615            #[cfg(feature = "extended_numeric_types")]
616            NumericArray::UInt8(arc) => cast_arc!(arc),
617            #[cfg(feature = "extended_numeric_types")]
618            NumericArray::UInt16(arc) => cast_arc!(arc),
619            NumericArray::Null => {
620                NumericArray::Float64(Arc::new(FloatArray::new(Vec64::new(), None)))
621            }
622        }
623    }
624
625    /// Convert to FloatArray<f64> using From.
626    pub fn f64(self) -> Result<FloatArray<f64>, MinarrowError> {
627        match self {
628            #[cfg(feature = "extended_numeric_types")]
629            NumericArray::Int8(a) => Ok(FloatArray::<f64>::from(&*a)),
630            #[cfg(feature = "extended_numeric_types")]
631            NumericArray::Int16(a) => Ok(FloatArray::<f64>::from(&*a)),
632            NumericArray::Int32(a) => Ok(FloatArray::<f64>::from(&*a)),
633            NumericArray::Int64(a) => Ok(FloatArray::<f64>::from(&*a)),
634            #[cfg(feature = "extended_numeric_types")]
635            NumericArray::UInt8(a) => Ok(FloatArray::<f64>::from(&*a)),
636            #[cfg(feature = "extended_numeric_types")]
637            NumericArray::UInt16(a) => Ok(FloatArray::<f64>::from(&*a)),
638            NumericArray::UInt32(a) => Ok(FloatArray::<f64>::from(&*a)),
639            NumericArray::UInt64(a) => Ok(FloatArray::<f64>::from(&*a)),
640            NumericArray::Float32(a) => Ok(FloatArray::<f64>::from(&*a)),
641            NumericArray::Float64(a) => match Arc::try_unwrap(a) {
642                Ok(inner) => Ok(inner),
643                Err(shared) => Ok((*shared).clone()),
644            },
645            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
646        }
647    }
648
649    /// Converts to BooleanArray<u8>.
650    ///
651    /// All non-zero values become `true`, but the null mask is preserved.
652    pub fn bool(self) -> Result<BooleanArray<u8>, MinarrowError> {
653        match self {
654            #[cfg(feature = "extended_numeric_types")]
655            NumericArray::Int8(a) => Ok(BooleanArray::<u8>::from(&*a)),
656            #[cfg(feature = "extended_numeric_types")]
657            NumericArray::Int16(a) => Ok(BooleanArray::<u8>::from(&*a)),
658            NumericArray::Int32(a) => Ok(BooleanArray::<u8>::from(&*a)),
659            NumericArray::Int64(a) => Ok(BooleanArray::<u8>::from(&*a)),
660            #[cfg(feature = "extended_numeric_types")]
661            NumericArray::UInt8(a) => Ok(BooleanArray::<u8>::from(&*a)),
662            #[cfg(feature = "extended_numeric_types")]
663            NumericArray::UInt16(a) => Ok(BooleanArray::<u8>::from(&*a)),
664            NumericArray::UInt32(a) => Ok(BooleanArray::<u8>::from(&*a)),
665            NumericArray::UInt64(a) => Ok(BooleanArray::<u8>::from(&*a)),
666            NumericArray::Float32(a) => Ok(BooleanArray::<u8>::from(&*a)),
667            NumericArray::Float64(a) => Ok(BooleanArray::<u8>::from(&*a)),
668            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
669        }
670    }
671
672    /// Converts to StringArray<u32> by formatting each value as string.
673    ///
674    /// Preserves Null mask.
675    pub fn str(self) -> Result<StringArray<u32>, MinarrowError> {
676        match self {
677            #[cfg(feature = "extended_numeric_types")]
678            NumericArray::Int8(a) => Ok(StringArray::<u32>::from(&*a)),
679            #[cfg(feature = "extended_numeric_types")]
680            NumericArray::Int16(a) => Ok(StringArray::<u32>::from(&*a)),
681            NumericArray::Int32(a) => Ok(StringArray::<u32>::from(&*a)),
682            NumericArray::Int64(a) => Ok(StringArray::<u32>::from(&*a)),
683            #[cfg(feature = "extended_numeric_types")]
684            NumericArray::UInt8(a) => Ok(StringArray::<u32>::from(&*a)),
685            #[cfg(feature = "extended_numeric_types")]
686            NumericArray::UInt16(a) => Ok(StringArray::<u32>::from(&*a)),
687            NumericArray::UInt32(a) => Ok(StringArray::<u32>::from(&*a)),
688            NumericArray::UInt64(a) => Ok(StringArray::<u32>::from(&*a)),
689            NumericArray::Float32(a) => Ok(StringArray::<u32>::from(&*a)),
690            NumericArray::Float64(a) => Ok(StringArray::<u32>::from(&*a)),
691            NumericArray::Null => Err(MinarrowError::NullError { message: None }),
692        }
693    }
694}
695
696impl Display for NumericArray {
697    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
698        match self {
699            #[cfg(feature = "extended_numeric_types")]
700            NumericArray::Int8(arr) => write_numeric_array_with_header(f, "Int8", arr.as_ref()),
701            #[cfg(feature = "extended_numeric_types")]
702            NumericArray::Int16(arr) => write_numeric_array_with_header(f, "Int16", arr.as_ref()),
703            NumericArray::Int32(arr) => write_numeric_array_with_header(f, "Int32", arr.as_ref()),
704            NumericArray::Int64(arr) => write_numeric_array_with_header(f, "Int64", arr.as_ref()),
705            #[cfg(feature = "extended_numeric_types")]
706            NumericArray::UInt8(arr) => write_numeric_array_with_header(f, "UInt8", arr.as_ref()),
707            #[cfg(feature = "extended_numeric_types")]
708            NumericArray::UInt16(arr) => write_numeric_array_with_header(f, "UInt16", arr.as_ref()),
709            NumericArray::UInt32(arr) => write_numeric_array_with_header(f, "UInt32", arr.as_ref()),
710            NumericArray::UInt64(arr) => write_numeric_array_with_header(f, "UInt64", arr.as_ref()),
711            NumericArray::Float32(arr) => {
712                write_numeric_array_with_header(f, "Float32", arr.as_ref())
713            }
714            NumericArray::Float64(arr) => {
715                write_numeric_array_with_header(f, "Float64", arr.as_ref())
716            }
717            NumericArray::Null => writeln!(f, "NullNumericArray [0 values]"),
718        }
719    }
720}
721
722/// Writes the standard header, then delegates to the contained array's Display.
723fn write_numeric_array_with_header<T>(
724    f: &mut Formatter<'_>,
725    dtype_name: &str,
726    arr: &(impl MaskedArray<CopyType = T> + Display + ?Sized),
727) -> std::fmt::Result {
728    writeln!(
729        f,
730        "NumericArray [{dtype_name}] [{} values] (null count: {})",
731        arr.len(),
732        arr.null_count()
733    )?;
734    // Delegate row formatting
735    Display::fmt(arr, f)
736}
737
738impl Shape for NumericArray {
739    fn shape(&self) -> ShapeDim {
740        ShapeDim::Rank1(self.len())
741    }
742}
743
744// TODO: Add cross-type casting
745impl Concatenate for NumericArray {
746    fn concat(self, other: Self) -> Result<Self, MinarrowError> {
747        match (self, other) {
748            #[cfg(feature = "extended_numeric_types")]
749            (NumericArray::Int8(a), NumericArray::Int8(b)) => {
750                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
751                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
752                Ok(NumericArray::Int8(Arc::new(a.concat(b)?)))
753            }
754            #[cfg(feature = "extended_numeric_types")]
755            (NumericArray::Int16(a), NumericArray::Int16(b)) => {
756                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
757                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
758                Ok(NumericArray::Int16(Arc::new(a.concat(b)?)))
759            }
760            (NumericArray::Int32(a), NumericArray::Int32(b)) => {
761                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
762                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
763                Ok(NumericArray::Int32(Arc::new(a.concat(b)?)))
764            }
765            (NumericArray::Int64(a), NumericArray::Int64(b)) => {
766                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
767                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
768                Ok(NumericArray::Int64(Arc::new(a.concat(b)?)))
769            }
770            #[cfg(feature = "extended_numeric_types")]
771            (NumericArray::UInt8(a), NumericArray::UInt8(b)) => {
772                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
773                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
774                Ok(NumericArray::UInt8(Arc::new(a.concat(b)?)))
775            }
776            #[cfg(feature = "extended_numeric_types")]
777            (NumericArray::UInt16(a), NumericArray::UInt16(b)) => {
778                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
779                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
780                Ok(NumericArray::UInt16(Arc::new(a.concat(b)?)))
781            }
782            (NumericArray::UInt32(a), NumericArray::UInt32(b)) => {
783                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
784                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
785                Ok(NumericArray::UInt32(Arc::new(a.concat(b)?)))
786            }
787            (NumericArray::UInt64(a), NumericArray::UInt64(b)) => {
788                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
789                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
790                Ok(NumericArray::UInt64(Arc::new(a.concat(b)?)))
791            }
792            (NumericArray::Float32(a), NumericArray::Float32(b)) => {
793                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
794                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
795                Ok(NumericArray::Float32(Arc::new(a.concat(b)?)))
796            }
797            (NumericArray::Float64(a), NumericArray::Float64(b)) => {
798                let a = Arc::try_unwrap(a).unwrap_or_else(|arc| (*arc).clone());
799                let b = Arc::try_unwrap(b).unwrap_or_else(|arc| (*arc).clone());
800                Ok(NumericArray::Float64(Arc::new(a.concat(b)?)))
801            }
802            (NumericArray::Null, NumericArray::Null) => Ok(NumericArray::Null),
803            (lhs, rhs) => Err(MinarrowError::IncompatibleTypeError {
804                from: "NumericArray",
805                to: "NumericArray",
806                message: Some(format!(
807                    "Cannot concatenate mismatched NumericArray variants: {:?} and {:?}",
808                    variant_name(&lhs),
809                    variant_name(&rhs)
810                )),
811            }),
812        }
813    }
814}
815
816/// Helper function to get the variant name for error messages
817fn variant_name(arr: &NumericArray) -> &'static str {
818    match arr {
819        #[cfg(feature = "extended_numeric_types")]
820        NumericArray::Int8(_) => "Int8",
821        #[cfg(feature = "extended_numeric_types")]
822        NumericArray::Int16(_) => "Int16",
823        NumericArray::Int32(_) => "Int32",
824        NumericArray::Int64(_) => "Int64",
825        #[cfg(feature = "extended_numeric_types")]
826        NumericArray::UInt8(_) => "UInt8",
827        #[cfg(feature = "extended_numeric_types")]
828        NumericArray::UInt16(_) => "UInt16",
829        NumericArray::UInt32(_) => "UInt32",
830        NumericArray::UInt64(_) => "UInt64",
831        NumericArray::Float32(_) => "Float32",
832        NumericArray::Float64(_) => "Float64",
833        NumericArray::Null => "Null",
834    }
835}