1mod any_value;
2pub mod cut;
3use polars_arrow::compute::concatenate::concatenate_validities;
4use polars_arrow::compute::utils::combine_validities_and;
5pub mod flatten;
6pub(crate) mod series;
7mod supertype;
8use std::borrow::Cow;
9use std::ops::{Deref, DerefMut};
10mod schema;
11
12pub use any_value::*;
13use flatten::*;
14use num_traits::{One, Zero};
15pub use polars_arrow;
16use polars_arrow::bitmap::Bitmap;
17pub use polars_arrow::legacy::utils::*;
18pub use polars_arrow::trusted_len::TrustMyLength;
19pub use rayon;
20use rayon::prelude::*;
21pub use schema::*;
22pub use series::*;
23pub use supertype::*;
24
25use crate::prelude::*;
26use crate::runtime::RAYON;
27
28#[repr(transparent)]
29pub struct Wrap<T>(pub T);
30
31impl<T> Deref for Wrap<T> {
32 type Target = T;
33 fn deref(&self) -> &Self::Target {
34 &self.0
35 }
36}
37
38#[inline(always)]
39pub fn _set_partition_size() -> usize {
40 RAYON.current_num_threads()
41}
42
43pub fn par_iter_bounded<T: Sync>(items: &[T]) -> rayon::iter::MinLen<rayon::slice::Iter<'_, T>> {
48 const TASKS_PER_THREAD: usize = 8;
49 let min_len = items
50 .len()
51 .div_ceil(_set_partition_size() * TASKS_PER_THREAD);
52 items.par_iter().with_min_len(min_len.max(1))
53}
54
55pub struct NoNull<T> {
62 inner: T,
63}
64
65impl<T> NoNull<T> {
66 pub fn new(inner: T) -> Self {
67 NoNull { inner }
68 }
69
70 pub fn into_inner(self) -> T {
71 self.inner
72 }
73}
74
75impl<T> Deref for NoNull<T> {
76 type Target = T;
77
78 fn deref(&self) -> &Self::Target {
79 &self.inner
80 }
81}
82
83impl<T> DerefMut for NoNull<T> {
84 fn deref_mut(&mut self) -> &mut Self::Target {
85 &mut self.inner
86 }
87}
88
89pub(crate) fn get_iter_capacity<T, I: Iterator<Item = T>>(iter: &I) -> usize {
90 match iter.size_hint() {
91 (_lower, Some(upper)) => upper,
92 (0, None) => 1024,
93 (lower, None) => lower,
94 }
95}
96
97#[doc(hidden)]
100pub fn _split_offsets(len: usize, n: usize) -> Vec<(usize, usize)> {
101 if n == 1 {
102 vec![(0, len)]
103 } else {
104 let chunk_size = len / n;
105
106 (0..n)
107 .map(|partition| {
108 let offset = partition * chunk_size;
109 let len = if partition == (n - 1) {
110 len - offset
111 } else {
112 chunk_size
113 };
114 (partition * chunk_size, len)
115 })
116 .collect_trusted()
117 }
118}
119
120#[allow(clippy::len_without_is_empty)]
121pub trait Container: Clone {
122 fn slice(&self, offset: i64, len: usize) -> Self;
123
124 fn split_at(&self, offset: i64) -> (Self, Self);
125
126 fn len(&self) -> usize;
127
128 fn iter_chunks(&self) -> impl Iterator<Item = Self>;
129
130 fn should_rechunk(&self) -> bool;
131
132 fn n_chunks(&self) -> usize;
133
134 fn chunk_lengths(&self) -> impl Iterator<Item = usize>;
135}
136
137impl Container for DataFrame {
138 fn slice(&self, offset: i64, len: usize) -> Self {
139 DataFrame::slice(self, offset, len)
140 }
141
142 fn split_at(&self, offset: i64) -> (Self, Self) {
143 DataFrame::split_at(self, offset)
144 }
145
146 fn len(&self) -> usize {
147 self.height()
148 }
149
150 fn iter_chunks(&self) -> impl Iterator<Item = Self> {
151 flatten_df_iter(self)
152 }
153
154 fn should_rechunk(&self) -> bool {
155 self.should_rechunk()
156 }
157
158 fn n_chunks(&self) -> usize {
159 DataFrame::first_col_n_chunks(self)
160 }
161
162 fn chunk_lengths(&self) -> impl Iterator<Item = usize> {
163 self.columns()[0].as_materialized_series().chunk_lengths()
165 }
166}
167
168impl<T: PolarsDataType> Container for ChunkedArray<T> {
169 fn slice(&self, offset: i64, len: usize) -> Self {
170 ChunkedArray::slice(self, offset, len)
171 }
172
173 fn split_at(&self, offset: i64) -> (Self, Self) {
174 ChunkedArray::split_at(self, offset)
175 }
176
177 fn len(&self) -> usize {
178 ChunkedArray::len(self)
179 }
180
181 fn iter_chunks(&self) -> impl Iterator<Item = Self> {
182 self.downcast_iter()
183 .map(|arr| Self::with_chunk(self.name().clone(), arr.clone()))
184 }
185
186 fn should_rechunk(&self) -> bool {
187 false
188 }
189
190 fn n_chunks(&self) -> usize {
191 self.chunks().len()
192 }
193
194 fn chunk_lengths(&self) -> impl Iterator<Item = usize> {
195 ChunkedArray::chunk_lengths(self)
196 }
197}
198
199impl Container for Series {
200 fn slice(&self, offset: i64, len: usize) -> Self {
201 self.0.slice(offset, len)
202 }
203
204 fn split_at(&self, offset: i64) -> (Self, Self) {
205 self.0.split_at(offset)
206 }
207
208 fn len(&self) -> usize {
209 self.0.len()
210 }
211
212 fn iter_chunks(&self) -> impl Iterator<Item = Self> {
213 (0..self.0.n_chunks()).map(|i| self.select_chunk(i))
214 }
215
216 fn should_rechunk(&self) -> bool {
217 false
218 }
219
220 fn n_chunks(&self) -> usize {
221 self.chunks().len()
222 }
223
224 fn chunk_lengths(&self) -> impl Iterator<Item = usize> {
225 self.0.chunk_lengths()
226 }
227}
228
229fn split_impl<C: Container>(container: &C, target: usize, chunk_size: usize) -> Vec<C> {
230 if target == 1 {
231 return vec![container.clone()];
232 }
233 let mut out = Vec::with_capacity(target);
234 let chunk_size = chunk_size as i64;
235
236 let (chunk, mut remainder) = container.split_at(chunk_size);
238 out.push(chunk);
239
240 for _ in 1..target - 1 {
242 let (a, b) = remainder.split_at(chunk_size);
243 out.push(a);
244 remainder = b
245 }
246 out.push(remainder);
248 out
249}
250
251pub fn split<C: Container>(container: &C, target: usize) -> Vec<C> {
253 let total_len = container.len();
254 if total_len == 0 {
255 return vec![container.clone()];
256 }
257
258 let chunk_size = std::cmp::max(total_len / target, 1);
259
260 if container.n_chunks() == target
261 && container
262 .chunk_lengths()
263 .all(|len| len.abs_diff(chunk_size) < 100)
264 && !container.should_rechunk()
266 {
267 return container.iter_chunks().collect();
268 }
269 split_impl(container, target, chunk_size)
270}
271
272pub fn split_and_flatten<C: Container>(container: &C, target: usize) -> Vec<C> {
275 let total_len = container.len();
276 if total_len == 0 {
277 return vec![container.clone()];
278 }
279
280 let chunk_size = std::cmp::max(total_len / target, 1);
281
282 if container.n_chunks() == target
283 && container
284 .chunk_lengths()
285 .all(|len| len.abs_diff(chunk_size) < 100)
286 && !container.should_rechunk()
288 {
289 return container.iter_chunks().collect();
290 }
291
292 if container.n_chunks() == 1 {
293 split_impl(container, target, chunk_size)
294 } else {
295 let mut out = Vec::with_capacity(target);
296 let chunks = container.iter_chunks();
297
298 'new_chunk: for mut chunk in chunks {
299 loop {
300 let h = chunk.len();
301 if h < chunk_size {
302 out.push(chunk);
304 continue 'new_chunk;
305 }
306
307 if ((h - chunk_size) as f64 / chunk_size as f64) < 0.3 {
309 out.push(chunk);
310 continue 'new_chunk;
311 }
312
313 let (a, b) = chunk.split_at(chunk_size as i64);
314 out.push(a);
315 chunk = b;
316 }
317 }
318 out
319 }
320}
321
322pub fn split_df_as_ref(df: &DataFrame, target: usize, strict: bool) -> Vec<DataFrame> {
328 if strict {
329 split(df, target)
330 } else {
331 split_and_flatten(df, target)
332 }
333}
334
335#[doc(hidden)]
336pub fn split_df(df: &mut DataFrame, target: usize, strict: bool) -> Vec<DataFrame> {
339 if target == 0 || df.height() == 0 {
340 return vec![df.clone()];
341 }
342 df.align_chunks_par();
344 split_df_as_ref(df, target, strict)
345}
346
347pub fn slice_slice<T>(vals: &[T], offset: i64, len: usize) -> &[T] {
348 let (raw_offset, slice_len) = slice_offsets(offset, len, vals.len());
349 &vals[raw_offset..raw_offset + slice_len]
350}
351
352#[inline]
353pub fn slice_offsets(offset: i64, length: usize, array_len: usize) -> (usize, usize) {
354 let signed_start_offset = if offset < 0 {
355 offset.saturating_add_unsigned(array_len as u64)
356 } else {
357 offset
358 };
359 let signed_stop_offset = signed_start_offset.saturating_add_unsigned(length as u64);
360
361 let signed_array_len: i64 = array_len
362 .try_into()
363 .expect("array length larger than i64::MAX");
364 let clamped_start_offset = signed_start_offset.clamp(0, signed_array_len);
365 let clamped_stop_offset = signed_stop_offset.clamp(0, signed_array_len);
366
367 let slice_start_idx = clamped_start_offset as usize;
368 let slice_len = (clamped_stop_offset - clamped_start_offset) as usize;
369 (slice_start_idx, slice_len)
370}
371
372#[macro_export]
374macro_rules! match_dtype_to_physical_apply_macro {
375 ($obj:expr, $macro:ident, $macro_string:ident, $macro_bool:ident $(, $opt_args:expr)*) => {{
376 match $obj {
377 DataType::String => $macro_string!($($opt_args)*),
378 DataType::Boolean => $macro_bool!($($opt_args)*),
379 #[cfg(feature = "dtype-u8")]
380 DataType::UInt8 => $macro!(u8 $(, $opt_args)*),
381 #[cfg(feature = "dtype-u16")]
382 DataType::UInt16 => $macro!(u16 $(, $opt_args)*),
383 DataType::UInt32 => $macro!(u32 $(, $opt_args)*),
384 DataType::UInt64 => $macro!(u64 $(, $opt_args)*),
385 #[cfg(feature = "dtype-i8")]
386 DataType::Int8 => $macro!(i8 $(, $opt_args)*),
387 #[cfg(feature = "dtype-i16")]
388 DataType::Int16 => $macro!(i16 $(, $opt_args)*),
389 DataType::Int32 => $macro!(i32 $(, $opt_args)*),
390 DataType::Int64 => $macro!(i64 $(, $opt_args)*),
391 #[cfg(feature = "dtype-i128")]
392 DataType::Int128 => $macro!(i128 $(, $opt_args)*),
393 #[cfg(feature = "dtype-f16")]
394 DataType::Float16 => $macro!(pf16 $(, $opt_args)*),
395 DataType::Float32 => $macro!(f32 $(, $opt_args)*),
396 DataType::Float64 => $macro!(f64 $(, $opt_args)*),
397 dt => panic!("not implemented for dtype {:?}", dt),
398 }
399 }};
400}
401
402#[macro_export]
404macro_rules! match_dtype_to_logical_apply_macro {
405 ($obj:expr, $macro:ident, $macro_string:ident, $macro_binary:ident, $macro_bool:ident $(, $opt_args:expr)*) => {{
406 match $obj {
407 DataType::String => $macro_string!($($opt_args)*),
408 DataType::Binary => $macro_binary!($($opt_args)*),
409 DataType::Boolean => $macro_bool!($($opt_args)*),
410 #[cfg(feature = "dtype-u8")]
411 DataType::UInt8 => $macro!(UInt8Type $(, $opt_args)*),
412 #[cfg(feature = "dtype-u16")]
413 DataType::UInt16 => $macro!(UInt16Type $(, $opt_args)*),
414 DataType::UInt32 => $macro!(UInt32Type $(, $opt_args)*),
415 DataType::UInt64 => $macro!(UInt64Type $(, $opt_args)*),
416 #[cfg(feature = "dtype-u128")]
417 DataType::UInt128 => $macro!(UInt128Type $(, $opt_args)*),
418 #[cfg(feature = "dtype-i8")]
419 DataType::Int8 => $macro!(Int8Type $(, $opt_args)*),
420 #[cfg(feature = "dtype-i16")]
421 DataType::Int16 => $macro!(Int16Type $(, $opt_args)*),
422 DataType::Int32 => $macro!(Int32Type $(, $opt_args)*),
423 DataType::Int64 => $macro!(Int64Type $(, $opt_args)*),
424 #[cfg(feature = "dtype-i128")]
425 DataType::Int128 => $macro!(Int128Type $(, $opt_args)*),
426 #[cfg(feature = "dtype-f16")]
427 DataType::Float16 => $macro!(Float16Type $(, $opt_args)*),
428 DataType::Float32 => $macro!(Float32Type $(, $opt_args)*),
429 DataType::Float64 => $macro!(Float64Type $(, $opt_args)*),
430 dt => panic!("not implemented for dtype {:?}", dt),
431 }
432 }};
433}
434
435#[macro_export]
437macro_rules! match_arrow_dtype_apply_macro_ca {
438 ($self:expr, $macro:ident, $macro_string:ident, $macro_bool:ident $(, $opt_args:expr)*) => {{
439 match $self.dtype() {
440 DataType::String => $macro_string!($self.str().unwrap() $(, $opt_args)*),
441 DataType::Boolean => $macro_bool!($self.bool().unwrap() $(, $opt_args)*),
442 #[cfg(feature = "dtype-u8")]
443 DataType::UInt8 => $macro!($self.u8().unwrap() $(, $opt_args)*),
444 #[cfg(feature = "dtype-u16")]
445 DataType::UInt16 => $macro!($self.u16().unwrap() $(, $opt_args)*),
446 DataType::UInt32 => $macro!($self.u32().unwrap() $(, $opt_args)*),
447 DataType::UInt64 => $macro!($self.u64().unwrap() $(, $opt_args)*),
448 #[cfg(feature = "dtype-u128")]
449 DataType::UInt128 => $macro!($self.u128().unwrap() $(, $opt_args)*),
450 #[cfg(feature = "dtype-i8")]
451 DataType::Int8 => $macro!($self.i8().unwrap() $(, $opt_args)*),
452 #[cfg(feature = "dtype-i16")]
453 DataType::Int16 => $macro!($self.i16().unwrap() $(, $opt_args)*),
454 DataType::Int32 => $macro!($self.i32().unwrap() $(, $opt_args)*),
455 DataType::Int64 => $macro!($self.i64().unwrap() $(, $opt_args)*),
456 #[cfg(feature = "dtype-i128")]
457 DataType::Int128 => $macro!($self.i128().unwrap() $(, $opt_args)*),
458 #[cfg(feature = "dtype-f16")]
459 DataType::Float16 => $macro!($self.f16().unwrap() $(, $opt_args)*),
460 DataType::Float32 => $macro!($self.f32().unwrap() $(, $opt_args)*),
461 DataType::Float64 => $macro!($self.f64().unwrap() $(, $opt_args)*),
462 dt => panic!("not implemented for dtype {:?}", dt),
463 }
464 }};
465}
466
467#[macro_export]
468macro_rules! with_match_physical_numeric_type {(
469 $dtype:expr, | $_:tt $T:ident | $($body:tt)*
470) => ({
471 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
472 #[cfg(feature = "dtype-f16")]
473 use polars_utils::float16::pf16;
474 use $crate::datatypes::DataType::*;
475 match $dtype {
476 #[cfg(feature = "dtype-i8")]
477 Int8 => __with_ty__! { i8 },
478 #[cfg(feature = "dtype-i16")]
479 Int16 => __with_ty__! { i16 },
480 Int32 => __with_ty__! { i32 },
481 Int64 => __with_ty__! { i64 },
482 #[cfg(feature = "dtype-i128")]
483 Int128 => __with_ty__! { i128 },
484 #[cfg(feature = "dtype-u8")]
485 UInt8 => __with_ty__! { u8 },
486 #[cfg(feature = "dtype-u16")]
487 UInt16 => __with_ty__! { u16 },
488 UInt32 => __with_ty__! { u32 },
489 UInt64 => __with_ty__! { u64 },
490 #[cfg(feature = "dtype-u128")]
491 UInt128 => __with_ty__! { u128 },
492 #[cfg(feature = "dtype-f16")]
493 Float16 => __with_ty__! { pf16 },
494 Float32 => __with_ty__! { f32 },
495 Float64 => __with_ty__! { f64 },
496 dt => panic!("not implemented for dtype {:?}", dt),
497 }
498})}
499
500#[macro_export]
501macro_rules! with_match_physical_integer_type {(
502 $dtype:expr, | $_:tt $T:ident | $($body:tt)*
503) => ({
504 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
505 #[cfg(feature = "dtype-f16")]
506 use polars_utils::float16::pf16;
507 use $crate::datatypes::DataType::*;
508 match $dtype {
509 #[cfg(feature = "dtype-i8")]
510 Int8 => __with_ty__! { i8 },
511 #[cfg(feature = "dtype-i16")]
512 Int16 => __with_ty__! { i16 },
513 Int32 => __with_ty__! { i32 },
514 Int64 => __with_ty__! { i64 },
515 #[cfg(feature = "dtype-i128")]
516 Int128 => __with_ty__! { i128 },
517 #[cfg(feature = "dtype-u8")]
518 UInt8 => __with_ty__! { u8 },
519 #[cfg(feature = "dtype-u16")]
520 UInt16 => __with_ty__! { u16 },
521 UInt32 => __with_ty__! { u32 },
522 UInt64 => __with_ty__! { u64 },
523 #[cfg(feature = "dtype-u128")]
524 UInt128 => __with_ty__! { u128 },
525 dt => panic!("not implemented for dtype {:?}", dt),
526 }
527})}
528
529#[macro_export]
530macro_rules! with_match_physical_float_type {(
531 $dtype:expr, | $_:tt $T:ident | $($body:tt)*
532) => ({
533 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
534 use polars_utils::float16::pf16;
535 use $crate::datatypes::DataType::*;
536 match $dtype {
537 #[cfg(feature = "dtype-f16")]
538 Float16 => __with_ty__! { pf16 },
539 Float32 => __with_ty__! { f32 },
540 Float64 => __with_ty__! { f64 },
541 dt => panic!("not implemented for dtype {:?}", dt),
542 }
543})}
544
545#[macro_export]
546macro_rules! with_match_physical_float_polars_type {(
547 $key_type:expr, | $_:tt $T:ident | $($body:tt)*
548) => ({
549 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
550 use $crate::datatypes::DataType::*;
551 match $key_type {
552 #[cfg(feature = "dtype-f16")]
553 Float16 => __with_ty__! { Float16Type },
554 Float32 => __with_ty__! { Float32Type },
555 Float64 => __with_ty__! { Float64Type },
556 dt => panic!("not implemented for dtype {:?}", dt),
557 }
558})}
559
560#[macro_export]
561macro_rules! with_match_physical_numeric_polars_type {(
562 $key_type:expr, | $_:tt $T:ident | $($body:tt)*
563) => ({
564 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
565 use $crate::datatypes::DataType::*;
566 match $key_type {
567 #[cfg(feature = "dtype-i8")]
568 Int8 => __with_ty__! { Int8Type },
569 #[cfg(feature = "dtype-i16")]
570 Int16 => __with_ty__! { Int16Type },
571 Int32 => __with_ty__! { Int32Type },
572 Int64 => __with_ty__! { Int64Type },
573 #[cfg(feature = "dtype-i128")]
574 Int128 => __with_ty__! { Int128Type },
575 #[cfg(feature = "dtype-u8")]
576 UInt8 => __with_ty__! { UInt8Type },
577 #[cfg(feature = "dtype-u16")]
578 UInt16 => __with_ty__! { UInt16Type },
579 UInt32 => __with_ty__! { UInt32Type },
580 UInt64 => __with_ty__! { UInt64Type },
581 #[cfg(feature = "dtype-u128")]
582 UInt128 => __with_ty__! { UInt128Type },
583 #[cfg(feature = "dtype-f16")]
584 Float16 => __with_ty__! { Float16Type },
585 Float32 => __with_ty__! { Float32Type },
586 Float64 => __with_ty__! { Float64Type },
587 dt => panic!("not implemented for dtype {:?}", dt),
588 }
589})}
590
591#[macro_export]
592macro_rules! with_match_physical_integer_polars_type {(
593 $key_type:expr, | $_:tt $T:ident | $($body:tt)*
594) => ({
595 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
596 use $crate::datatypes::DataType::*;
597 use $crate::datatypes::*;
598 match $key_type {
599 #[cfg(feature = "dtype-i8")]
600 Int8 => __with_ty__! { Int8Type },
601 #[cfg(feature = "dtype-i16")]
602 Int16 => __with_ty__! { Int16Type },
603 Int32 => __with_ty__! { Int32Type },
604 Int64 => __with_ty__! { Int64Type },
605 #[cfg(feature = "dtype-i128")]
606 Int128 => __with_ty__! { Int128Type },
607 #[cfg(feature = "dtype-u8")]
608 UInt8 => __with_ty__! { UInt8Type },
609 #[cfg(feature = "dtype-u16")]
610 UInt16 => __with_ty__! { UInt16Type },
611 UInt32 => __with_ty__! { UInt32Type },
612 UInt64 => __with_ty__! { UInt64Type },
613 #[cfg(feature = "dtype-u128")]
614 UInt128 => __with_ty__! { UInt128Type },
615 dt => panic!("not implemented for dtype {:?}", dt),
616 }
617})}
618
619#[macro_export]
620macro_rules! with_match_categorical_physical_type {(
621 $dtype:expr, | $_:tt $T:ident | $($body:tt)*
622) => ({
623 macro_rules! __with_ty__ {( $_ $T:ident ) => ( $($body)* )}
624 match $dtype {
625 CategoricalPhysical::U8 => __with_ty__! { Categorical8Type },
626 CategoricalPhysical::U16 => __with_ty__! { Categorical16Type },
627 CategoricalPhysical::U32 => __with_ty__! { Categorical32Type },
628 }
629})}
630
631#[macro_export]
634macro_rules! downcast_as_macro_arg_physical {
635 ($self:expr, $macro:ident $(, $opt_args:expr)*) => {{
636 match $self.dtype() {
637 #[cfg(feature = "dtype-u8")]
638 DataType::UInt8 => $macro!($self.u8().unwrap() $(, $opt_args)*),
639 #[cfg(feature = "dtype-u16")]
640 DataType::UInt16 => $macro!($self.u16().unwrap() $(, $opt_args)*),
641 DataType::UInt32 => $macro!($self.u32().unwrap() $(, $opt_args)*),
642 DataType::UInt64 => $macro!($self.u64().unwrap() $(, $opt_args)*),
643 #[cfg(feature = "dtype-u128")]
644 DataType::UInt128 => $macro!($self.u128().unwrap() $(, $opt_args)*),
645 #[cfg(feature = "dtype-i8")]
646 DataType::Int8 => $macro!($self.i8().unwrap() $(, $opt_args)*),
647 #[cfg(feature = "dtype-i16")]
648 DataType::Int16 => $macro!($self.i16().unwrap() $(, $opt_args)*),
649 DataType::Int32 => $macro!($self.i32().unwrap() $(, $opt_args)*),
650 DataType::Int64 => $macro!($self.i64().unwrap() $(, $opt_args)*),
651 #[cfg(feature = "dtype-i128")]
652 DataType::Int128 => $macro!($self.i128().unwrap() $(, $opt_args)*),
653 #[cfg(feature = "dtype-f16")]
654 DataType::Float16 => $macro!($self.f16().unwrap() $(, $opt_args)*),
655 DataType::Float32 => $macro!($self.f32().unwrap() $(, $opt_args)*),
656 DataType::Float64 => $macro!($self.f64().unwrap() $(, $opt_args)*),
657 dt => panic!("not implemented for {:?}", dt),
658 }
659 }};
660}
661
662#[macro_export]
665macro_rules! downcast_as_macro_arg_physical_mut {
666 ($self:expr, $macro:ident $(, $opt_args:expr)*) => {{
667 match $self.dtype().clone() {
669 #[cfg(feature = "dtype-u8")]
670 DataType::UInt8 => {
671 let ca: &mut UInt8Chunked = $self.as_mut();
672 $macro!(UInt8Type, ca $(, $opt_args)*)
673 },
674 #[cfg(feature = "dtype-u16")]
675 DataType::UInt16 => {
676 let ca: &mut UInt16Chunked = $self.as_mut();
677 $macro!(UInt16Type, ca $(, $opt_args)*)
678 },
679 DataType::UInt32 => {
680 let ca: &mut UInt32Chunked = $self.as_mut();
681 $macro!(UInt32Type, ca $(, $opt_args)*)
682 },
683 DataType::UInt64 => {
684 let ca: &mut UInt64Chunked = $self.as_mut();
685 $macro!(UInt64Type, ca $(, $opt_args)*)
686 },
687 #[cfg(feature = "dtype-u128")]
688 DataType::UInt128 => {
689 let ca: &mut UInt128Chunked = $self.as_mut();
690 $macro!(UInt128Type, ca $(, $opt_args)*)
691 },
692 #[cfg(feature = "dtype-i8")]
693 DataType::Int8 => {
694 let ca: &mut Int8Chunked = $self.as_mut();
695 $macro!(Int8Type, ca $(, $opt_args)*)
696 },
697 #[cfg(feature = "dtype-i16")]
698 DataType::Int16 => {
699 let ca: &mut Int16Chunked = $self.as_mut();
700 $macro!(Int16Type, ca $(, $opt_args)*)
701 },
702 DataType::Int32 => {
703 let ca: &mut Int32Chunked = $self.as_mut();
704 $macro!(Int32Type, ca $(, $opt_args)*)
705 },
706 DataType::Int64 => {
707 let ca: &mut Int64Chunked = $self.as_mut();
708 $macro!(Int64Type, ca $(, $opt_args)*)
709 },
710 #[cfg(feature = "dtype-i128")]
711 DataType::Int128 => {
712 let ca: &mut Int128Chunked = $self.as_mut();
713 $macro!(Int128Type, ca $(, $opt_args)*)
714 },
715 #[cfg(feature = "dtype-f16")]
716 DataType::Float16 => {
717 let ca: &mut Float16Chunked = $self.as_mut();
718 $macro!(Float16Type, ca $(, $opt_args)*)
719 },
720 DataType::Float32 => {
721 let ca: &mut Float32Chunked = $self.as_mut();
722 $macro!(Float32Type, ca $(, $opt_args)*)
723 },
724 DataType::Float64 => {
725 let ca: &mut Float64Chunked = $self.as_mut();
726 $macro!(Float64Type, ca $(, $opt_args)*)
727 },
728 dt => panic!("not implemented for {:?}", dt),
729 }
730 }};
731}
732
733#[macro_export]
734macro_rules! apply_method_all_arrow_series {
735 ($self:expr, $method:ident, $($args:expr),*) => {
736 match $self.dtype() {
737 DataType::Boolean => $self.bool().unwrap().$method($($args),*),
738 DataType::String => $self.str().unwrap().$method($($args),*),
739 #[cfg(feature = "dtype-u8")]
740 DataType::UInt8 => $self.u8().unwrap().$method($($args),*),
741 #[cfg(feature = "dtype-u16")]
742 DataType::UInt16 => $self.u16().unwrap().$method($($args),*),
743 DataType::UInt32 => $self.u32().unwrap().$method($($args),*),
744 DataType::UInt64 => $self.u64().unwrap().$method($($args),*),
745 #[cfg(feature = "dtype-u128")]
746 DataType::UInt128 => $self.u128().unwrap().$medthod($($args),*),
747 #[cfg(feature = "dtype-i8")]
748 DataType::Int8 => $self.i8().unwrap().$method($($args),*),
749 #[cfg(feature = "dtype-i16")]
750 DataType::Int16 => $self.i16().unwrap().$method($($args),*),
751 DataType::Int32 => $self.i32().unwrap().$method($($args),*),
752 DataType::Int64 => $self.i64().unwrap().$method($($args),*),
753 #[cfg(feature = "dtype-i128")]
754 DataType::Int128 => $self.i128().unwrap().$method($($args),*),
755 #[cfg(feature = "dtype-f16")]
756 DataType::Float16 => $self.f16().unwrap().$method($($args),*),
757 DataType::Float32 => $self.f32().unwrap().$method($($args),*),
758 DataType::Float64 => $self.f64().unwrap().$method($($args),*),
759 DataType::Time => $self.time().unwrap().$method($($args),*),
760 DataType::Date => $self.date().unwrap().$method($($args),*),
761 DataType::Datetime(_, _) => $self.datetime().unwrap().$method($($args),*),
762 DataType::List(_) => $self.list().unwrap().$method($($args),*),
763 DataType::Struct(_) => $self.struct_().unwrap().$method($($args),*),
764 dt => panic!("dtype {:?} not supported", dt)
765 }
766 }
767}
768
769#[macro_export]
770macro_rules! apply_method_physical_integer {
771 ($self:expr, $method:ident, $($args:expr),*) => {
772 match $self.dtype() {
773 #[cfg(feature = "dtype-u8")]
774 DataType::UInt8 => $self.u8().unwrap().$method($($args),*),
775 #[cfg(feature = "dtype-u16")]
776 DataType::UInt16 => $self.u16().unwrap().$method($($args),*),
777 DataType::UInt32 => $self.u32().unwrap().$method($($args),*),
778 DataType::UInt64 => $self.u64().unwrap().$method($($args),*),
779 #[cfg(feature = "dtype-u128")]
780 DataType::UInt128 => $self.u128().unwrap().$method($($args),*),
781 #[cfg(feature = "dtype-i8")]
782 DataType::Int8 => $self.i8().unwrap().$method($($args),*),
783 #[cfg(feature = "dtype-i16")]
784 DataType::Int16 => $self.i16().unwrap().$method($($args),*),
785 DataType::Int32 => $self.i32().unwrap().$method($($args),*),
786 DataType::Int64 => $self.i64().unwrap().$method($($args),*),
787 #[cfg(feature = "dtype-i128")]
788 DataType::Int128 => $self.i128().unwrap().$method($($args),*),
789 dt => panic!("not implemented for dtype {:?}", dt),
790 }
791 }
792}
793
794#[macro_export]
796macro_rules! apply_method_physical_numeric {
797 ($self:expr, $method:ident, $($args:expr),*) => {
798 match $self.dtype() {
799 #[cfg(feature = "dtype-f16")]
800 DataType::Float16 => $self.f16().unwrap().$method($($args),*),
801 DataType::Float32 => $self.f32().unwrap().$method($($args),*),
802 DataType::Float64 => $self.f64().unwrap().$method($($args),*),
803 _ => apply_method_physical_integer!($self, $method, $($args),*),
804 }
805 }
806}
807
808#[macro_export]
809macro_rules! df {
810 ($($col_name:expr => $slice:expr), + $(,)?) => {
811 $crate::prelude::DataFrame::new_infer_height(vec![
812 $($crate::prelude::Column::from(<$crate::prelude::Series as $crate::prelude::NamedFrom::<_, _>>::new($col_name.into(), $slice)),)+
813 ])
814 }
815}
816
817pub fn get_time_units(tu_l: &TimeUnit, tu_r: &TimeUnit) -> TimeUnit {
818 use crate::datatypes::time_unit::TimeUnit::*;
819 match (tu_l, tu_r) {
820 (Nanoseconds, Microseconds) => Microseconds,
821 (_, Milliseconds) => Milliseconds,
822 _ => *tu_l,
823 }
824}
825
826#[cold]
827#[inline(never)]
828fn width_mismatch(df1: &DataFrame, df2: &DataFrame) -> PolarsError {
829 let mut df1_extra = Vec::new();
830 let mut df2_extra = Vec::new();
831
832 let s1 = df1.schema();
833 let s2 = df2.schema();
834
835 s1.field_compare(s2, &mut df1_extra, &mut df2_extra);
836
837 let df1_extra = df1_extra
838 .into_iter()
839 .map(|(_, (n, _))| n.as_str())
840 .collect::<Vec<_>>()
841 .join(", ");
842 let df2_extra = df2_extra
843 .into_iter()
844 .map(|(_, (n, _))| n.as_str())
845 .collect::<Vec<_>>()
846 .join(", ");
847
848 polars_err!(
849 SchemaMismatch: r#"unable to vstack, dataframes have different widths ({} != {}).
850One dataframe has additional columns: [{df1_extra}].
851Other dataframe has additional columns: [{df2_extra}]."#,
852 df1.width(),
853 df2.width(),
854 )
855}
856
857pub fn accumulate_dataframes_vertical_unchecked<I>(dfs: I) -> DataFrame
860where
861 I: IntoIterator<Item = DataFrame>,
862{
863 let mut iter = dfs.into_iter();
864 let additional = iter.size_hint().0;
865 let mut acc_df = iter.next().unwrap();
866 acc_df.reserve_chunks(additional);
867
868 for df in iter {
869 if acc_df.width() != df.width() {
870 panic!("{}", width_mismatch(&acc_df, &df));
871 }
872
873 acc_df.vstack_mut_owned_unchecked(df);
874 }
875 acc_df
876}
877
878pub fn accumulate_dataframes_vertical<I>(dfs: I) -> PolarsResult<DataFrame>
882where
883 I: IntoIterator<Item = DataFrame>,
884{
885 let mut iter = dfs.into_iter();
886 let additional = iter.size_hint().0;
887 let mut acc_df = iter.next().unwrap();
888 acc_df.reserve_chunks(additional);
889 for df in iter {
890 if acc_df.width() != df.width() {
891 return Err(width_mismatch(&acc_df, &df));
892 }
893
894 acc_df.vstack_mut_owned(df)?;
895 }
896
897 Ok(acc_df)
898}
899
900pub fn concat_df<'a, I>(dfs: I) -> PolarsResult<DataFrame>
902where
903 I: IntoIterator<Item = &'a DataFrame>,
904{
905 let mut iter = dfs.into_iter();
906 let additional = iter.size_hint().0;
907 let mut acc_df = iter.next().unwrap().clone();
908 acc_df.reserve_chunks(additional);
909 for df in iter {
910 acc_df.vstack_mut(df)?;
911 }
912 Ok(acc_df)
913}
914
915pub fn concat_df_unchecked<'a, I>(dfs: I) -> DataFrame
917where
918 I: IntoIterator<Item = &'a DataFrame>,
919{
920 let mut iter = dfs.into_iter();
921 let additional = iter.size_hint().0;
922 let mut acc_df = iter.next().unwrap().clone();
923 acc_df.reserve_chunks(additional);
924 for df in iter {
925 acc_df.vstack_mut_unchecked(df);
926 }
927 acc_df
928}
929
930pub fn accumulate_dataframes_horizontal(dfs: Vec<DataFrame>) -> PolarsResult<DataFrame> {
931 let mut iter = dfs.into_iter();
932 let mut acc_df = iter.next().unwrap();
933 for df in iter {
934 acc_df.hstack_mut(df.columns())?;
935 }
936 Ok(acc_df)
937}
938
939pub fn align_chunks_binary<'a, T, B>(
943 left: &'a ChunkedArray<T>,
944 right: &'a ChunkedArray<B>,
945) -> (Cow<'a, ChunkedArray<T>>, Cow<'a, ChunkedArray<B>>)
946where
947 B: PolarsDataType,
948 T: PolarsDataType,
949{
950 let assert = || {
951 assert_eq!(
952 left.len(),
953 right.len(),
954 "expected arrays of the same length"
955 )
956 };
957 match (left.chunks.len(), right.chunks.len()) {
958 (1, 1) => (Cow::Borrowed(left), Cow::Borrowed(right)),
960 (a, b)
962 if a == b
963 && left
964 .chunk_lengths()
965 .zip(right.chunk_lengths())
966 .all(|(l, r)| l == r) =>
967 {
968 (Cow::Borrowed(left), Cow::Borrowed(right))
969 },
970 (_, 1) => {
971 assert();
972 (
973 Cow::Borrowed(left),
974 Cow::Owned(right.match_chunks(left.chunk_lengths())),
975 )
976 },
977 (1, _) => {
978 assert();
979 (
980 Cow::Owned(left.match_chunks(right.chunk_lengths())),
981 Cow::Borrowed(right),
982 )
983 },
984 (_, _) => {
985 assert();
986 let left = left.rechunk();
988 (
989 Cow::Owned(left.match_chunks(right.chunk_lengths())),
990 Cow::Borrowed(right),
991 )
992 },
993 }
994}
995
996pub fn align_chunks_binary_ca_series<'a, T>(
1000 left: &'a ChunkedArray<T>,
1001 right: &'a Series,
1002) -> (Cow<'a, ChunkedArray<T>>, Cow<'a, Series>)
1003where
1004 T: PolarsDataType,
1005{
1006 let assert = || {
1007 assert_eq!(
1008 left.len(),
1009 right.len(),
1010 "expected arrays of the same length"
1011 )
1012 };
1013 match (left.chunks.len(), right.chunks().len()) {
1014 (1, 1) => (Cow::Borrowed(left), Cow::Borrowed(right)),
1016 (a, b)
1018 if a == b
1019 && left
1020 .chunk_lengths()
1021 .zip(right.chunk_lengths())
1022 .all(|(l, r)| l == r) =>
1023 {
1024 assert();
1025 (Cow::Borrowed(left), Cow::Borrowed(right))
1026 },
1027 (_, 1) => (left.rechunk(), Cow::Borrowed(right)),
1028 (1, _) => (Cow::Borrowed(left), Cow::Owned(right.rechunk())),
1029 (_, _) => {
1030 assert();
1031 (left.rechunk(), Cow::Owned(right.rechunk()))
1032 },
1033 }
1034}
1035
1036#[cfg(feature = "performant")]
1037pub(crate) fn align_chunks_binary_owned_series(left: Series, right: Series) -> (Series, Series) {
1038 match (left.chunks().len(), right.chunks().len()) {
1039 (1, 1) => (left, right),
1040 (a, b)
1042 if a == b
1043 && left
1044 .chunk_lengths()
1045 .zip(right.chunk_lengths())
1046 .all(|(l, r)| l == r) =>
1047 {
1048 (left, right)
1049 },
1050 (_, 1) => (left.rechunk(), right),
1051 (1, _) => (left, right.rechunk()),
1052 (_, _) => (left.rechunk(), right.rechunk()),
1053 }
1054}
1055
1056pub(crate) fn align_chunks_binary_owned<T, B>(
1057 left: ChunkedArray<T>,
1058 right: ChunkedArray<B>,
1059) -> (ChunkedArray<T>, ChunkedArray<B>)
1060where
1061 B: PolarsDataType,
1062 T: PolarsDataType,
1063{
1064 match (left.chunks.len(), right.chunks.len()) {
1065 (1, 1) => (left, right),
1066 (a, b)
1068 if a == b
1069 && left
1070 .chunk_lengths()
1071 .zip(right.chunk_lengths())
1072 .all(|(l, r)| l == r) =>
1073 {
1074 (left, right)
1075 },
1076 (_, 1) => (left.rechunk().into_owned(), right),
1077 (1, _) => (left, right.rechunk().into_owned()),
1078 (_, _) => (left.rechunk().into_owned(), right.rechunk().into_owned()),
1079 }
1080}
1081
1082#[allow(clippy::type_complexity)]
1085pub fn align_chunks_ternary<'a, A, B, C>(
1086 a: &'a ChunkedArray<A>,
1087 b: &'a ChunkedArray<B>,
1088 c: &'a ChunkedArray<C>,
1089) -> (
1090 Cow<'a, ChunkedArray<A>>,
1091 Cow<'a, ChunkedArray<B>>,
1092 Cow<'a, ChunkedArray<C>>,
1093)
1094where
1095 A: PolarsDataType,
1096 B: PolarsDataType,
1097 C: PolarsDataType,
1098{
1099 if a.chunks.len() == 1 && b.chunks.len() == 1 && c.chunks.len() == 1 {
1100 return (Cow::Borrowed(a), Cow::Borrowed(b), Cow::Borrowed(c));
1101 }
1102
1103 assert!(
1104 a.len() == b.len() && b.len() == c.len(),
1105 "expected arrays of the same length"
1106 );
1107
1108 match (a.chunks.len(), b.chunks.len(), c.chunks.len()) {
1109 (_, 1, 1) => (
1110 Cow::Borrowed(a),
1111 Cow::Owned(b.match_chunks(a.chunk_lengths())),
1112 Cow::Owned(c.match_chunks(a.chunk_lengths())),
1113 ),
1114 (1, 1, _) => (
1115 Cow::Owned(a.match_chunks(c.chunk_lengths())),
1116 Cow::Owned(b.match_chunks(c.chunk_lengths())),
1117 Cow::Borrowed(c),
1118 ),
1119 (1, _, 1) => (
1120 Cow::Owned(a.match_chunks(b.chunk_lengths())),
1121 Cow::Borrowed(b),
1122 Cow::Owned(c.match_chunks(b.chunk_lengths())),
1123 ),
1124 (1, _, _) => {
1125 let b = b.rechunk();
1126 (
1127 Cow::Owned(a.match_chunks(c.chunk_lengths())),
1128 Cow::Owned(b.match_chunks(c.chunk_lengths())),
1129 Cow::Borrowed(c),
1130 )
1131 },
1132 (_, 1, _) => {
1133 let a = a.rechunk();
1134 (
1135 Cow::Owned(a.match_chunks(c.chunk_lengths())),
1136 Cow::Owned(b.match_chunks(c.chunk_lengths())),
1137 Cow::Borrowed(c),
1138 )
1139 },
1140 (_, _, 1) => {
1141 let b = b.rechunk();
1142 (
1143 Cow::Borrowed(a),
1144 Cow::Owned(b.match_chunks(a.chunk_lengths())),
1145 Cow::Owned(c.match_chunks(a.chunk_lengths())),
1146 )
1147 },
1148 (len_a, len_b, len_c)
1149 if len_a == len_b
1150 && len_b == len_c
1151 && a.chunk_lengths()
1152 .zip(b.chunk_lengths())
1153 .zip(c.chunk_lengths())
1154 .all(|((a, b), c)| a == b && b == c) =>
1155 {
1156 (Cow::Borrowed(a), Cow::Borrowed(b), Cow::Borrowed(c))
1157 },
1158 _ => {
1159 let a = a.rechunk();
1161 let b = b.rechunk();
1162 (
1163 Cow::Owned(a.match_chunks(c.chunk_lengths())),
1164 Cow::Owned(b.match_chunks(c.chunk_lengths())),
1165 Cow::Borrowed(c),
1166 )
1167 },
1168 }
1169}
1170
1171pub fn binary_concatenate_validities<'a, T, B>(
1172 left: &'a ChunkedArray<T>,
1173 right: &'a ChunkedArray<B>,
1174) -> Option<Bitmap>
1175where
1176 B: PolarsDataType,
1177 T: PolarsDataType,
1178{
1179 let (left, right) = align_chunks_binary(left, right);
1180 let left_validity = concatenate_validities(left.chunks());
1181 let right_validity = concatenate_validities(right.chunks());
1182 combine_validities_and(left_validity.as_ref(), right_validity.as_ref())
1183}
1184
1185pub trait IntoVec<T> {
1187 fn into_vec(self) -> Vec<T>;
1188}
1189
1190impl<I, S> IntoVec<PlSmallStr> for I
1191where
1192 I: IntoIterator<Item = S>,
1193 S: Into<PlSmallStr>,
1194{
1195 fn into_vec(self) -> Vec<PlSmallStr> {
1196 self.into_iter().map(|s| s.into()).collect()
1197 }
1198}
1199
1200#[inline]
1205pub(crate) fn index_to_chunked_index<
1206 I: Iterator<Item = Idx>,
1207 Idx: PartialOrd + std::ops::AddAssign + std::ops::SubAssign + Zero + One,
1208>(
1209 chunk_lens: I,
1210 index: Idx,
1211) -> (Idx, Idx) {
1212 let mut index_remainder = index;
1213 let mut current_chunk_idx = Zero::zero();
1214
1215 for chunk_len in chunk_lens {
1216 if chunk_len > index_remainder {
1217 break;
1218 } else {
1219 index_remainder -= chunk_len;
1220 current_chunk_idx += One::one();
1221 }
1222 }
1223 (current_chunk_idx, index_remainder)
1224}
1225
1226pub(crate) fn index_to_chunked_index_rev<
1227 I: Iterator<Item = Idx>,
1228 Idx: PartialOrd
1229 + std::ops::AddAssign
1230 + std::ops::SubAssign
1231 + std::ops::Sub<Output = Idx>
1232 + Zero
1233 + One
1234 + Copy
1235 + std::fmt::Debug,
1236>(
1237 chunk_lens_rev: I,
1238 index_from_back: Idx,
1239 total_chunks: Idx,
1240) -> (Idx, Idx) {
1241 debug_assert!(index_from_back > Zero::zero(), "at least -1");
1242 let mut index_remainder = index_from_back;
1243 let mut current_chunk_idx = One::one();
1244 let mut current_chunk_len = Zero::zero();
1245
1246 for chunk_len in chunk_lens_rev {
1247 current_chunk_len = chunk_len;
1248 if chunk_len >= index_remainder {
1249 break;
1250 } else {
1251 index_remainder -= chunk_len;
1252 current_chunk_idx += One::one();
1253 }
1254 }
1255 (
1256 total_chunks - current_chunk_idx,
1257 current_chunk_len - index_remainder,
1258 )
1259}
1260
1261pub fn first_null<'a, I>(iter: I) -> Option<usize>
1262where
1263 I: Iterator<Item = &'a dyn Array>,
1264{
1265 let mut offset = 0;
1266 for arr in iter {
1267 if let Some(mask) = arr.validity() {
1268 let len_mask = mask.len();
1269 let n = mask.leading_ones();
1270 if n < len_mask {
1271 return Some(offset + n);
1272 }
1273 offset += len_mask
1274 } else {
1275 offset += arr.len();
1276 }
1277 }
1278 None
1279}
1280
1281pub fn first_non_null<'a, I>(iter: I) -> Option<usize>
1282where
1283 I: Iterator<Item = &'a dyn Array>,
1284{
1285 let mut offset = 0;
1286 for arr in iter {
1287 if let Some(mask) = arr.validity() {
1288 let len_mask = mask.len();
1289 let n = mask.leading_zeros();
1290 if n < len_mask {
1291 return Some(offset + n);
1292 }
1293 offset += len_mask
1294 } else if !arr.is_empty() {
1295 return Some(offset);
1296 }
1297 }
1298 None
1299}
1300
1301pub fn last_non_null<'a, I>(iter: I, len: usize) -> Option<usize>
1302where
1303 I: DoubleEndedIterator<Item = &'a dyn Array>,
1304{
1305 if len == 0 {
1306 return None;
1307 }
1308 let mut offset = 0;
1309 for arr in iter.rev() {
1310 if let Some(mask) = arr.validity() {
1311 let len_mask = mask.len();
1312 let n = mask.trailing_zeros();
1313 if n < len_mask {
1314 return Some(len - offset - n - 1);
1315 }
1316 offset += len_mask;
1317 } else if !arr.is_empty() {
1318 return Some(len - offset - 1);
1319 }
1320 }
1321 None
1322}
1323
1324pub fn coalesce_nulls_columns(a: &Column, b: &Column) -> (Column, Column) {
1325 if a.null_count() > 0 || b.null_count() > 0 {
1326 let mut a = a.as_materialized_series().rechunk();
1327 let mut b = b.as_materialized_series().rechunk();
1328 for (arr_a, arr_b) in unsafe { a.chunks_mut().iter_mut().zip(b.chunks_mut()) } {
1329 let validity = match (arr_a.validity(), arr_b.validity()) {
1330 (None, Some(b)) => Some(b.clone()),
1331 (Some(a), Some(b)) => Some(a & b),
1332 (Some(a), None) => Some(a.clone()),
1333 (None, None) => None,
1334 };
1335 *arr_a = arr_a.with_validity(validity.clone());
1336 *arr_b = arr_b.with_validity(validity);
1337 }
1338 a.compute_len();
1339 b.compute_len();
1340 (a.into(), b.into())
1341 } else {
1342 (a.clone(), b.clone())
1343 }
1344}
1345
1346#[cfg(test)]
1347mod test {
1348 use super::*;
1349
1350 #[test]
1351 fn test_split() {
1352 let ca: Int32Chunked = (0..10).collect_ca("a".into());
1353
1354 let out = split(&ca, 3);
1355 assert_eq!(out[0].len(), 3);
1356 assert_eq!(out[1].len(), 3);
1357 assert_eq!(out[2].len(), 4);
1358 }
1359
1360 #[test]
1361 fn test_align_chunks() -> PolarsResult<()> {
1362 let a = Int32Chunked::new(PlSmallStr::EMPTY, &[1, 2, 3, 4]);
1363 let mut b = Int32Chunked::new(PlSmallStr::EMPTY, &[1]);
1364 let b2 = Int32Chunked::new(PlSmallStr::EMPTY, &[2, 3, 4]);
1365
1366 b.append(&b2)?;
1367 let (a, b) = align_chunks_binary(&a, &b);
1368 assert_eq!(
1369 a.chunk_lengths().collect::<Vec<_>>(),
1370 b.chunk_lengths().collect::<Vec<_>>()
1371 );
1372
1373 let a = Int32Chunked::new(PlSmallStr::EMPTY, &[1, 2, 3, 4]);
1374 let mut b = Int32Chunked::new(PlSmallStr::EMPTY, &[1]);
1375 let b1 = b.clone();
1376 b.append(&b1)?;
1377 b.append(&b1)?;
1378 b.append(&b1)?;
1379 let (a, b) = align_chunks_binary(&a, &b);
1380 assert_eq!(
1381 a.chunk_lengths().collect::<Vec<_>>(),
1382 b.chunk_lengths().collect::<Vec<_>>()
1383 );
1384
1385 Ok(())
1386 }
1387}