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