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