polars_compute/cast/
mod.rs

1//! Defines different casting operators such as [`cast`] or [`primitive_to_binary`].
2
3mod binary_to;
4mod binview_to;
5mod boolean_to;
6mod decimal_to;
7mod dictionary_to;
8mod primitive_to;
9mod utf8_to;
10
11pub use binary_to::*;
12#[cfg(feature = "dtype-decimal")]
13pub use binview_to::binview_to_decimal;
14use binview_to::binview_to_primitive_dyn;
15pub use binview_to::utf8view_to_utf8;
16pub use boolean_to::*;
17pub use decimal_to::*;
18pub mod temporal;
19use arrow::array::*;
20use arrow::datatypes::*;
21use arrow::match_integer_type;
22use arrow::offset::{Offset, Offsets};
23use binview_to::{
24    binview_to_dictionary, utf8view_to_date32_dyn, utf8view_to_dictionary,
25    utf8view_to_naive_timestamp_dyn, view_to_binary,
26};
27use dictionary_to::*;
28use polars_error::{polars_bail, polars_ensure, polars_err, PolarsResult};
29use polars_utils::IdxSize;
30pub use primitive_to::*;
31use temporal::utf8view_to_timestamp;
32pub use utf8_to::*;
33
34/// options defining how Cast kernels behave
35#[derive(Clone, Copy, Debug, Default)]
36pub struct CastOptionsImpl {
37    /// default to false
38    /// whether an overflowing cast should be converted to `None` (default), or be wrapped (i.e. `256i16 as u8 = 0` vectorized).
39    /// Settings this to `true` is 5-6x faster for numeric types.
40    pub wrapped: bool,
41    /// default to false
42    /// whether to cast to an integer at the best-effort
43    pub partial: bool,
44}
45
46impl CastOptionsImpl {
47    pub fn unchecked() -> Self {
48        Self {
49            wrapped: true,
50            partial: false,
51        }
52    }
53}
54
55impl CastOptionsImpl {
56    fn with_wrapped(&self, v: bool) -> Self {
57        let mut option = *self;
58        option.wrapped = v;
59        option
60    }
61}
62
63macro_rules! primitive_dyn {
64    ($from:expr, $expr:tt) => {{
65        let from = $from.as_any().downcast_ref().unwrap();
66        Ok(Box::new($expr(from)))
67    }};
68    ($from:expr, $expr:tt, $to:expr) => {{
69        let from = $from.as_any().downcast_ref().unwrap();
70        Ok(Box::new($expr(from, $to)))
71    }};
72    ($from:expr, $expr:tt, $from_t:expr, $to:expr) => {{
73        let from = $from.as_any().downcast_ref().unwrap();
74        Ok(Box::new($expr(from, $from_t, $to)))
75    }};
76    ($from:expr, $expr:tt, $arg1:expr, $arg2:expr, $arg3:expr) => {{
77        let from = $from.as_any().downcast_ref().unwrap();
78        Ok(Box::new($expr(from, $arg1, $arg2, $arg3)))
79    }};
80}
81
82fn cast_struct(
83    array: &StructArray,
84    to_type: &ArrowDataType,
85    options: CastOptionsImpl,
86) -> PolarsResult<StructArray> {
87    let values = array.values();
88    let fields = StructArray::get_fields(to_type);
89    let new_values = values
90        .iter()
91        .zip(fields)
92        .map(|(arr, field)| cast(arr.as_ref(), field.dtype(), options))
93        .collect::<PolarsResult<Vec<_>>>()?;
94
95    Ok(StructArray::new(
96        to_type.clone(),
97        array.len(),
98        new_values,
99        array.validity().cloned(),
100    ))
101}
102
103fn cast_list<O: Offset>(
104    array: &ListArray<O>,
105    to_type: &ArrowDataType,
106    options: CastOptionsImpl,
107) -> PolarsResult<ListArray<O>> {
108    let values = array.values();
109    let new_values = cast(
110        values.as_ref(),
111        ListArray::<O>::get_child_type(to_type),
112        options,
113    )?;
114
115    Ok(ListArray::<O>::new(
116        to_type.clone(),
117        array.offsets().clone(),
118        new_values,
119        array.validity().cloned(),
120    ))
121}
122
123fn cast_list_to_large_list(array: &ListArray<i32>, to_type: &ArrowDataType) -> ListArray<i64> {
124    let offsets = array.offsets().into();
125
126    ListArray::<i64>::new(
127        to_type.clone(),
128        offsets,
129        array.values().clone(),
130        array.validity().cloned(),
131    )
132}
133
134fn cast_large_to_list(array: &ListArray<i64>, to_type: &ArrowDataType) -> ListArray<i32> {
135    let offsets = array.offsets().try_into().expect("Convertme to error");
136
137    ListArray::<i32>::new(
138        to_type.clone(),
139        offsets,
140        array.values().clone(),
141        array.validity().cloned(),
142    )
143}
144
145fn cast_fixed_size_list_to_list<O: Offset>(
146    fixed: &FixedSizeListArray,
147    to_type: &ArrowDataType,
148    options: CastOptionsImpl,
149) -> PolarsResult<ListArray<O>> {
150    let new_values = cast(
151        fixed.values().as_ref(),
152        ListArray::<O>::get_child_type(to_type),
153        options,
154    )?;
155
156    let offsets = (0..=fixed.len())
157        .map(|ix| O::from_as_usize(ix * fixed.size()))
158        .collect::<Vec<_>>();
159    // SAFETY: offsets _are_ monotonically increasing
160    let offsets = unsafe { Offsets::new_unchecked(offsets) };
161
162    Ok(ListArray::<O>::new(
163        to_type.clone(),
164        offsets.into(),
165        new_values,
166        fixed.validity().cloned(),
167    ))
168}
169
170fn cast_list_to_fixed_size_list<O: Offset>(
171    list: &ListArray<O>,
172    inner: &Field,
173    size: usize,
174    options: CastOptionsImpl,
175) -> PolarsResult<FixedSizeListArray> {
176    let null_cnt = list.null_count();
177    let new_values = if null_cnt == 0 {
178        let start_offset = list.offsets().first().to_usize();
179        let offsets = list.offsets().buffer();
180
181        let mut is_valid = true;
182        for (i, offset) in offsets.iter().enumerate() {
183            is_valid &= offset.to_usize() == start_offset + i * size;
184        }
185
186        polars_ensure!(is_valid, ComputeError: "not all elements have the specified width {size}");
187
188        let sliced_values = list
189            .values()
190            .sliced(start_offset, list.offsets().range().to_usize());
191        cast(sliced_values.as_ref(), inner.dtype(), options)?
192    } else {
193        let offsets = list.offsets().as_slice();
194        // Check the lengths of each list are equal to the fixed size.
195        // SAFETY: we know the index is in bound.
196        let mut expected_offset = unsafe { *offsets.get_unchecked(0) } + O::from_as_usize(size);
197        for i in 1..=list.len() {
198            // SAFETY: we know the index is in bound.
199            let current_offset = unsafe { *offsets.get_unchecked(i) };
200            if list.is_null(i - 1) {
201                expected_offset = current_offset + O::from_as_usize(size);
202            } else {
203                polars_ensure!(current_offset == expected_offset, ComputeError:
204            "not all elements have the specified width {size}");
205                expected_offset += O::from_as_usize(size);
206            }
207        }
208
209        // Build take indices for the values. This is used to fill in the null slots.
210        let mut indices =
211            MutablePrimitiveArray::<IdxSize>::with_capacity(list.values().len() + null_cnt * size);
212        for i in 0..list.len() {
213            if list.is_null(i) {
214                indices.extend_constant(size, None)
215            } else {
216                // SAFETY: we know the index is in bound.
217                let current_offset = unsafe { *offsets.get_unchecked(i) };
218                for j in 0..size {
219                    indices.push(Some(
220                        (current_offset + O::from_as_usize(j)).to_usize() as IdxSize
221                    ));
222                }
223            }
224        }
225        let take_values =
226            unsafe { crate::gather::take_unchecked(list.values().as_ref(), &indices.freeze()) };
227
228        cast(take_values.as_ref(), inner.dtype(), options)?
229    };
230
231    FixedSizeListArray::try_new(
232        ArrowDataType::FixedSizeList(Box::new(inner.clone()), size),
233        list.len(),
234        new_values,
235        list.validity().cloned(),
236    )
237    .map_err(|_| polars_err!(ComputeError: "not all elements have the specified width {size}"))
238}
239
240pub fn cast_default(array: &dyn Array, to_type: &ArrowDataType) -> PolarsResult<Box<dyn Array>> {
241    cast(array, to_type, Default::default())
242}
243
244pub fn cast_unchecked(array: &dyn Array, to_type: &ArrowDataType) -> PolarsResult<Box<dyn Array>> {
245    cast(array, to_type, CastOptionsImpl::unchecked())
246}
247
248/// Cast `array` to the provided data type and return a new [`Array`] with
249/// type `to_type`, if possible.
250///
251/// Behavior:
252/// * PrimitiveArray to PrimitiveArray: overflowing cast will be None
253/// * Boolean to Utf8: `true` => '1', `false` => `0`
254/// * Utf8 to numeric: strings that can't be parsed to numbers return null, float strings
255///   in integer casts return null
256/// * Numeric to boolean: 0 returns `false`, any other value returns `true`
257/// * List to List: the underlying data type is cast
258/// * Fixed Size List to List: the underlying data type is cast
259/// * List to Fixed Size List: the offsets are checked for valid order, then the
260///   underlying type is cast.
261/// * Struct to Struct: the underlying fields are cast.
262/// * PrimitiveArray to List: a list array with 1 value per slot is created
263/// * Date32 and Date64: precision lost when going to higher interval
264/// * Time32 and Time64: precision lost when going to higher interval
265/// * Timestamp and Date{32|64}: precision lost when going to higher interval
266/// * Temporal to/from backing primitive: zero-copy with data type change
267///
268/// Unsupported Casts
269/// * non-`StructArray` to `StructArray` or `StructArray` to non-`StructArray`
270/// * List to primitive
271/// * Utf8 to boolean
272/// * Interval and duration
273pub fn cast(
274    array: &dyn Array,
275    to_type: &ArrowDataType,
276    options: CastOptionsImpl,
277) -> PolarsResult<Box<dyn Array>> {
278    use ArrowDataType::*;
279    let from_type = array.dtype();
280
281    // clone array if types are the same
282    if from_type == to_type {
283        return Ok(clone(array));
284    }
285
286    let as_options = options.with_wrapped(true);
287    match (from_type, to_type) {
288        (Null, _) | (_, Null) => Ok(new_null_array(to_type.clone(), array.len())),
289        (Struct(from_fd), Struct(to_fd)) => {
290            polars_ensure!(from_fd.len() == to_fd.len(), InvalidOperation: "Cannot cast struct with different number of fields.");
291            cast_struct(array.as_any().downcast_ref().unwrap(), to_type, options).map(|x| x.boxed())
292        },
293        (Struct(_), _) | (_, Struct(_)) => polars_bail!(InvalidOperation:
294            "Cannot cast from struct to other types"
295        ),
296        (Dictionary(index_type, ..), _) => match_integer_type!(index_type, |$T| {
297            dictionary_cast_dyn::<$T>(array, to_type, options)
298        }),
299        (_, Dictionary(index_type, value_type, _)) => match_integer_type!(index_type, |$T| {
300            cast_to_dictionary::<$T>(array, value_type, options)
301        }),
302        // not supported by polars
303        // (List(_), FixedSizeList(inner, size)) => cast_list_to_fixed_size_list::<i32>(
304        //     array.as_any().downcast_ref().unwrap(),
305        //     inner.as_ref(),
306        //     *size,
307        //     options,
308        // )
309        // .map(|x| x.boxed()),
310        (LargeList(_), FixedSizeList(inner, size)) => cast_list_to_fixed_size_list::<i64>(
311            array.as_any().downcast_ref().unwrap(),
312            inner.as_ref(),
313            *size,
314            options,
315        )
316        .map(|x| x.boxed()),
317        (FixedSizeList(_, _), List(_)) => cast_fixed_size_list_to_list::<i32>(
318            array.as_any().downcast_ref().unwrap(),
319            to_type,
320            options,
321        )
322        .map(|x| x.boxed()),
323        (FixedSizeList(_, _), LargeList(_)) => cast_fixed_size_list_to_list::<i64>(
324            array.as_any().downcast_ref().unwrap(),
325            to_type,
326            options,
327        )
328        .map(|x| x.boxed()),
329        (BinaryView, _) => match to_type {
330            Utf8View => array
331                .as_any()
332                .downcast_ref::<BinaryViewArray>()
333                .unwrap()
334                .to_utf8view()
335                .map(|arr| arr.boxed()),
336            LargeBinary => Ok(binview_to::view_to_binary::<i64>(
337                array.as_any().downcast_ref().unwrap(),
338            )
339            .boxed()),
340            LargeList(inner) if matches!(inner.dtype, ArrowDataType::UInt8) => {
341                let bin_array = view_to_binary::<i64>(array.as_any().downcast_ref().unwrap());
342                Ok(binary_to_list(&bin_array, to_type.clone()).boxed())
343            },
344            _ => polars_bail!(InvalidOperation:
345                "casting from {from_type:?} to {to_type:?} not supported",
346            ),
347        },
348        (LargeList(_), LargeList(_)) => {
349            cast_list::<i64>(array.as_any().downcast_ref().unwrap(), to_type, options)
350                .map(|x| x.boxed())
351        },
352        (List(lhs), LargeList(rhs)) if lhs == rhs => {
353            Ok(cast_list_to_large_list(array.as_any().downcast_ref().unwrap(), to_type).boxed())
354        },
355        (LargeList(lhs), List(rhs)) if lhs == rhs => {
356            Ok(cast_large_to_list(array.as_any().downcast_ref().unwrap(), to_type).boxed())
357        },
358
359        (_, List(to)) => {
360            // cast primitive to list's primitive
361            let values = cast(array, &to.dtype, options)?;
362            // create offsets, where if array.len() = 2, we have [0,1,2]
363            let offsets = (0..=array.len() as i32).collect::<Vec<_>>();
364            // SAFETY: offsets _are_ monotonically increasing
365            let offsets = unsafe { Offsets::new_unchecked(offsets) };
366
367            let list_array = ListArray::<i32>::new(to_type.clone(), offsets.into(), values, None);
368
369            Ok(Box::new(list_array))
370        },
371
372        (_, LargeList(to)) if from_type != &LargeBinary => {
373            // cast primitive to list's primitive
374            let values = cast(array, &to.dtype, options)?;
375            // create offsets, where if array.len() = 2, we have [0,1,2]
376            let offsets = (0..=array.len() as i64).collect::<Vec<_>>();
377            // SAFETY: offsets _are_ monotonically increasing
378            let offsets = unsafe { Offsets::new_unchecked(offsets) };
379
380            let list_array = ListArray::<i64>::new(
381                to_type.clone(),
382                offsets.into(),
383                values,
384                array.validity().cloned(),
385            );
386
387            Ok(Box::new(list_array))
388        },
389
390        (Utf8View, _) => {
391            let arr = array.as_any().downcast_ref::<Utf8ViewArray>().unwrap();
392
393            match to_type {
394                BinaryView => Ok(arr.to_binview().boxed()),
395                LargeUtf8 => Ok(binview_to::utf8view_to_utf8::<i64>(arr).boxed()),
396                UInt8 => binview_to_primitive_dyn::<u8>(&arr.to_binview(), to_type, options),
397                UInt16 => binview_to_primitive_dyn::<u16>(&arr.to_binview(), to_type, options),
398                UInt32 => binview_to_primitive_dyn::<u32>(&arr.to_binview(), to_type, options),
399                UInt64 => binview_to_primitive_dyn::<u64>(&arr.to_binview(), to_type, options),
400                Int8 => binview_to_primitive_dyn::<i8>(&arr.to_binview(), to_type, options),
401                Int16 => binview_to_primitive_dyn::<i16>(&arr.to_binview(), to_type, options),
402                Int32 => binview_to_primitive_dyn::<i32>(&arr.to_binview(), to_type, options),
403                Int64 => binview_to_primitive_dyn::<i64>(&arr.to_binview(), to_type, options),
404                #[cfg(feature = "dtype-i128")]
405                Int128 => binview_to_primitive_dyn::<i128>(&arr.to_binview(), to_type, options),
406                Float32 => binview_to_primitive_dyn::<f32>(&arr.to_binview(), to_type, options),
407                Float64 => binview_to_primitive_dyn::<f64>(&arr.to_binview(), to_type, options),
408                Timestamp(time_unit, None) => {
409                    utf8view_to_naive_timestamp_dyn(array, time_unit.to_owned())
410                },
411                Timestamp(time_unit, Some(time_zone)) => utf8view_to_timestamp(
412                    array.as_any().downcast_ref().unwrap(),
413                    RFC3339,
414                    time_zone.clone(),
415                    time_unit.to_owned(),
416                )
417                .map(|arr| arr.boxed()),
418                Date32 => utf8view_to_date32_dyn(array),
419                #[cfg(feature = "dtype-decimal")]
420                Decimal(precision, scale) => {
421                    Ok(binview_to_decimal(&arr.to_binview(), Some(*precision), *scale).to_boxed())
422                },
423                _ => polars_bail!(InvalidOperation:
424                    "casting from {from_type:?} to {to_type:?} not supported",
425                ),
426            }
427        },
428
429        (_, Boolean) => match from_type {
430            UInt8 => primitive_to_boolean_dyn::<u8>(array, to_type.clone()),
431            UInt16 => primitive_to_boolean_dyn::<u16>(array, to_type.clone()),
432            UInt32 => primitive_to_boolean_dyn::<u32>(array, to_type.clone()),
433            UInt64 => primitive_to_boolean_dyn::<u64>(array, to_type.clone()),
434            Int8 => primitive_to_boolean_dyn::<i8>(array, to_type.clone()),
435            Int16 => primitive_to_boolean_dyn::<i16>(array, to_type.clone()),
436            Int32 => primitive_to_boolean_dyn::<i32>(array, to_type.clone()),
437            Int64 => primitive_to_boolean_dyn::<i64>(array, to_type.clone()),
438            #[cfg(feature = "dtype-i128")]
439            Int128 => primitive_to_boolean_dyn::<i128>(array, to_type.clone()),
440            Float32 => primitive_to_boolean_dyn::<f32>(array, to_type.clone()),
441            Float64 => primitive_to_boolean_dyn::<f64>(array, to_type.clone()),
442            Decimal(_, _) => primitive_to_boolean_dyn::<i128>(array, to_type.clone()),
443            _ => polars_bail!(InvalidOperation:
444                "casting from {from_type:?} to {to_type:?} not supported",
445            ),
446        },
447        (Boolean, _) => match to_type {
448            UInt8 => boolean_to_primitive_dyn::<u8>(array),
449            UInt16 => boolean_to_primitive_dyn::<u16>(array),
450            UInt32 => boolean_to_primitive_dyn::<u32>(array),
451            UInt64 => boolean_to_primitive_dyn::<u64>(array),
452            Int8 => boolean_to_primitive_dyn::<i8>(array),
453            Int16 => boolean_to_primitive_dyn::<i16>(array),
454            Int32 => boolean_to_primitive_dyn::<i32>(array),
455            Int64 => boolean_to_primitive_dyn::<i64>(array),
456            #[cfg(feature = "dtype-i128")]
457            Int128 => boolean_to_primitive_dyn::<i128>(array),
458            Float32 => boolean_to_primitive_dyn::<f32>(array),
459            Float64 => boolean_to_primitive_dyn::<f64>(array),
460            Utf8View => boolean_to_utf8view_dyn(array),
461            BinaryView => boolean_to_binaryview_dyn(array),
462            _ => polars_bail!(InvalidOperation:
463                "casting from {from_type:?} to {to_type:?} not supported",
464            ),
465        },
466        (_, BinaryView) => from_to_binview(array, from_type, to_type).map(|arr| arr.boxed()),
467        (_, Utf8View) => match from_type {
468            LargeUtf8 => Ok(utf8_to_utf8view(
469                array.as_any().downcast_ref::<Utf8Array<i64>>().unwrap(),
470            )
471            .boxed()),
472            Utf8 => Ok(
473                utf8_to_utf8view(array.as_any().downcast_ref::<Utf8Array<i32>>().unwrap()).boxed(),
474            ),
475            #[cfg(feature = "dtype-decimal")]
476            Decimal(_, _) => Ok(decimal_to_utf8view_dyn(array).boxed()),
477            _ => from_to_binview(array, from_type, to_type)
478                .map(|arr| unsafe { arr.to_utf8view_unchecked() }.boxed()),
479        },
480        (Utf8, _) => match to_type {
481            LargeUtf8 => Ok(Box::new(utf8_to_large_utf8(
482                array.as_any().downcast_ref().unwrap(),
483            ))),
484            _ => polars_bail!(InvalidOperation:
485                "casting from {from_type:?} to {to_type:?} not supported",
486            ),
487        },
488        (LargeUtf8, _) => match to_type {
489            LargeBinary => Ok(utf8_to_binary::<i64>(
490                array.as_any().downcast_ref().unwrap(),
491                to_type.clone(),
492            )
493            .boxed()),
494            _ => polars_bail!(InvalidOperation:
495                "casting from {from_type:?} to {to_type:?} not supported",
496            ),
497        },
498        (_, LargeUtf8) => match from_type {
499            UInt8 => primitive_to_utf8_dyn::<u8, i64>(array),
500            LargeBinary => {
501                binary_to_utf8::<i64>(array.as_any().downcast_ref().unwrap(), to_type.clone())
502                    .map(|x| x.boxed())
503            },
504            _ => polars_bail!(InvalidOperation:
505                "casting from {from_type:?} to {to_type:?} not supported",
506            ),
507        },
508
509        (Binary, _) => match to_type {
510            LargeBinary => Ok(Box::new(binary_to_large_binary(
511                array.as_any().downcast_ref().unwrap(),
512                to_type.clone(),
513            ))),
514            _ => polars_bail!(InvalidOperation:
515                "casting from {from_type:?} to {to_type:?} not supported",
516            ),
517        },
518
519        (LargeBinary, _) => match to_type {
520            UInt8 => binary_to_primitive_dyn::<i64, u8>(array, to_type, options),
521            UInt16 => binary_to_primitive_dyn::<i64, u16>(array, to_type, options),
522            UInt32 => binary_to_primitive_dyn::<i64, u32>(array, to_type, options),
523            UInt64 => binary_to_primitive_dyn::<i64, u64>(array, to_type, options),
524            Int8 => binary_to_primitive_dyn::<i64, i8>(array, to_type, options),
525            Int16 => binary_to_primitive_dyn::<i64, i16>(array, to_type, options),
526            Int32 => binary_to_primitive_dyn::<i64, i32>(array, to_type, options),
527            Int64 => binary_to_primitive_dyn::<i64, i64>(array, to_type, options),
528            #[cfg(feature = "dtype-i128")]
529            Int128 => binary_to_primitive_dyn::<i64, i128>(array, to_type, options),
530            Float32 => binary_to_primitive_dyn::<i64, f32>(array, to_type, options),
531            Float64 => binary_to_primitive_dyn::<i64, f64>(array, to_type, options),
532            Binary => {
533                binary_large_to_binary(array.as_any().downcast_ref().unwrap(), to_type.clone())
534                    .map(|x| x.boxed())
535            },
536            LargeUtf8 => {
537                binary_to_utf8::<i64>(array.as_any().downcast_ref().unwrap(), to_type.clone())
538                    .map(|x| x.boxed())
539            },
540            _ => polars_bail!(InvalidOperation:
541                "casting from {from_type:?} to {to_type:?} not supported",
542            ),
543        },
544        (FixedSizeBinary(_), _) => match to_type {
545            Binary => Ok(fixed_size_binary_binary::<i32>(
546                array.as_any().downcast_ref().unwrap(),
547                to_type.clone(),
548            )
549            .boxed()),
550            LargeBinary => Ok(fixed_size_binary_binary::<i64>(
551                array.as_any().downcast_ref().unwrap(),
552                to_type.clone(),
553            )
554            .boxed()),
555            _ => polars_bail!(InvalidOperation:
556                "casting from {from_type:?} to {to_type:?} not supported",
557            ),
558        },
559        // start numeric casts
560        (UInt8, UInt16) => primitive_to_primitive_dyn::<u8, u16>(array, to_type, as_options),
561        (UInt8, UInt32) => primitive_to_primitive_dyn::<u8, u32>(array, to_type, as_options),
562        (UInt8, UInt64) => primitive_to_primitive_dyn::<u8, u64>(array, to_type, as_options),
563        (UInt8, Int8) => primitive_to_primitive_dyn::<u8, i8>(array, to_type, options),
564        (UInt8, Int16) => primitive_to_primitive_dyn::<u8, i16>(array, to_type, options),
565        (UInt8, Int32) => primitive_to_primitive_dyn::<u8, i32>(array, to_type, options),
566        (UInt8, Int64) => primitive_to_primitive_dyn::<u8, i64>(array, to_type, options),
567        #[cfg(feature = "dtype-i128")]
568        (UInt8, Int128) => primitive_to_primitive_dyn::<u8, i128>(array, to_type, options),
569        (UInt8, Float32) => primitive_to_primitive_dyn::<u8, f32>(array, to_type, as_options),
570        (UInt8, Float64) => primitive_to_primitive_dyn::<u8, f64>(array, to_type, as_options),
571        (UInt8, Decimal(p, s)) => integer_to_decimal_dyn::<u8>(array, *p, *s),
572
573        (UInt16, UInt8) => primitive_to_primitive_dyn::<u16, u8>(array, to_type, options),
574        (UInt16, UInt32) => primitive_to_primitive_dyn::<u16, u32>(array, to_type, as_options),
575        (UInt16, UInt64) => primitive_to_primitive_dyn::<u16, u64>(array, to_type, as_options),
576        (UInt16, Int8) => primitive_to_primitive_dyn::<u16, i8>(array, to_type, options),
577        (UInt16, Int16) => primitive_to_primitive_dyn::<u16, i16>(array, to_type, options),
578        (UInt16, Int32) => primitive_to_primitive_dyn::<u16, i32>(array, to_type, options),
579        (UInt16, Int64) => primitive_to_primitive_dyn::<u16, i64>(array, to_type, options),
580        #[cfg(feature = "dtype-i128")]
581        (UInt16, Int128) => primitive_to_primitive_dyn::<u16, i128>(array, to_type, options),
582        (UInt16, Float32) => primitive_to_primitive_dyn::<u16, f32>(array, to_type, as_options),
583        (UInt16, Float64) => primitive_to_primitive_dyn::<u16, f64>(array, to_type, as_options),
584        (UInt16, Decimal(p, s)) => integer_to_decimal_dyn::<u16>(array, *p, *s),
585
586        (UInt32, UInt8) => primitive_to_primitive_dyn::<u32, u8>(array, to_type, options),
587        (UInt32, UInt16) => primitive_to_primitive_dyn::<u32, u16>(array, to_type, options),
588        (UInt32, UInt64) => primitive_to_primitive_dyn::<u32, u64>(array, to_type, as_options),
589        (UInt32, Int8) => primitive_to_primitive_dyn::<u32, i8>(array, to_type, options),
590        (UInt32, Int16) => primitive_to_primitive_dyn::<u32, i16>(array, to_type, options),
591        (UInt32, Int32) => primitive_to_primitive_dyn::<u32, i32>(array, to_type, options),
592        (UInt32, Int64) => primitive_to_primitive_dyn::<u32, i64>(array, to_type, options),
593        #[cfg(feature = "dtype-i128")]
594        (UInt32, Int128) => primitive_to_primitive_dyn::<u32, i128>(array, to_type, options),
595        (UInt32, Float32) => primitive_to_primitive_dyn::<u32, f32>(array, to_type, as_options),
596        (UInt32, Float64) => primitive_to_primitive_dyn::<u32, f64>(array, to_type, as_options),
597        (UInt32, Decimal(p, s)) => integer_to_decimal_dyn::<u32>(array, *p, *s),
598
599        (UInt64, UInt8) => primitive_to_primitive_dyn::<u64, u8>(array, to_type, options),
600        (UInt64, UInt16) => primitive_to_primitive_dyn::<u64, u16>(array, to_type, options),
601        (UInt64, UInt32) => primitive_to_primitive_dyn::<u64, u32>(array, to_type, options),
602        (UInt64, Int8) => primitive_to_primitive_dyn::<u64, i8>(array, to_type, options),
603        (UInt64, Int16) => primitive_to_primitive_dyn::<u64, i16>(array, to_type, options),
604        (UInt64, Int32) => primitive_to_primitive_dyn::<u64, i32>(array, to_type, options),
605        (UInt64, Int64) => primitive_to_primitive_dyn::<u64, i64>(array, to_type, options),
606        #[cfg(feature = "dtype-i128")]
607        (UInt64, Int128) => primitive_to_primitive_dyn::<u64, i128>(array, to_type, options),
608        (UInt64, Float32) => primitive_to_primitive_dyn::<u64, f32>(array, to_type, as_options),
609        (UInt64, Float64) => primitive_to_primitive_dyn::<u64, f64>(array, to_type, as_options),
610        (UInt64, Decimal(p, s)) => integer_to_decimal_dyn::<u64>(array, *p, *s),
611
612        (Int8, UInt8) => primitive_to_primitive_dyn::<i8, u8>(array, to_type, options),
613        (Int8, UInt16) => primitive_to_primitive_dyn::<i8, u16>(array, to_type, options),
614        (Int8, UInt32) => primitive_to_primitive_dyn::<i8, u32>(array, to_type, options),
615        (Int8, UInt64) => primitive_to_primitive_dyn::<i8, u64>(array, to_type, options),
616        (Int8, Int16) => primitive_to_primitive_dyn::<i8, i16>(array, to_type, as_options),
617        (Int8, Int32) => primitive_to_primitive_dyn::<i8, i32>(array, to_type, as_options),
618        (Int8, Int64) => primitive_to_primitive_dyn::<i8, i64>(array, to_type, as_options),
619        #[cfg(feature = "dtype-i128")]
620        (Int8, Int128) => primitive_to_primitive_dyn::<i8, i128>(array, to_type, as_options),
621        (Int8, Float32) => primitive_to_primitive_dyn::<i8, f32>(array, to_type, as_options),
622        (Int8, Float64) => primitive_to_primitive_dyn::<i8, f64>(array, to_type, as_options),
623        (Int8, Decimal(p, s)) => integer_to_decimal_dyn::<i8>(array, *p, *s),
624
625        (Int16, UInt8) => primitive_to_primitive_dyn::<i16, u8>(array, to_type, options),
626        (Int16, UInt16) => primitive_to_primitive_dyn::<i16, u16>(array, to_type, options),
627        (Int16, UInt32) => primitive_to_primitive_dyn::<i16, u32>(array, to_type, options),
628        (Int16, UInt64) => primitive_to_primitive_dyn::<i16, u64>(array, to_type, options),
629        (Int16, Int8) => primitive_to_primitive_dyn::<i16, i8>(array, to_type, options),
630        (Int16, Int32) => primitive_to_primitive_dyn::<i16, i32>(array, to_type, as_options),
631        (Int16, Int64) => primitive_to_primitive_dyn::<i16, i64>(array, to_type, as_options),
632        #[cfg(feature = "dtype-i128")]
633        (Int16, Int128) => primitive_to_primitive_dyn::<i16, i128>(array, to_type, as_options),
634        (Int16, Float32) => primitive_to_primitive_dyn::<i16, f32>(array, to_type, as_options),
635        (Int16, Float64) => primitive_to_primitive_dyn::<i16, f64>(array, to_type, as_options),
636        (Int16, Decimal(p, s)) => integer_to_decimal_dyn::<i16>(array, *p, *s),
637
638        (Int32, UInt8) => primitive_to_primitive_dyn::<i32, u8>(array, to_type, options),
639        (Int32, UInt16) => primitive_to_primitive_dyn::<i32, u16>(array, to_type, options),
640        (Int32, UInt32) => primitive_to_primitive_dyn::<i32, u32>(array, to_type, options),
641        (Int32, UInt64) => primitive_to_primitive_dyn::<i32, u64>(array, to_type, options),
642        (Int32, Int8) => primitive_to_primitive_dyn::<i32, i8>(array, to_type, options),
643        (Int32, Int16) => primitive_to_primitive_dyn::<i32, i16>(array, to_type, options),
644        (Int32, Int64) => primitive_to_primitive_dyn::<i32, i64>(array, to_type, as_options),
645        #[cfg(feature = "dtype-i128")]
646        (Int32, Int128) => primitive_to_primitive_dyn::<i32, i128>(array, to_type, as_options),
647        (Int32, Float32) => primitive_to_primitive_dyn::<i32, f32>(array, to_type, as_options),
648        (Int32, Float64) => primitive_to_primitive_dyn::<i32, f64>(array, to_type, as_options),
649        (Int32, Decimal(p, s)) => integer_to_decimal_dyn::<i32>(array, *p, *s),
650
651        (Int64, UInt8) => primitive_to_primitive_dyn::<i64, u8>(array, to_type, options),
652        (Int64, UInt16) => primitive_to_primitive_dyn::<i64, u16>(array, to_type, options),
653        (Int64, UInt32) => primitive_to_primitive_dyn::<i64, u32>(array, to_type, options),
654        (Int64, UInt64) => primitive_to_primitive_dyn::<i64, u64>(array, to_type, options),
655        (Int64, Int8) => primitive_to_primitive_dyn::<i64, i8>(array, to_type, options),
656        (Int64, Int16) => primitive_to_primitive_dyn::<i64, i16>(array, to_type, options),
657        (Int64, Int32) => primitive_to_primitive_dyn::<i64, i32>(array, to_type, options),
658        #[cfg(feature = "dtype-i128")]
659        (Int64, Int128) => primitive_to_primitive_dyn::<i64, i128>(array, to_type, options),
660        (Int64, Float32) => primitive_to_primitive_dyn::<i64, f32>(array, to_type, options),
661        (Int64, Float64) => primitive_to_primitive_dyn::<i64, f64>(array, to_type, as_options),
662        (Int64, Decimal(p, s)) => integer_to_decimal_dyn::<i64>(array, *p, *s),
663
664        #[cfg(feature = "dtype-i128")]
665        (Int128, UInt8) => primitive_to_primitive_dyn::<i128, u8>(array, to_type, options),
666        #[cfg(feature = "dtype-i128")]
667        (Int128, UInt16) => primitive_to_primitive_dyn::<i128, u16>(array, to_type, options),
668        #[cfg(feature = "dtype-i128")]
669        (Int128, UInt32) => primitive_to_primitive_dyn::<i128, u32>(array, to_type, options),
670        #[cfg(feature = "dtype-i128")]
671        (Int128, UInt64) => primitive_to_primitive_dyn::<i128, u64>(array, to_type, options),
672        #[cfg(feature = "dtype-i128")]
673        (Int128, Int8) => primitive_to_primitive_dyn::<i128, i8>(array, to_type, options),
674        #[cfg(feature = "dtype-i128")]
675        (Int128, Int16) => primitive_to_primitive_dyn::<i128, i16>(array, to_type, options),
676        #[cfg(feature = "dtype-i128")]
677        (Int128, Int32) => primitive_to_primitive_dyn::<i128, i32>(array, to_type, options),
678        #[cfg(feature = "dtype-i128")]
679        (Int128, Int64) => primitive_to_primitive_dyn::<i128, i64>(array, to_type, options),
680        #[cfg(feature = "dtype-i128")]
681        (Int128, Float32) => primitive_to_primitive_dyn::<i128, f32>(array, to_type, options),
682        #[cfg(feature = "dtype-i128")]
683        (Int128, Float64) => primitive_to_primitive_dyn::<i128, f64>(array, to_type, as_options),
684        #[cfg(feature = "dtype-i128")]
685        (Int128, Decimal(p, s)) => integer_to_decimal_dyn::<i128>(array, *p, *s),
686
687        (Float16, Float32) => {
688            let from = array.as_any().downcast_ref().unwrap();
689            Ok(f16_to_f32(from).boxed())
690        },
691
692        (Float32, UInt8) => primitive_to_primitive_dyn::<f32, u8>(array, to_type, options),
693        (Float32, UInt16) => primitive_to_primitive_dyn::<f32, u16>(array, to_type, options),
694        (Float32, UInt32) => primitive_to_primitive_dyn::<f32, u32>(array, to_type, options),
695        (Float32, UInt64) => primitive_to_primitive_dyn::<f32, u64>(array, to_type, options),
696        (Float32, Int8) => primitive_to_primitive_dyn::<f32, i8>(array, to_type, options),
697        (Float32, Int16) => primitive_to_primitive_dyn::<f32, i16>(array, to_type, options),
698        (Float32, Int32) => primitive_to_primitive_dyn::<f32, i32>(array, to_type, options),
699        (Float32, Int64) => primitive_to_primitive_dyn::<f32, i64>(array, to_type, options),
700        (Float32, Int128) => primitive_to_primitive_dyn::<f32, i128>(array, to_type, options),
701        (Float32, Float64) => primitive_to_primitive_dyn::<f32, f64>(array, to_type, as_options),
702        (Float32, Decimal(p, s)) => float_to_decimal_dyn::<f32>(array, *p, *s),
703
704        (Float64, UInt8) => primitive_to_primitive_dyn::<f64, u8>(array, to_type, options),
705        (Float64, UInt16) => primitive_to_primitive_dyn::<f64, u16>(array, to_type, options),
706        (Float64, UInt32) => primitive_to_primitive_dyn::<f64, u32>(array, to_type, options),
707        (Float64, UInt64) => primitive_to_primitive_dyn::<f64, u64>(array, to_type, options),
708        (Float64, Int8) => primitive_to_primitive_dyn::<f64, i8>(array, to_type, options),
709        (Float64, Int16) => primitive_to_primitive_dyn::<f64, i16>(array, to_type, options),
710        (Float64, Int32) => primitive_to_primitive_dyn::<f64, i32>(array, to_type, options),
711        (Float64, Int64) => primitive_to_primitive_dyn::<f64, i64>(array, to_type, options),
712        (Float64, Int128) => primitive_to_primitive_dyn::<f64, i128>(array, to_type, options),
713        (Float64, Float32) => primitive_to_primitive_dyn::<f64, f32>(array, to_type, options),
714        (Float64, Decimal(p, s)) => float_to_decimal_dyn::<f64>(array, *p, *s),
715
716        (Decimal(_, _), UInt8) => decimal_to_integer_dyn::<u8>(array),
717        (Decimal(_, _), UInt16) => decimal_to_integer_dyn::<u16>(array),
718        (Decimal(_, _), UInt32) => decimal_to_integer_dyn::<u32>(array),
719        (Decimal(_, _), UInt64) => decimal_to_integer_dyn::<u64>(array),
720        (Decimal(_, _), Int8) => decimal_to_integer_dyn::<i8>(array),
721        (Decimal(_, _), Int16) => decimal_to_integer_dyn::<i16>(array),
722        (Decimal(_, _), Int32) => decimal_to_integer_dyn::<i32>(array),
723        (Decimal(_, _), Int64) => decimal_to_integer_dyn::<i64>(array),
724        (Decimal(_, _), Int128) => decimal_to_integer_dyn::<i128>(array),
725        (Decimal(_, _), Float32) => decimal_to_float_dyn::<f32>(array),
726        (Decimal(_, _), Float64) => decimal_to_float_dyn::<f64>(array),
727        (Decimal(_, _), Decimal(to_p, to_s)) => decimal_to_decimal_dyn(array, *to_p, *to_s),
728        // end numeric casts
729
730        // temporal casts
731        (Int32, Date32) => primitive_to_same_primitive_dyn::<i32>(array, to_type),
732        (Int32, Time32(TimeUnit::Second)) => primitive_dyn!(array, int32_to_time32s),
733        (Int32, Time32(TimeUnit::Millisecond)) => primitive_dyn!(array, int32_to_time32ms),
734        // No support for microsecond/nanosecond with i32
735        (Date32, Int32) => primitive_to_same_primitive_dyn::<i32>(array, to_type),
736        (Date32, Int64) => primitive_to_primitive_dyn::<i32, i64>(array, to_type, options),
737        (Time32(_), Int32) => primitive_to_same_primitive_dyn::<i32>(array, to_type),
738        (Int64, Date64) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
739        // No support for second/milliseconds with i64
740        (Int64, Time64(TimeUnit::Microsecond)) => primitive_dyn!(array, int64_to_time64us),
741        (Int64, Time64(TimeUnit::Nanosecond)) => primitive_dyn!(array, int64_to_time64ns),
742
743        (Date64, Int32) => primitive_to_primitive_dyn::<i64, i32>(array, to_type, options),
744        (Date64, Int64) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
745        (Time64(_), Int64) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
746        (Date32, Date64) => primitive_dyn!(array, date32_to_date64),
747        (Date64, Date32) => primitive_dyn!(array, date64_to_date32),
748        (Time32(TimeUnit::Second), Time32(TimeUnit::Millisecond)) => {
749            primitive_dyn!(array, time32s_to_time32ms)
750        },
751        (Time32(TimeUnit::Millisecond), Time32(TimeUnit::Second)) => {
752            primitive_dyn!(array, time32ms_to_time32s)
753        },
754        (Time32(from_unit), Time64(to_unit)) => {
755            primitive_dyn!(array, time32_to_time64, *from_unit, *to_unit)
756        },
757        (Time64(TimeUnit::Microsecond), Time64(TimeUnit::Nanosecond)) => {
758            primitive_dyn!(array, time64us_to_time64ns)
759        },
760        (Time64(TimeUnit::Nanosecond), Time64(TimeUnit::Microsecond)) => {
761            primitive_dyn!(array, time64ns_to_time64us)
762        },
763        (Time64(from_unit), Time32(to_unit)) => {
764            primitive_dyn!(array, time64_to_time32, *from_unit, *to_unit)
765        },
766        (Timestamp(_, _), Int64) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
767        (Int64, Timestamp(_, _)) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
768        (Timestamp(from_unit, _), Timestamp(to_unit, tz)) => {
769            primitive_dyn!(array, timestamp_to_timestamp, *from_unit, *to_unit, tz)
770        },
771        (Timestamp(from_unit, _), Date32) => primitive_dyn!(array, timestamp_to_date32, *from_unit),
772        (Timestamp(from_unit, _), Date64) => primitive_dyn!(array, timestamp_to_date64, *from_unit),
773
774        (Int64, Duration(_)) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
775        (Duration(_), Int64) => primitive_to_same_primitive_dyn::<i64>(array, to_type),
776
777        // Not supported by Polars.
778        // (Interval(IntervalUnit::DayTime), Interval(IntervalUnit::MonthDayNano)) => {
779        //     primitive_dyn!(array, days_ms_to_months_days_ns)
780        // },
781        // (Interval(IntervalUnit::YearMonth), Interval(IntervalUnit::MonthDayNano)) => {
782        //     primitive_dyn!(array, months_to_months_days_ns)
783        // },
784        _ => polars_bail!(InvalidOperation:
785            "casting from {from_type:?} to {to_type:?} not supported",
786        ),
787    }
788}
789
790/// Attempts to encode an array into an `ArrayDictionary` with index
791/// type K and value (dictionary) type value_type
792///
793/// K is the key type
794fn cast_to_dictionary<K: DictionaryKey>(
795    array: &dyn Array,
796    dict_value_type: &ArrowDataType,
797    options: CastOptionsImpl,
798) -> PolarsResult<Box<dyn Array>> {
799    let array = cast(array, dict_value_type, options)?;
800    let array = array.as_ref();
801    match *dict_value_type {
802        ArrowDataType::Int8 => primitive_to_dictionary_dyn::<i8, K>(array),
803        ArrowDataType::Int16 => primitive_to_dictionary_dyn::<i16, K>(array),
804        ArrowDataType::Int32 => primitive_to_dictionary_dyn::<i32, K>(array),
805        ArrowDataType::Int64 => primitive_to_dictionary_dyn::<i64, K>(array),
806        ArrowDataType::UInt8 => primitive_to_dictionary_dyn::<u8, K>(array),
807        ArrowDataType::UInt16 => primitive_to_dictionary_dyn::<u16, K>(array),
808        ArrowDataType::UInt32 => primitive_to_dictionary_dyn::<u32, K>(array),
809        ArrowDataType::UInt64 => primitive_to_dictionary_dyn::<u64, K>(array),
810        ArrowDataType::BinaryView => {
811            binview_to_dictionary::<K>(array.as_any().downcast_ref().unwrap())
812                .map(|arr| arr.boxed())
813        },
814        ArrowDataType::Utf8View => {
815            utf8view_to_dictionary::<K>(array.as_any().downcast_ref().unwrap())
816                .map(|arr| arr.boxed())
817        },
818        ArrowDataType::LargeUtf8 => utf8_to_dictionary_dyn::<i64, K>(array),
819        ArrowDataType::LargeBinary => binary_to_dictionary_dyn::<i64, K>(array),
820        ArrowDataType::Time64(_) => primitive_to_dictionary_dyn::<i64, K>(array),
821        ArrowDataType::Timestamp(_, _) => primitive_to_dictionary_dyn::<i64, K>(array),
822        ArrowDataType::Date32 => primitive_to_dictionary_dyn::<i32, K>(array),
823        _ => polars_bail!(ComputeError:
824            "unsupported output type for dictionary packing: {dict_value_type:?}"
825        ),
826    }
827}
828
829fn from_to_binview(
830    array: &dyn Array,
831    from_type: &ArrowDataType,
832    to_type: &ArrowDataType,
833) -> PolarsResult<BinaryViewArray> {
834    use ArrowDataType::*;
835    let binview = match from_type {
836        UInt8 => primitive_to_binview_dyn::<u8>(array),
837        UInt16 => primitive_to_binview_dyn::<u16>(array),
838        UInt32 => primitive_to_binview_dyn::<u32>(array),
839        UInt64 => primitive_to_binview_dyn::<u64>(array),
840        Int8 => primitive_to_binview_dyn::<i8>(array),
841        Int16 => primitive_to_binview_dyn::<i16>(array),
842        Int32 => primitive_to_binview_dyn::<i32>(array),
843        Int64 => primitive_to_binview_dyn::<i64>(array),
844        Float32 => primitive_to_binview_dyn::<f32>(array),
845        Float64 => primitive_to_binview_dyn::<f64>(array),
846        Binary => binary_to_binview::<i32>(array.as_any().downcast_ref().unwrap()),
847        FixedSizeBinary(_) => fixed_size_binary_to_binview(array.as_any().downcast_ref().unwrap()),
848        LargeBinary => binary_to_binview::<i64>(array.as_any().downcast_ref().unwrap()),
849        _ => polars_bail!(InvalidOperation:
850            "casting from {from_type:?} to {to_type:?} not supported",
851        ),
852    };
853    Ok(binview)
854}