Skip to main content

polars_core/chunked_array/comparison/
mod.rs

1mod scalar;
2
3#[cfg(feature = "dtype-categorical")]
4mod categorical;
5
6use std::ops::{BitAnd, BitOr, Not};
7
8use arrow::array::BooleanArray;
9use arrow::bitmap::{Bitmap, BitmapBuilder};
10use num_traits::{NumCast, ToPrimitive};
11use polars_compute::comparisons::{TotalEqKernel, TotalOrdKernel};
12
13use crate::prelude::*;
14use crate::series::IsSorted;
15use crate::series::implementations::null::NullChunked;
16
17impl<T> ChunkCompareEq<&ChunkedArray<T>> for ChunkedArray<T>
18where
19    T: PolarsNumericType,
20    T::Array: TotalOrdKernel<Scalar = T::Native> + TotalEqKernel<Scalar = T::Native>,
21{
22    type Item = BooleanChunked;
23
24    fn equal(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
25        // Broadcast.
26        match (self.len(), rhs.len()) {
27            (_, 1) => {
28                if let Some(value) = rhs.get(0) {
29                    self.equal(value)
30                } else {
31                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
32                }
33            },
34            (1, _) => {
35                if let Some(value) = self.get(0) {
36                    rhs.equal(value)
37                } else {
38                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
39                }
40            },
41            _ => arity::binary_mut_values(
42                self,
43                rhs,
44                |a, b| a.tot_eq_kernel(b).into(),
45                PlSmallStr::EMPTY,
46            ),
47        }
48    }
49
50    fn equal_missing(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
51        // Broadcast.
52        match (self.len(), rhs.len()) {
53            (_, 1) => {
54                if let Some(value) = rhs.get(0) {
55                    self.equal_missing(value)
56                } else {
57                    self.is_null()
58                }
59            },
60            (1, _) => {
61                if let Some(value) = self.get(0) {
62                    rhs.equal_missing(value)
63                } else {
64                    rhs.is_null()
65                }
66            },
67            _ => arity::binary_mut_with_options(
68                self,
69                rhs,
70                |a, b| a.tot_eq_missing_kernel(b).into(),
71                PlSmallStr::EMPTY,
72            ),
73        }
74    }
75
76    fn not_equal(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
77        // Broadcast.
78        match (self.len(), rhs.len()) {
79            (_, 1) => {
80                if let Some(value) = rhs.get(0) {
81                    self.not_equal(value)
82                } else {
83                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
84                }
85            },
86            (1, _) => {
87                if let Some(value) = self.get(0) {
88                    rhs.not_equal(value)
89                } else {
90                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
91                }
92            },
93            _ => arity::binary_mut_values(
94                self,
95                rhs,
96                |a, b| a.tot_ne_kernel(b).into(),
97                PlSmallStr::EMPTY,
98            ),
99        }
100    }
101
102    fn not_equal_missing(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
103        // Broadcast.
104        match (self.len(), rhs.len()) {
105            (_, 1) => {
106                if let Some(value) = rhs.get(0) {
107                    self.not_equal_missing(value)
108                } else {
109                    self.is_not_null()
110                }
111            },
112            (1, _) => {
113                if let Some(value) = self.get(0) {
114                    rhs.not_equal_missing(value)
115                } else {
116                    rhs.is_not_null()
117                }
118            },
119            _ => arity::binary_mut_with_options(
120                self,
121                rhs,
122                |a, b| a.tot_ne_missing_kernel(b).into(),
123                PlSmallStr::EMPTY,
124            ),
125        }
126    }
127}
128
129impl<T> ChunkCompareIneq<&ChunkedArray<T>> for ChunkedArray<T>
130where
131    T: PolarsNumericType,
132    T::Array: TotalOrdKernel<Scalar = T::Native> + TotalEqKernel<Scalar = T::Native>,
133{
134    type Item = BooleanChunked;
135
136    fn lt(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
137        // Broadcast.
138        match (self.len(), rhs.len()) {
139            (_, 1) => {
140                if let Some(value) = rhs.get(0) {
141                    self.lt(value)
142                } else {
143                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
144                }
145            },
146            (1, _) => {
147                if let Some(value) = self.get(0) {
148                    rhs.gt(value)
149                } else {
150                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
151                }
152            },
153            _ => arity::binary_mut_values(
154                self,
155                rhs,
156                |a, b| a.tot_lt_kernel(b).into(),
157                PlSmallStr::EMPTY,
158            ),
159        }
160    }
161
162    fn lt_eq(&self, rhs: &ChunkedArray<T>) -> BooleanChunked {
163        // Broadcast.
164        match (self.len(), rhs.len()) {
165            (_, 1) => {
166                if let Some(value) = rhs.get(0) {
167                    self.lt_eq(value)
168                } else {
169                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
170                }
171            },
172            (1, _) => {
173                if let Some(value) = self.get(0) {
174                    rhs.gt_eq(value)
175                } else {
176                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
177                }
178            },
179            _ => arity::binary_mut_values(
180                self,
181                rhs,
182                |a, b| a.tot_le_kernel(b).into(),
183                PlSmallStr::EMPTY,
184            ),
185        }
186    }
187
188    fn gt(&self, rhs: &Self) -> BooleanChunked {
189        rhs.lt(self)
190    }
191
192    fn gt_eq(&self, rhs: &Self) -> BooleanChunked {
193        rhs.lt_eq(self)
194    }
195}
196
197impl ChunkCompareEq<&NullChunked> for NullChunked {
198    type Item = BooleanChunked;
199
200    fn equal(&self, rhs: &NullChunked) -> Self::Item {
201        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
202    }
203
204    fn equal_missing(&self, rhs: &NullChunked) -> Self::Item {
205        BooleanChunked::full(self.name().clone(), true, get_broadcast_length(self, rhs))
206    }
207
208    fn not_equal(&self, rhs: &NullChunked) -> Self::Item {
209        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
210    }
211
212    fn not_equal_missing(&self, rhs: &NullChunked) -> Self::Item {
213        BooleanChunked::full(self.name().clone(), false, get_broadcast_length(self, rhs))
214    }
215}
216
217impl ChunkCompareIneq<&NullChunked> for NullChunked {
218    type Item = BooleanChunked;
219
220    fn gt(&self, rhs: &NullChunked) -> Self::Item {
221        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
222    }
223
224    fn gt_eq(&self, rhs: &NullChunked) -> Self::Item {
225        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
226    }
227
228    fn lt(&self, rhs: &NullChunked) -> Self::Item {
229        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
230    }
231
232    fn lt_eq(&self, rhs: &NullChunked) -> Self::Item {
233        BooleanChunked::full_null(self.name().clone(), get_broadcast_length(self, rhs))
234    }
235}
236
237#[inline]
238fn get_broadcast_length(lhs: &NullChunked, rhs: &NullChunked) -> usize {
239    match (lhs.len(), rhs.len()) {
240        (1, len_r) => len_r,
241        (len_l, 1) => len_l,
242        (len_l, len_r) if len_l == len_r => len_l,
243        _ => panic!("Cannot compare two series of different lengths."),
244    }
245}
246
247impl ChunkCompareEq<&BooleanChunked> for BooleanChunked {
248    type Item = BooleanChunked;
249
250    fn equal(&self, rhs: &BooleanChunked) -> BooleanChunked {
251        // Broadcast.
252        match (self.len(), rhs.len()) {
253            (_, 1) => {
254                if let Some(value) = rhs.get(0) {
255                    arity::unary_mut_values(self, |arr| arr.tot_eq_kernel_broadcast(&value).into())
256                } else {
257                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
258                }
259            },
260            (1, _) => {
261                if let Some(value) = self.get(0) {
262                    arity::unary_mut_values(rhs, |arr| arr.tot_eq_kernel_broadcast(&value).into())
263                } else {
264                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
265                }
266            },
267            _ => arity::binary_mut_values(
268                self,
269                rhs,
270                |a, b| a.tot_eq_kernel(b).into(),
271                PlSmallStr::EMPTY,
272            ),
273        }
274    }
275
276    fn equal_missing(&self, rhs: &BooleanChunked) -> BooleanChunked {
277        // Broadcast.
278        match (self.len(), rhs.len()) {
279            (_, 1) => {
280                if let Some(value) = rhs.get(0) {
281                    arity::unary_mut_with_options(self, |arr| {
282                        arr.tot_eq_missing_kernel_broadcast(&value).into()
283                    })
284                } else {
285                    self.is_null()
286                }
287            },
288            (1, _) => {
289                if let Some(value) = self.get(0) {
290                    arity::unary_mut_with_options(rhs, |arr| {
291                        arr.tot_eq_missing_kernel_broadcast(&value).into()
292                    })
293                } else {
294                    rhs.is_null()
295                }
296            },
297            _ => arity::binary_mut_with_options(
298                self,
299                rhs,
300                |a, b| a.tot_eq_missing_kernel(b).into(),
301                PlSmallStr::EMPTY,
302            ),
303        }
304    }
305
306    fn not_equal(&self, rhs: &BooleanChunked) -> BooleanChunked {
307        // Broadcast.
308        match (self.len(), rhs.len()) {
309            (_, 1) => {
310                if let Some(value) = rhs.get(0) {
311                    arity::unary_mut_values(self, |arr| arr.tot_ne_kernel_broadcast(&value).into())
312                } else {
313                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
314                }
315            },
316            (1, _) => {
317                if let Some(value) = self.get(0) {
318                    arity::unary_mut_values(rhs, |arr| arr.tot_ne_kernel_broadcast(&value).into())
319                } else {
320                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
321                }
322            },
323            _ => arity::binary_mut_values(
324                self,
325                rhs,
326                |a, b| a.tot_ne_kernel(b).into(),
327                PlSmallStr::EMPTY,
328            ),
329        }
330    }
331
332    fn not_equal_missing(&self, rhs: &BooleanChunked) -> BooleanChunked {
333        // Broadcast.
334        match (self.len(), rhs.len()) {
335            (_, 1) => {
336                if let Some(value) = rhs.get(0) {
337                    arity::unary_mut_with_options(self, |arr| {
338                        arr.tot_ne_missing_kernel_broadcast(&value).into()
339                    })
340                } else {
341                    self.is_not_null()
342                }
343            },
344            (1, _) => {
345                if let Some(value) = self.get(0) {
346                    arity::unary_mut_with_options(rhs, |arr| {
347                        arr.tot_ne_missing_kernel_broadcast(&value).into()
348                    })
349                } else {
350                    rhs.is_not_null()
351                }
352            },
353            _ => arity::binary_mut_with_options(
354                self,
355                rhs,
356                |a, b| a.tot_ne_missing_kernel(b).into(),
357                PlSmallStr::EMPTY,
358            ),
359        }
360    }
361}
362
363impl ChunkCompareIneq<&BooleanChunked> for BooleanChunked {
364    type Item = BooleanChunked;
365
366    fn lt(&self, rhs: &BooleanChunked) -> BooleanChunked {
367        // Broadcast.
368        match (self.len(), rhs.len()) {
369            (_, 1) => {
370                if let Some(value) = rhs.get(0) {
371                    arity::unary_mut_values(self, |arr| arr.tot_lt_kernel_broadcast(&value).into())
372                } else {
373                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
374                }
375            },
376            (1, _) => {
377                if let Some(value) = self.get(0) {
378                    arity::unary_mut_values(rhs, |arr| arr.tot_gt_kernel_broadcast(&value).into())
379                } else {
380                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
381                }
382            },
383            _ => arity::binary_mut_values(
384                self,
385                rhs,
386                |a, b| a.tot_lt_kernel(b).into(),
387                PlSmallStr::EMPTY,
388            ),
389        }
390    }
391
392    fn lt_eq(&self, rhs: &BooleanChunked) -> BooleanChunked {
393        // Broadcast.
394        match (self.len(), rhs.len()) {
395            (_, 1) => {
396                if let Some(value) = rhs.get(0) {
397                    arity::unary_mut_values(self, |arr| arr.tot_le_kernel_broadcast(&value).into())
398                } else {
399                    BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
400                }
401            },
402            (1, _) => {
403                if let Some(value) = self.get(0) {
404                    arity::unary_mut_values(rhs, |arr| arr.tot_ge_kernel_broadcast(&value).into())
405                } else {
406                    BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
407                }
408            },
409            _ => arity::binary_mut_values(
410                self,
411                rhs,
412                |a, b| a.tot_le_kernel(b).into(),
413                PlSmallStr::EMPTY,
414            ),
415        }
416    }
417
418    fn gt(&self, rhs: &Self) -> BooleanChunked {
419        rhs.lt(self)
420    }
421
422    fn gt_eq(&self, rhs: &Self) -> BooleanChunked {
423        rhs.lt_eq(self)
424    }
425}
426
427impl ChunkCompareEq<&StringChunked> for StringChunked {
428    type Item = BooleanChunked;
429
430    fn equal(&self, rhs: &StringChunked) -> BooleanChunked {
431        self.as_binary().equal(&rhs.as_binary())
432    }
433
434    fn equal_missing(&self, rhs: &StringChunked) -> BooleanChunked {
435        self.as_binary().equal_missing(&rhs.as_binary())
436    }
437
438    fn not_equal(&self, rhs: &StringChunked) -> BooleanChunked {
439        self.as_binary().not_equal(&rhs.as_binary())
440    }
441
442    fn not_equal_missing(&self, rhs: &StringChunked) -> BooleanChunked {
443        self.as_binary().not_equal_missing(&rhs.as_binary())
444    }
445}
446
447impl ChunkCompareIneq<&StringChunked> for StringChunked {
448    type Item = BooleanChunked;
449
450    fn gt(&self, rhs: &StringChunked) -> BooleanChunked {
451        self.as_binary().gt(&rhs.as_binary())
452    }
453
454    fn gt_eq(&self, rhs: &StringChunked) -> BooleanChunked {
455        self.as_binary().gt_eq(&rhs.as_binary())
456    }
457
458    fn lt(&self, rhs: &StringChunked) -> BooleanChunked {
459        self.as_binary().lt(&rhs.as_binary())
460    }
461
462    fn lt_eq(&self, rhs: &StringChunked) -> BooleanChunked {
463        self.as_binary().lt_eq(&rhs.as_binary())
464    }
465}
466
467macro_rules! binary_eq_ineq_impl {
468    ($($ca:ident),+) => {
469        $(
470        impl ChunkCompareEq<&$ca> for $ca {
471            type Item = BooleanChunked;
472
473            fn equal(&self, rhs: &$ca) -> BooleanChunked {
474                // Broadcast.
475                match (self.len(), rhs.len()) {
476                    (_, 1) => {
477                        if let Some(value) = rhs.get(0) {
478                            self.equal(value)
479                        } else {
480                            BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
481                        }
482                    },
483                    (1, _) => {
484                        if let Some(value) = self.get(0) {
485                            rhs.equal(value)
486                        } else {
487                            BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
488                        }
489                    },
490                    _ => arity::binary_mut_values(
491                        self,
492                        rhs,
493                        |a, b| a.tot_eq_kernel(b).into(),
494                        PlSmallStr::EMPTY,
495                    ),
496                }
497            }
498
499            fn equal_missing(&self, rhs: &$ca) -> BooleanChunked {
500                // Broadcast.
501                match (self.len(), rhs.len()) {
502                    (_, 1) => {
503                        if let Some(value) = rhs.get(0) {
504                            self.equal_missing(value)
505                        } else {
506                            self.is_null()
507                        }
508                    },
509                    (1, _) => {
510                        if let Some(value) = self.get(0) {
511                            rhs.equal_missing(value)
512                        } else {
513                            rhs.is_null()
514                        }
515                    },
516                    _ => arity::binary_mut_with_options(
517                        self,
518                        rhs,
519                        |a, b| a.tot_eq_missing_kernel(b).into(),
520                        PlSmallStr::EMPTY,
521                    ),
522                }
523            }
524
525            fn not_equal(&self, rhs: &$ca) -> BooleanChunked {
526                // Broadcast.
527                match (self.len(), rhs.len()) {
528                    (_, 1) => {
529                        if let Some(value) = rhs.get(0) {
530                            self.not_equal(value)
531                        } else {
532                            BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
533                        }
534                    },
535                    (1, _) => {
536                        if let Some(value) = self.get(0) {
537                            rhs.not_equal(value)
538                        } else {
539                            BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
540                        }
541                    },
542                    _ => arity::binary_mut_values(
543                        self,
544                        rhs,
545                        |a, b| a.tot_ne_kernel(b).into(),
546                        PlSmallStr::EMPTY,
547                    ),
548                }
549            }
550
551            fn not_equal_missing(&self, rhs: &$ca) -> BooleanChunked {
552                // Broadcast.
553                match (self.len(), rhs.len()) {
554                    (_, 1) => {
555                        if let Some(value) = rhs.get(0) {
556                            self.not_equal_missing(value)
557                        } else {
558                            self.is_not_null()
559                        }
560                    },
561                    (1, _) => {
562                        if let Some(value) = self.get(0) {
563                            rhs.not_equal_missing(value)
564                        } else {
565                            rhs.is_not_null()
566                        }
567                    },
568                    _ => arity::binary_mut_with_options(
569                        self,
570                        rhs,
571                        |a, b| a.tot_ne_missing_kernel(b).into(),
572                        PlSmallStr::EMPTY,
573                    ),
574                }
575            }
576        }
577
578        impl ChunkCompareIneq<&$ca> for $ca {
579            type Item = BooleanChunked;
580
581            fn lt(&self, rhs: &$ca) -> BooleanChunked {
582                // Broadcast.
583                match (self.len(), rhs.len()) {
584                    (_, 1) => {
585                        if let Some(value) = rhs.get(0) {
586                            self.lt(value)
587                        } else {
588                            BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
589                        }
590                    },
591                    (1, _) => {
592                        if let Some(value) = self.get(0) {
593                            rhs.gt(value)
594                        } else {
595                            BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
596                        }
597                    },
598                    _ => arity::binary_mut_values(
599                        self,
600                        rhs,
601                        |a, b| a.tot_lt_kernel(b).into(),
602                        PlSmallStr::EMPTY,
603                    ),
604                }
605            }
606
607            fn lt_eq(&self, rhs: &$ca) -> BooleanChunked {
608                // Broadcast.
609                match (self.len(), rhs.len()) {
610                    (_, 1) => {
611                        if let Some(value) = rhs.get(0) {
612                            self.lt_eq(value)
613                        } else {
614                            BooleanChunked::full_null(PlSmallStr::EMPTY, self.len())
615                        }
616                    },
617                    (1, _) => {
618                        if let Some(value) = self.get(0) {
619                            rhs.gt_eq(value)
620                        } else {
621                            BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len())
622                        }
623                    },
624                    _ => arity::binary_mut_values(
625                        self,
626                        rhs,
627                        |a, b| a.tot_le_kernel(b).into(),
628                        PlSmallStr::EMPTY,
629                    ),
630                }
631            }
632
633            fn gt(&self, rhs: &Self) -> BooleanChunked {
634                rhs.lt(self)
635            }
636
637            fn gt_eq(&self, rhs: &Self) -> BooleanChunked {
638                rhs.lt_eq(self)
639            }
640        }
641        )+
642    };
643}
644
645binary_eq_ineq_impl!(BinaryChunked, BinaryOffsetChunked);
646
647fn _list_comparison_helper<F, B>(
648    lhs: &ListChunked,
649    rhs: &ListChunked,
650    op: F,
651    broadcast_op: B,
652    missing: bool,
653    is_ne: bool,
654) -> BooleanChunked
655where
656    F: Fn(&ListArray<i64>, &ListArray<i64>) -> Bitmap,
657    B: Fn(&ListArray<i64>, &Box<dyn Array>) -> Bitmap,
658{
659    match (lhs.len(), rhs.len()) {
660        (_, 1) => {
661            let right = rhs
662                .downcast_iter()
663                .find(|x| !x.is_empty())
664                .unwrap()
665                .as_any()
666                .downcast_ref::<ListArray<i64>>()
667                .unwrap();
668
669            if !right.validity().is_none_or(|v| v.get(0).unwrap()) {
670                if missing {
671                    if is_ne {
672                        return lhs.is_not_null();
673                    } else {
674                        return lhs.is_null();
675                    }
676                } else {
677                    return BooleanChunked::full_null(PlSmallStr::EMPTY, lhs.len());
678                }
679            }
680
681            let values = right.values().sliced(
682                (*right.offsets().first()).try_into().unwrap(),
683                right.offsets().range().try_into().unwrap(),
684            );
685
686            if missing {
687                arity::unary_mut_with_options(lhs, |a| broadcast_op(a, &values).into())
688            } else {
689                arity::unary_mut_values(lhs, |a| broadcast_op(a, &values).into())
690            }
691        },
692        (1, _) => {
693            let left = lhs
694                .downcast_iter()
695                .find(|x| !x.is_empty())
696                .unwrap()
697                .as_any()
698                .downcast_ref::<ListArray<i64>>()
699                .unwrap();
700
701            if !left.validity().is_none_or(|v| v.get(0).unwrap()) {
702                if missing {
703                    if is_ne {
704                        return rhs.is_not_null();
705                    } else {
706                        return rhs.is_null();
707                    }
708                } else {
709                    return BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len());
710                }
711            }
712
713            let values = left.values().sliced(
714                (*left.offsets().first()).try_into().unwrap(),
715                left.offsets().range().try_into().unwrap(),
716            );
717
718            if missing {
719                arity::unary_mut_with_options(rhs, |a| broadcast_op(a, &values).into())
720            } else {
721                arity::unary_mut_values(rhs, |a| broadcast_op(a, &values).into())
722            }
723        },
724        _ => {
725            if missing {
726                arity::binary_mut_with_options(lhs, rhs, |a, b| op(a, b).into(), PlSmallStr::EMPTY)
727            } else {
728                arity::binary_mut_values(lhs, rhs, |a, b| op(a, b).into(), PlSmallStr::EMPTY)
729            }
730        },
731    }
732}
733
734impl ChunkCompareEq<&ListChunked> for ListChunked {
735    type Item = BooleanChunked;
736    fn equal(&self, rhs: &ListChunked) -> BooleanChunked {
737        _list_comparison_helper(
738            self,
739            rhs,
740            TotalEqKernel::tot_eq_kernel,
741            TotalEqKernel::tot_eq_kernel_broadcast,
742            false,
743            false,
744        )
745    }
746
747    fn equal_missing(&self, rhs: &ListChunked) -> BooleanChunked {
748        _list_comparison_helper(
749            self,
750            rhs,
751            TotalEqKernel::tot_eq_missing_kernel,
752            TotalEqKernel::tot_eq_missing_kernel_broadcast,
753            true,
754            false,
755        )
756    }
757
758    fn not_equal(&self, rhs: &ListChunked) -> BooleanChunked {
759        _list_comparison_helper(
760            self,
761            rhs,
762            TotalEqKernel::tot_ne_kernel,
763            TotalEqKernel::tot_ne_kernel_broadcast,
764            false,
765            true,
766        )
767    }
768
769    fn not_equal_missing(&self, rhs: &ListChunked) -> BooleanChunked {
770        _list_comparison_helper(
771            self,
772            rhs,
773            TotalEqKernel::tot_ne_missing_kernel,
774            TotalEqKernel::tot_ne_missing_kernel_broadcast,
775            true,
776            true,
777        )
778    }
779}
780
781#[cfg(feature = "dtype-struct")]
782fn struct_helper<F, R>(
783    a: &StructChunked,
784    b: &StructChunked,
785    op: F,
786    reduce: R,
787    op_is_ne: bool,
788    is_missing: bool,
789) -> BooleanChunked
790where
791    F: Fn(&Series, &Series) -> BooleanChunked,
792    R: Fn(BooleanChunked, BooleanChunked) -> BooleanChunked,
793{
794    let len_a = a.len();
795    let len_b = b.len();
796    let broadcasts = len_a == 1 || len_b == 1;
797    assert!(a.struct_fields().len() == b.struct_fields().len());
798    assert!(a.len() == b.len() || broadcasts);
799
800    let mut out = a
801        .fields_as_series()
802        .iter()
803        .zip(b.fields_as_series().iter())
804        .map(|(l, r)| op(l, r))
805        .reduce(&reduce)
806        .unwrap_or_else(|| BooleanChunked::full(PlSmallStr::EMPTY, !op_is_ne, a.len()));
807
808    if is_missing && (a.has_nulls() || b.has_nulls()) {
809        // Do some allocations so that we can use the Series dispatch, it otherwise
810        // gets complicated dealing with combinations of ==, != and broadcasting.
811        let default =
812            || BooleanChunked::with_chunk(PlSmallStr::EMPTY, BooleanArray::from_slice([true]));
813        let validity_to_ca = |x| unsafe {
814            BooleanChunked::with_chunk(
815                PlSmallStr::EMPTY,
816                BooleanArray::from_inner_unchecked(ArrowDataType::Boolean, x, None),
817            )
818        };
819
820        let a_s = a.rechunk_validity().map_or_else(default, validity_to_ca);
821        let b_s = b.rechunk_validity().map_or_else(default, validity_to_ca);
822
823        let shared_validity = (&a_s).bitand(&b_s);
824        let valid_nested = if op_is_ne {
825            (shared_validity).bitand(out)
826        } else {
827            (!shared_validity).bitor(out)
828        };
829        out = reduce(op(&a_s.into_series(), &b_s.into_series()), valid_nested);
830    }
831
832    if !is_missing && (a.has_nulls() || b.has_nulls()) {
833        use arrow::compute::utils::combine_validities_and;
834        let av = a.rechunk_validity();
835        let bv = b.rechunk_validity();
836        out.set_validity(combine_validities_and(av.as_ref(), bv.as_ref()));
837    }
838
839    out
840}
841
842#[cfg(feature = "dtype-struct")]
843impl ChunkCompareEq<&StructChunked> for StructChunked {
844    type Item = BooleanChunked;
845    fn equal(&self, rhs: &StructChunked) -> BooleanChunked {
846        struct_helper(
847            self,
848            rhs,
849            |l, r| l.equal_missing(r).unwrap(),
850            |a, b| a.bitand(b),
851            false,
852            false,
853        )
854    }
855
856    fn equal_missing(&self, rhs: &StructChunked) -> BooleanChunked {
857        struct_helper(
858            self,
859            rhs,
860            |l, r| l.equal_missing(r).unwrap(),
861            |a, b| a.bitand(b),
862            false,
863            true,
864        )
865    }
866
867    fn not_equal(&self, rhs: &StructChunked) -> BooleanChunked {
868        struct_helper(
869            self,
870            rhs,
871            |l, r| l.not_equal_missing(r).unwrap(),
872            |a, b| a.bitor(b),
873            true,
874            false,
875        )
876    }
877
878    fn not_equal_missing(&self, rhs: &StructChunked) -> BooleanChunked {
879        struct_helper(
880            self,
881            rhs,
882            |l, r| l.not_equal_missing(r).unwrap(),
883            |a, b| a.bitor(b),
884            true,
885            true,
886        )
887    }
888}
889
890#[cfg(feature = "dtype-array")]
891fn _array_comparison_helper<F, B>(
892    lhs: &ArrayChunked,
893    rhs: &ArrayChunked,
894    op: F,
895    broadcast_op: B,
896    missing: bool,
897    is_ne: bool,
898) -> BooleanChunked
899where
900    F: Fn(&FixedSizeListArray, &FixedSizeListArray) -> Bitmap,
901    B: Fn(&FixedSizeListArray, &Box<dyn Array>) -> Bitmap,
902{
903    match (lhs.len(), rhs.len()) {
904        (_, 1) => {
905            let right = rhs
906                .downcast_iter()
907                .find(|x| !x.is_empty())
908                .unwrap()
909                .as_any()
910                .downcast_ref::<FixedSizeListArray>()
911                .unwrap();
912
913            if !right.validity().is_none_or(|v| v.get(0).unwrap()) {
914                if missing {
915                    if is_ne {
916                        return lhs.is_not_null();
917                    } else {
918                        return lhs.is_null();
919                    }
920                } else {
921                    return BooleanChunked::full_null(PlSmallStr::EMPTY, lhs.len());
922                }
923            }
924
925            if missing {
926                arity::unary_mut_with_options(lhs, |a| broadcast_op(a, right.values()).into())
927            } else {
928                arity::unary_mut_values(lhs, |a| broadcast_op(a, right.values()).into())
929            }
930        },
931        (1, _) => {
932            let left = lhs
933                .downcast_iter()
934                .find(|x| !x.is_empty())
935                .unwrap()
936                .as_any()
937                .downcast_ref::<FixedSizeListArray>()
938                .unwrap();
939
940            if !left.validity().is_none_or(|v| v.get(0).unwrap()) {
941                if missing {
942                    if is_ne {
943                        return rhs.is_not_null();
944                    } else {
945                        return rhs.is_null();
946                    }
947                } else {
948                    return BooleanChunked::full_null(PlSmallStr::EMPTY, rhs.len());
949                }
950            }
951
952            if missing {
953                arity::unary_mut_with_options(rhs, |a| broadcast_op(a, left.values()).into())
954            } else {
955                arity::unary_mut_values(rhs, |a| broadcast_op(a, left.values()).into())
956            }
957        },
958        _ => {
959            if missing {
960                arity::binary_mut_with_options(lhs, rhs, |a, b| op(a, b).into(), PlSmallStr::EMPTY)
961            } else {
962                arity::binary_mut_values(lhs, rhs, |a, b| op(a, b).into(), PlSmallStr::EMPTY)
963            }
964        },
965    }
966}
967
968#[cfg(feature = "dtype-array")]
969impl ChunkCompareEq<&ArrayChunked> for ArrayChunked {
970    type Item = BooleanChunked;
971    fn equal(&self, rhs: &ArrayChunked) -> BooleanChunked {
972        _array_comparison_helper(
973            self,
974            rhs,
975            TotalEqKernel::tot_eq_kernel,
976            TotalEqKernel::tot_eq_kernel_broadcast,
977            false,
978            false,
979        )
980    }
981
982    fn equal_missing(&self, rhs: &ArrayChunked) -> BooleanChunked {
983        _array_comparison_helper(
984            self,
985            rhs,
986            TotalEqKernel::tot_eq_missing_kernel,
987            TotalEqKernel::tot_eq_missing_kernel_broadcast,
988            true,
989            false,
990        )
991    }
992
993    fn not_equal(&self, rhs: &ArrayChunked) -> BooleanChunked {
994        _array_comparison_helper(
995            self,
996            rhs,
997            TotalEqKernel::tot_ne_kernel,
998            TotalEqKernel::tot_ne_kernel_broadcast,
999            false,
1000            true,
1001        )
1002    }
1003
1004    fn not_equal_missing(&self, rhs: &ArrayChunked) -> Self::Item {
1005        _array_comparison_helper(
1006            self,
1007            rhs,
1008            TotalEqKernel::tot_ne_missing_kernel,
1009            TotalEqKernel::tot_ne_missing_kernel_broadcast,
1010            true,
1011            true,
1012        )
1013    }
1014}
1015
1016impl Not for &BooleanChunked {
1017    type Output = BooleanChunked;
1018
1019    fn not(self) -> Self::Output {
1020        let chunks = self.downcast_iter().map(polars_compute::boolean::not);
1021        ChunkedArray::from_chunk_iter(self.name().clone(), chunks)
1022    }
1023}
1024
1025impl Not for BooleanChunked {
1026    type Output = BooleanChunked;
1027
1028    fn not(self) -> Self::Output {
1029        (&self).not()
1030    }
1031}
1032
1033impl BooleanChunked {
1034    /// Returns whether any of the values in the column are `true`.
1035    ///
1036    /// Null values are ignored.
1037    pub fn any(&self) -> bool {
1038        self.downcast_iter()
1039            .any(|a| polars_compute::boolean::any(a).unwrap_or(false))
1040    }
1041
1042    /// Returns whether all values in the array are `true`.
1043    ///
1044    /// Null values are ignored.
1045    pub fn all(&self) -> bool {
1046        self.downcast_iter()
1047            .all(|a| polars_compute::boolean::all(a).unwrap_or(true))
1048    }
1049
1050    /// Returns whether any of the values in the column are `true`.
1051    ///
1052    /// The output is unknown (`None`) if the array contains any null values and
1053    /// no `true` values.
1054    pub fn any_kleene(&self) -> Option<bool> {
1055        for arr in self.downcast_iter() {
1056            if let Some(true) = polars_compute::boolean::any(arr) {
1057                return Some(true);
1058            }
1059        }
1060        if self.has_nulls() { None } else { Some(false) }
1061    }
1062
1063    /// Returns whether all values in the column are `true`.
1064    ///
1065    /// The output is unknown (`None`) if the array contains any null values and
1066    /// no `false` values.
1067    pub fn all_kleene(&self) -> Option<bool> {
1068        for arr in self.downcast_iter() {
1069            if let Some(false) = polars_compute::boolean::all(arr) {
1070                return Some(false);
1071            }
1072        }
1073        if self.has_nulls() { None } else { Some(true) }
1074    }
1075}
1076
1077// private
1078pub(crate) trait ChunkEqualElement {
1079    /// Only meant for physical types.
1080    /// Check if element in self is equal to element in other, assumes same dtypes
1081    ///
1082    /// # Safety
1083    ///
1084    /// No type checks.
1085    unsafe fn equal_element(&self, _idx_self: usize, _idx_other: usize, _other: &Series) -> bool {
1086        unimplemented!()
1087    }
1088}
1089
1090impl<T> ChunkEqualElement for ChunkedArray<T>
1091where
1092    T: PolarsNumericType,
1093{
1094    unsafe fn equal_element(&self, idx_self: usize, idx_other: usize, other: &Series) -> bool {
1095        let ca_other = other.as_ref().as_ref();
1096        debug_assert!(self.dtype() == other.dtype());
1097        let ca_other = &*(ca_other as *const ChunkedArray<T>);
1098        // Should be get and not get_unchecked, because there could be nulls
1099        self.get_unchecked(idx_self)
1100            .tot_eq(&ca_other.get_unchecked(idx_other))
1101    }
1102}
1103
1104impl ChunkEqualElement for BooleanChunked {
1105    unsafe fn equal_element(&self, idx_self: usize, idx_other: usize, other: &Series) -> bool {
1106        let ca_other = other.as_ref().as_ref();
1107        debug_assert!(self.dtype() == other.dtype());
1108        let ca_other = &*(ca_other as *const BooleanChunked);
1109        self.get_unchecked(idx_self) == ca_other.get_unchecked(idx_other)
1110    }
1111}
1112
1113impl ChunkEqualElement for StringChunked {
1114    unsafe fn equal_element(&self, idx_self: usize, idx_other: usize, other: &Series) -> bool {
1115        let ca_other = other.as_ref().as_ref();
1116        debug_assert!(self.dtype() == other.dtype());
1117        let ca_other = &*(ca_other as *const StringChunked);
1118        self.get_unchecked(idx_self) == ca_other.get_unchecked(idx_other)
1119    }
1120}
1121
1122impl ChunkEqualElement for BinaryChunked {
1123    unsafe fn equal_element(&self, idx_self: usize, idx_other: usize, other: &Series) -> bool {
1124        let ca_other = other.as_ref().as_ref();
1125        debug_assert!(self.dtype() == other.dtype());
1126        let ca_other = &*(ca_other as *const BinaryChunked);
1127        self.get_unchecked(idx_self) == ca_other.get_unchecked(idx_other)
1128    }
1129}
1130
1131impl ChunkEqualElement for BinaryOffsetChunked {
1132    unsafe fn equal_element(&self, idx_self: usize, idx_other: usize, other: &Series) -> bool {
1133        let ca_other = other.as_ref().as_ref();
1134        debug_assert!(self.dtype() == other.dtype());
1135        let ca_other = &*(ca_other as *const BinaryOffsetChunked);
1136        self.get_unchecked(idx_self) == ca_other.get_unchecked(idx_other)
1137    }
1138}
1139
1140impl ChunkEqualElement for ListChunked {}
1141#[cfg(feature = "dtype-array")]
1142impl ChunkEqualElement for ArrayChunked {}
1143
1144#[cfg(test)]
1145#[cfg_attr(feature = "nightly", allow(clippy::manual_repeat_n))] // remove once stable
1146mod test {
1147    use std::iter::repeat_n;
1148
1149    use super::super::test::get_chunked_array;
1150    use crate::prelude::*;
1151
1152    pub(crate) fn create_two_chunked() -> (Int32Chunked, Int32Chunked) {
1153        let mut a1 = Int32Chunked::new(PlSmallStr::from_static("a"), &[1, 2, 3]);
1154        let a2 = Int32Chunked::new(PlSmallStr::from_static("a"), &[4, 5, 6]);
1155        let a3 = Int32Chunked::new(PlSmallStr::from_static("a"), &[1, 2, 3, 4, 5, 6]);
1156        a1.append(&a2).unwrap();
1157        (a1, a3)
1158    }
1159
1160    #[test]
1161    fn test_bitwise_ops() {
1162        let a = BooleanChunked::new(PlSmallStr::from_static("a"), &[true, false, false]);
1163        let b = BooleanChunked::new(
1164            PlSmallStr::from_static("b"),
1165            &[Some(true), Some(true), None],
1166        );
1167        assert_eq!(Vec::from(&a | &b), &[Some(true), Some(true), None]);
1168        assert_eq!(Vec::from(&a & &b), &[Some(true), Some(false), Some(false)]);
1169        assert_eq!(Vec::from(!b), &[Some(false), Some(false), None]);
1170    }
1171
1172    #[test]
1173    fn test_compare_chunk_diff() {
1174        let (a1, a2) = create_two_chunked();
1175
1176        assert_eq!(
1177            a1.equal(&a2).into_iter().collect::<Vec<_>>(),
1178            repeat_n(Some(true), 6).collect::<Vec<_>>()
1179        );
1180        assert_eq!(
1181            a2.equal(&a1).into_iter().collect::<Vec<_>>(),
1182            repeat_n(Some(true), 6).collect::<Vec<_>>()
1183        );
1184        assert_eq!(
1185            a1.not_equal(&a2).into_iter().collect::<Vec<_>>(),
1186            repeat_n(Some(false), 6).collect::<Vec<_>>()
1187        );
1188        assert_eq!(
1189            a2.not_equal(&a1).into_iter().collect::<Vec<_>>(),
1190            repeat_n(Some(false), 6).collect::<Vec<_>>()
1191        );
1192        assert_eq!(
1193            a1.gt(&a2).into_iter().collect::<Vec<_>>(),
1194            repeat_n(Some(false), 6).collect::<Vec<_>>()
1195        );
1196        assert_eq!(
1197            a2.gt(&a1).into_iter().collect::<Vec<_>>(),
1198            repeat_n(Some(false), 6).collect::<Vec<_>>()
1199        );
1200        assert_eq!(
1201            a1.gt_eq(&a2).into_iter().collect::<Vec<_>>(),
1202            repeat_n(Some(true), 6).collect::<Vec<_>>()
1203        );
1204        assert_eq!(
1205            a2.gt_eq(&a1).into_iter().collect::<Vec<_>>(),
1206            repeat_n(Some(true), 6).collect::<Vec<_>>()
1207        );
1208        assert_eq!(
1209            a1.lt_eq(&a2).into_iter().collect::<Vec<_>>(),
1210            repeat_n(Some(true), 6).collect::<Vec<_>>()
1211        );
1212        assert_eq!(
1213            a2.lt_eq(&a1).into_iter().collect::<Vec<_>>(),
1214            repeat_n(Some(true), 6).collect::<Vec<_>>()
1215        );
1216        assert_eq!(
1217            a1.lt(&a2).into_iter().collect::<Vec<_>>(),
1218            repeat_n(Some(false), 6).collect::<Vec<_>>()
1219        );
1220        assert_eq!(
1221            a2.lt(&a1).into_iter().collect::<Vec<_>>(),
1222            repeat_n(Some(false), 6).collect::<Vec<_>>()
1223        );
1224    }
1225
1226    #[test]
1227    fn test_equal_chunks() {
1228        let a1 = get_chunked_array();
1229        let a2 = get_chunked_array();
1230
1231        assert_eq!(
1232            a1.equal(&a2).into_iter().collect::<Vec<_>>(),
1233            repeat_n(Some(true), 3).collect::<Vec<_>>()
1234        );
1235        assert_eq!(
1236            a2.equal(&a1).into_iter().collect::<Vec<_>>(),
1237            repeat_n(Some(true), 3).collect::<Vec<_>>()
1238        );
1239        assert_eq!(
1240            a1.not_equal(&a2).into_iter().collect::<Vec<_>>(),
1241            repeat_n(Some(false), 3).collect::<Vec<_>>()
1242        );
1243        assert_eq!(
1244            a2.not_equal(&a1).into_iter().collect::<Vec<_>>(),
1245            repeat_n(Some(false), 3).collect::<Vec<_>>()
1246        );
1247        assert_eq!(
1248            a1.gt(&a2).into_iter().collect::<Vec<_>>(),
1249            repeat_n(Some(false), 3).collect::<Vec<_>>()
1250        );
1251        assert_eq!(
1252            a2.gt(&a1).into_iter().collect::<Vec<_>>(),
1253            repeat_n(Some(false), 3).collect::<Vec<_>>()
1254        );
1255        assert_eq!(
1256            a1.gt_eq(&a2).into_iter().collect::<Vec<_>>(),
1257            repeat_n(Some(true), 3).collect::<Vec<_>>()
1258        );
1259        assert_eq!(
1260            a2.gt_eq(&a1).into_iter().collect::<Vec<_>>(),
1261            repeat_n(Some(true), 3).collect::<Vec<_>>()
1262        );
1263        assert_eq!(
1264            a1.lt_eq(&a2).into_iter().collect::<Vec<_>>(),
1265            repeat_n(Some(true), 3).collect::<Vec<_>>()
1266        );
1267        assert_eq!(
1268            a2.lt_eq(&a1).into_iter().collect::<Vec<_>>(),
1269            repeat_n(Some(true), 3).collect::<Vec<_>>()
1270        );
1271        assert_eq!(
1272            a1.lt(&a2).into_iter().collect::<Vec<_>>(),
1273            repeat_n(Some(false), 3).collect::<Vec<_>>()
1274        );
1275        assert_eq!(
1276            a2.lt(&a1).into_iter().collect::<Vec<_>>(),
1277            repeat_n(Some(false), 3).collect::<Vec<_>>()
1278        );
1279    }
1280
1281    #[test]
1282    fn test_null_handling() {
1283        // assert we comply with arrows way of handling null data
1284        // we check comparison on two arrays with one chunk and verify it is equal to a differently
1285        // chunked array comparison.
1286
1287        // two same chunked arrays
1288        let a1: Int32Chunked = [Some(1), None, Some(3)].iter().copied().collect();
1289        let a2: Int32Chunked = [Some(1), Some(2), Some(3)].iter().copied().collect();
1290
1291        let mut a2_2chunks: Int32Chunked = [Some(1), Some(2)].iter().copied().collect();
1292        a2_2chunks
1293            .append(&[Some(3)].iter().copied().collect())
1294            .unwrap();
1295
1296        assert_eq!(
1297            a1.equal(&a2).into_iter().collect::<Vec<_>>(),
1298            a1.equal(&a2_2chunks).into_iter().collect::<Vec<_>>()
1299        );
1300
1301        assert_eq!(
1302            a1.not_equal(&a2).into_iter().collect::<Vec<_>>(),
1303            a1.not_equal(&a2_2chunks).into_iter().collect::<Vec<_>>()
1304        );
1305        assert_eq!(
1306            a1.not_equal(&a2).into_iter().collect::<Vec<_>>(),
1307            a2_2chunks.not_equal(&a1).into_iter().collect::<Vec<_>>()
1308        );
1309
1310        assert_eq!(
1311            a1.gt(&a2).into_iter().collect::<Vec<_>>(),
1312            a1.gt(&a2_2chunks).into_iter().collect::<Vec<_>>()
1313        );
1314        assert_eq!(
1315            a1.gt(&a2).into_iter().collect::<Vec<_>>(),
1316            a2_2chunks.gt(&a1).into_iter().collect::<Vec<_>>()
1317        );
1318
1319        assert_eq!(
1320            a1.gt_eq(&a2).into_iter().collect::<Vec<_>>(),
1321            a1.gt_eq(&a2_2chunks).into_iter().collect::<Vec<_>>()
1322        );
1323        assert_eq!(
1324            a1.gt_eq(&a2).into_iter().collect::<Vec<_>>(),
1325            a2_2chunks.gt_eq(&a1).into_iter().collect::<Vec<_>>()
1326        );
1327
1328        assert_eq!(
1329            a1.lt_eq(&a2).into_iter().collect::<Vec<_>>(),
1330            a1.lt_eq(&a2_2chunks).into_iter().collect::<Vec<_>>()
1331        );
1332        assert_eq!(
1333            a1.lt_eq(&a2).into_iter().collect::<Vec<_>>(),
1334            a2_2chunks.lt_eq(&a1).into_iter().collect::<Vec<_>>()
1335        );
1336
1337        assert_eq!(
1338            a1.lt(&a2).into_iter().collect::<Vec<_>>(),
1339            a1.lt(&a2_2chunks).into_iter().collect::<Vec<_>>()
1340        );
1341        assert_eq!(
1342            a1.lt(&a2).into_iter().collect::<Vec<_>>(),
1343            a2_2chunks.lt(&a1).into_iter().collect::<Vec<_>>()
1344        );
1345    }
1346
1347    #[test]
1348    fn test_left_right() {
1349        // This failed with arrow comparisons.
1350        // sliced
1351        let a1: Int32Chunked = [Some(1), Some(2)].iter().copied().collect();
1352        let a1 = a1.slice(1, 1);
1353        let a2: Int32Chunked = [Some(2)].iter().copied().collect();
1354        assert_eq!(a1.equal(&a2).sum(), a2.equal(&a1).sum());
1355        assert_eq!(a1.not_equal(&a2).sum(), a2.not_equal(&a1).sum());
1356        assert_eq!(a1.gt(&a2).sum(), a2.gt(&a1).sum());
1357        assert_eq!(a1.lt(&a2).sum(), a2.lt(&a1).sum());
1358        assert_eq!(a1.lt_eq(&a2).sum(), a2.lt_eq(&a1).sum());
1359        assert_eq!(a1.gt_eq(&a2).sum(), a2.gt_eq(&a1).sum());
1360
1361        let a1: StringChunked = ["a", "b"].iter().copied().collect();
1362        let a1 = a1.slice(1, 1);
1363        let a2: StringChunked = ["b"].iter().copied().collect();
1364        assert_eq!(a1.equal(&a2).sum(), a2.equal(&a1).sum());
1365        assert_eq!(a1.not_equal(&a2).sum(), a2.not_equal(&a1).sum());
1366        assert_eq!(a1.gt(&a2).sum(), a2.gt(&a1).sum());
1367        assert_eq!(a1.lt(&a2).sum(), a2.lt(&a1).sum());
1368        assert_eq!(a1.lt_eq(&a2).sum(), a2.lt_eq(&a1).sum());
1369        assert_eq!(a1.gt_eq(&a2).sum(), a2.gt_eq(&a1).sum());
1370    }
1371
1372    #[test]
1373    fn test_kleene() {
1374        let a = BooleanChunked::new(PlSmallStr::EMPTY, &[Some(true), Some(false), None]);
1375        let trues = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[true, true, true]);
1376        let falses = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[false, false, false]);
1377
1378        let c = &a | &trues;
1379        assert_eq!(Vec::from(&c), &[Some(true), Some(true), Some(true)]);
1380
1381        let c = &a | &falses;
1382        assert_eq!(Vec::from(&c), &[Some(true), Some(false), None])
1383    }
1384
1385    #[test]
1386    fn list_broadcasting_lists() {
1387        let s_el = Series::new(PlSmallStr::EMPTY, &[1, 2, 3]);
1388        let s_lhs = Series::new(PlSmallStr::EMPTY, &[s_el.clone(), s_el.clone()]);
1389        let s_rhs = Series::new(PlSmallStr::EMPTY, std::slice::from_ref(&s_el));
1390
1391        let result = s_lhs.list().unwrap().equal(s_rhs.list().unwrap());
1392        assert_eq!(result.len(), 2);
1393        assert!(result.all());
1394    }
1395
1396    #[test]
1397    fn test_broadcasting_bools() {
1398        let a = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[true, false, true]);
1399        let true_ = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[true]);
1400        let false_ = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[false]);
1401
1402        let out = a.equal(&true_);
1403        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1404        let out = true_.equal(&a);
1405        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1406        let out = a.equal(&false_);
1407        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1408        let out = false_.equal(&a);
1409        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1410
1411        let out = a.not_equal(&true_);
1412        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1413        let out = true_.not_equal(&a);
1414        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1415        let out = a.not_equal(&false_);
1416        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1417        let out = false_.not_equal(&a);
1418        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1419
1420        let out = a.gt(&true_);
1421        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1422        let out = true_.gt(&a);
1423        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1424        let out = a.gt(&false_);
1425        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1426        let out = false_.gt(&a);
1427        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1428
1429        let out = a.gt_eq(&true_);
1430        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1431        let out = true_.gt_eq(&a);
1432        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1433        let out = a.gt_eq(&false_);
1434        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1435        let out = false_.gt_eq(&a);
1436        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1437
1438        let out = a.lt(&true_);
1439        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1440        let out = true_.lt(&a);
1441        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1442        let out = a.lt(&false_);
1443        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1444        let out = false_.lt(&a);
1445        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1446
1447        let out = a.lt_eq(&true_);
1448        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1449        let out = true_.lt_eq(&a);
1450        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(true)]);
1451        let out = a.lt_eq(&false_);
1452        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(false)]);
1453        let out = false_.lt_eq(&a);
1454        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1455
1456        let a =
1457            BooleanChunked::from_slice_options(PlSmallStr::EMPTY, &[Some(true), Some(false), None]);
1458        let all_true = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[true, true, true]);
1459        let all_false = BooleanChunked::from_slice(PlSmallStr::EMPTY, &[false, false, false]);
1460        let out = a.equal(&true_);
1461        assert_eq!(Vec::from(&out), &[Some(true), Some(false), None]);
1462        let out = a.not_equal(&true_);
1463        assert_eq!(Vec::from(&out), &[Some(false), Some(true), None]);
1464
1465        let out = a.equal(&all_true);
1466        assert_eq!(Vec::from(&out), &[Some(true), Some(false), None]);
1467        let out = a.not_equal(&all_true);
1468        assert_eq!(Vec::from(&out), &[Some(false), Some(true), None]);
1469        let out = a.equal(&false_);
1470        assert_eq!(Vec::from(&out), &[Some(false), Some(true), None]);
1471        let out = a.not_equal(&false_);
1472        assert_eq!(Vec::from(&out), &[Some(true), Some(false), None]);
1473        let out = a.equal(&all_false);
1474        assert_eq!(Vec::from(&out), &[Some(false), Some(true), None]);
1475        let out = a.not_equal(&all_false);
1476        assert_eq!(Vec::from(&out), &[Some(true), Some(false), None]);
1477    }
1478
1479    #[test]
1480    fn test_broadcasting_numeric() {
1481        let a = Int32Chunked::from_slice(PlSmallStr::EMPTY, &[1, 2, 3]);
1482        let one = Int32Chunked::from_slice(PlSmallStr::EMPTY, &[1]);
1483        let three = Int32Chunked::from_slice(PlSmallStr::EMPTY, &[3]);
1484
1485        let out = a.equal(&one);
1486        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(false)]);
1487        let out = one.equal(&a);
1488        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(false)]);
1489        let out = a.equal(&three);
1490        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(true)]);
1491        let out = three.equal(&a);
1492        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(true)]);
1493
1494        let out = a.not_equal(&one);
1495        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(true)]);
1496        let out = one.not_equal(&a);
1497        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(true)]);
1498        let out = a.not_equal(&three);
1499        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(false)]);
1500        let out = three.not_equal(&a);
1501        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(false)]);
1502
1503        let out = a.gt(&one);
1504        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(true)]);
1505        let out = one.gt(&a);
1506        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1507        let out = a.gt(&three);
1508        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1509        let out = three.gt(&a);
1510        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(false)]);
1511
1512        let out = a.lt(&one);
1513        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1514        let out = one.lt(&a);
1515        assert_eq!(Vec::from(&out), &[Some(false), Some(true), Some(true)]);
1516        let out = a.lt(&three);
1517        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(false)]);
1518        let out = three.lt(&a);
1519        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(false)]);
1520
1521        let out = a.gt_eq(&one);
1522        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1523        let out = one.gt_eq(&a);
1524        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(false)]);
1525        let out = a.gt_eq(&three);
1526        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(true)]);
1527        let out = three.gt_eq(&a);
1528        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1529
1530        let out = a.lt_eq(&one);
1531        assert_eq!(Vec::from(&out), &[Some(true), Some(false), Some(false)]);
1532        let out = one.lt_eq(&a);
1533        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1534        let out = a.lt_eq(&three);
1535        assert_eq!(Vec::from(&out), &[Some(true), Some(true), Some(true)]);
1536        let out = three.lt_eq(&a);
1537        assert_eq!(Vec::from(&out), &[Some(false), Some(false), Some(true)]);
1538    }
1539}