1use std::iter::repeat_n;
5
6use arrow_array::cast::AsArray;
7use vortex_buffer::BitBuffer;
8use vortex_buffer::BufferMut;
9use vortex_buffer::read_u64_le;
10use vortex_error::VortexResult;
11use vortex_error::vortex_bail;
12use vortex_error::vortex_err;
13use vortex_mask::AllOr;
14use vortex_mask::Mask;
15
16use crate::ArrayRef;
17use crate::Canonical;
18use crate::ExecutionCtx;
19use crate::IntoArray;
20use crate::array::ArrayView;
21use crate::array::VTable;
22use crate::arrays::Bool;
23use crate::arrays::BoolArray;
24use crate::arrays::Constant;
25use crate::arrays::ConstantArray;
26use crate::arrays::ScalarFn;
27use crate::arrays::scalar_fn::ExactScalarFn;
28use crate::arrays::scalar_fn::ScalarFnArrayExt;
29use crate::arrays::scalar_fn::ScalarFnArrayView;
30use crate::arrow::ArrowSessionExt;
31use crate::arrow::FromArrowArray;
32use crate::builtins::ArrayBuiltins;
33use crate::dtype::DType;
34use crate::dtype::Nullability;
35use crate::kernel::ExecuteParentKernel;
36use crate::scalar::BoolScalar;
37use crate::scalar::Scalar;
38use crate::scalar_fn::fns::binary::Binary;
39use crate::scalar_fn::fns::operators::Operator;
40use crate::validity::Validity;
41
42pub trait BooleanKernel: VTable {
51 fn boolean(
53 lhs: ArrayView<'_, Self>,
54 rhs: &ArrayRef,
55 operator: Operator,
56 ctx: &mut ExecutionCtx,
57 ) -> VortexResult<Option<ArrayRef>>;
58}
59
60#[derive(Default, Debug)]
65pub struct BooleanExecuteAdaptor<V>(pub V);
66
67impl<V> ExecuteParentKernel<V> for BooleanExecuteAdaptor<V>
68where
69 V: BooleanKernel,
70{
71 type Parent = ExactScalarFn<Binary>;
72
73 fn execute_parent(
74 &self,
75 array: ArrayView<'_, V>,
76 parent: ScalarFnArrayView<'_, Binary>,
77 child_idx: usize,
78 ctx: &mut ExecutionCtx,
79 ) -> VortexResult<Option<ArrayRef>> {
80 let op = *parent.options;
81 if !is_boolean_operator(op) {
82 return Ok(None);
83 }
84
85 let Some(scalar_fn_array) = parent.as_opt::<ScalarFn>() else {
86 return Ok(None);
87 };
88 let other = match child_idx {
89 0 => scalar_fn_array.get_child(1),
90 1 => scalar_fn_array.get_child(0),
91 _ => return Ok(None),
92 };
93
94 if let Some(result) = constant_boolean(array.array(), other, op)? {
95 return Ok(Some(result));
96 }
97
98 V::boolean(array, other, op, ctx)
99 }
100}
101
102#[deprecated(note = "Use `ArrayBuiltins::binary` instead")]
104pub fn and_kleene(lhs: &ArrayRef, rhs: &ArrayRef) -> VortexResult<ArrayRef> {
105 lhs.clone().binary(rhs.clone(), Operator::And)
106}
107
108#[deprecated(note = "Use `ArrayBuiltins::binary` instead")]
110pub fn or_kleene(lhs: &ArrayRef, rhs: &ArrayRef) -> VortexResult<ArrayRef> {
111 lhs.clone().binary(rhs.clone(), Operator::Or)
112}
113
114pub(crate) fn execute_boolean(
119 lhs: &ArrayRef,
120 rhs: &ArrayRef,
121 op: Operator,
122 ctx: &mut ExecutionCtx,
123) -> VortexResult<ArrayRef> {
124 let nullable = boolean_nullability(lhs, rhs);
125
126 if lhs.is_empty() {
127 return Ok(Canonical::empty(&DType::Bool(nullable)).into_array());
128 }
129
130 if let Some(result) = constant_boolean(lhs, rhs, op)? {
131 return Ok(result);
132 }
133
134 if let Some(lhs) = lhs.as_opt::<Bool>()
135 && let Some(result) = <Bool as BooleanKernel>::boolean(lhs, rhs, op, ctx)?
136 {
137 return Ok(result);
138 }
139
140 if let Some(rhs) = rhs.as_opt::<Bool>()
141 && let Some(result) = <Bool as BooleanKernel>::boolean(rhs, lhs, op, ctx)?
142 {
143 return Ok(result);
144 }
145
146 arrow_execute_boolean(lhs.clone(), rhs.clone(), op, ctx)
147}
148
149fn arrow_execute_boolean(
151 lhs: ArrayRef,
152 rhs: ArrayRef,
153 op: Operator,
154 ctx: &mut ExecutionCtx,
155) -> VortexResult<ArrayRef> {
156 let nullable = boolean_nullability(&lhs, &rhs);
157 let session = ctx.session().clone();
158
159 let lhs = session
160 .arrow()
161 .execute_arrow(lhs, None, ctx)?
162 .as_boolean_opt()
163 .ok_or_else(|| vortex_err!("expected lhs to be boolean"))?
164 .clone();
165
166 let rhs = session
167 .arrow()
168 .execute_arrow(rhs, None, ctx)?
169 .as_boolean_opt()
170 .ok_or_else(|| vortex_err!("expected rhs to be boolean"))?
171 .clone();
172
173 let array = match op {
174 Operator::And => arrow_arith::boolean::and_kleene(&lhs, &rhs)?,
175 Operator::Or => arrow_arith::boolean::or_kleene(&lhs, &rhs)?,
176 other => vortex_bail!("Not a boolean operator: {other}"),
177 };
178
179 ArrayRef::from_arrow(&array, nullable == Nullability::Nullable)
180}
181
182fn constant_boolean(
184 lhs: &ArrayRef,
185 rhs: &ArrayRef,
186 op: Operator,
187) -> VortexResult<Option<ArrayRef>> {
188 let nullable = boolean_nullability(lhs, rhs);
189
190 match (lhs.as_opt::<Constant>(), rhs.as_opt::<Constant>()) {
191 (Some(lhs), Some(rhs)) => {
192 let result = boolean_scalar_scalar(
193 bool_scalar_value(lhs.scalar())?,
194 bool_scalar_value(rhs.scalar())?,
195 op,
196 )?;
197
198 Ok(Some(constant_bool_result(result, lhs.len(), nullable)))
199 }
200 (Some(lhs), None) => constant_array_boolean(lhs.scalar(), rhs, op, nullable),
201 (None, Some(rhs)) => constant_array_boolean(rhs.scalar(), lhs, op, nullable),
202 (None, None) => Ok(None),
203 }
204}
205
206fn constant_array_boolean(
207 constant: &Scalar,
208 array: &ArrayRef,
209 op: Operator,
210 nullability: Nullability,
211) -> VortexResult<Option<ArrayRef>> {
212 match (op, bool_scalar_value(constant)?) {
213 (Operator::And, Some(false)) => Ok(Some(constant_bool_result(
214 Some(false),
215 array.len(),
216 nullability,
217 ))),
218 (Operator::And, Some(true)) => Ok(Some(cast_bool_nullability(array, nullability)?)),
219 (Operator::Or, Some(true)) => Ok(Some(constant_bool_result(
220 Some(true),
221 array.len(),
222 nullability,
223 ))),
224 (Operator::Or, Some(false)) => Ok(Some(cast_bool_nullability(array, nullability)?)),
225 (Operator::And | Operator::Or, None) => Ok(None),
226 (other, _) => vortex_bail!("Not a boolean operator: {other}"),
227 }
228}
229
230fn boolean_scalar_scalar(
231 lhs: Option<bool>,
232 rhs: Option<bool>,
233 op: Operator,
234) -> VortexResult<Option<bool>> {
235 Ok(match op {
236 Operator::And => match (lhs, rhs) {
237 (Some(false), _) | (_, Some(false)) => Some(false),
238 (None, _) | (_, None) => None,
239 (Some(l), Some(r)) => Some(l & r),
240 },
241 Operator::Or => match (lhs, rhs) {
242 (Some(true), _) | (_, Some(true)) => Some(true),
243 (None, _) | (_, None) => None,
244 (Some(l), Some(r)) => Some(l | r),
245 },
246 other => vortex_bail!("Not a boolean operator: {other}"),
247 })
248}
249
250fn bool_scalar_value(scalar: &Scalar) -> VortexResult<Option<bool>> {
251 Ok(scalar
252 .as_bool_opt()
253 .ok_or_else(|| vortex_err!("expected boolean scalar"))?
254 .value())
255}
256
257pub fn kleene_boolean_buffers(
259 lhs_values: BitBuffer,
260 lhs_validity: Validity,
261 rhs_values: BitBuffer,
262 rhs_validity: Validity,
263 operator: Operator,
264 nullability: Nullability,
265 ctx: &mut ExecutionCtx,
266) -> VortexResult<ArrayRef> {
267 let len = lhs_values.len();
268 debug_assert_eq!(rhs_values.len(), len);
269
270 if lhs_validity.definitely_no_nulls() && rhs_validity.definitely_no_nulls() {
271 let values = match operator {
272 Operator::And => lhs_values & &rhs_values,
273 Operator::Or => lhs_values | &rhs_values,
274 other => vortex_bail!("Not a boolean operator: {other}"),
275 };
276 return Ok(BoolArray::try_new(values, Validity::from(nullability))?.into_array());
277 }
278
279 let lhs_valid = lhs_validity.execute_mask(len, ctx)?;
280 let rhs_valid = rhs_validity.execute_mask(len, ctx)?;
281 fused_boolean_buffers(
282 len,
283 &lhs_values,
284 &lhs_valid,
285 &rhs_values,
286 &rhs_valid,
287 operator,
288 nullability,
289 )
290}
291
292pub fn kleene_boolean_buffer_scalar(
294 values: BitBuffer,
295 validity: Validity,
296 scalar: &BoolScalar<'_>,
297 operator: Operator,
298 nullability: Nullability,
299 ctx: &mut ExecutionCtx,
300) -> VortexResult<ArrayRef> {
301 let scalar_value = scalar.value();
302 let len = values.len();
303 let result = match (operator, scalar_value) {
304 (Operator::And, Some(false)) => {
305 return Ok(constant_bool_result(Some(false), len, nullability));
306 }
307 (Operator::And, Some(true)) => {
308 return Ok(
309 BoolArray::try_new(values, validity.union_nullability(nullability))?.into_array(),
310 );
311 }
312 (Operator::Or, Some(true)) => {
313 return Ok(constant_bool_result(Some(true), len, nullability));
314 }
315 (Operator::Or, Some(false)) => {
316 return Ok(
317 BoolArray::try_new(values, validity.union_nullability(nullability))?.into_array(),
318 );
319 }
320 (Operator::And, None) => {
321 let valid = validity
322 .execute_mask(len, ctx)?
323 .bitand_not(&Mask::from_buffer(values));
324 BoolArray::try_new(
325 BitBuffer::new_unset(len),
326 Validity::from_mask(valid, nullability),
327 )?
328 }
329 (Operator::Or, None) => {
330 let valid = validity.execute_mask(len, ctx)? & &Mask::from_buffer(values);
331 BoolArray::try_new(
332 BitBuffer::new_set(len),
333 Validity::from_mask(valid, nullability),
334 )?
335 }
336 (other, _) => vortex_bail!("Not a boolean operator: {other}"),
337 };
338
339 Ok(result.into_array())
340}
341
342fn fused_boolean_buffers(
343 len: usize,
344 lhs_values: &BitBuffer,
345 lhs_validity: &Mask,
346 rhs_values: &BitBuffer,
347 rhs_validity: &Mask,
348 operator: Operator,
349 nullability: Nullability,
350) -> VortexResult<ArrayRef> {
351 if let Some(result) = fused_boolean_buffers_aligned(
352 len,
353 lhs_values,
354 lhs_validity,
355 rhs_values,
356 rhs_validity,
357 operator,
358 nullability,
359 )? {
360 return Ok(result);
361 }
362
363 let n_words = len.div_ceil(64);
364
365 macro_rules! fuse {
366 ($lhs_valid_words:expr, $rhs_valid_words:expr) => {
367 fused_boolean_words(
368 len,
369 lhs_values.chunks().iter_padded(),
370 rhs_values.chunks().iter_padded(),
371 $lhs_valid_words,
372 $rhs_valid_words,
373 operator,
374 nullability,
375 )
376 };
377 }
378
379 match (lhs_validity.bit_buffer(), rhs_validity.bit_buffer()) {
380 (AllOr::All, AllOr::All) => {
381 fuse!(repeat_n(u64::MAX, n_words), repeat_n(u64::MAX, n_words))
382 }
383 (AllOr::All, AllOr::None) => {
384 fuse!(repeat_n(u64::MAX, n_words), repeat_n(0, n_words))
385 }
386 (AllOr::All, AllOr::Some(rhs_validity)) => fuse!(
387 repeat_n(u64::MAX, n_words),
388 rhs_validity.chunks().iter_padded()
389 ),
390 (AllOr::None, AllOr::All) => {
391 fuse!(repeat_n(0, n_words), repeat_n(u64::MAX, n_words))
392 }
393 (AllOr::None, AllOr::None) => {
394 fuse!(repeat_n(0, n_words), repeat_n(0, n_words))
395 }
396 (AllOr::None, AllOr::Some(rhs_validity)) => {
397 fuse!(repeat_n(0, n_words), rhs_validity.chunks().iter_padded())
398 }
399 (AllOr::Some(lhs_validity), AllOr::All) => fuse!(
400 lhs_validity.chunks().iter_padded(),
401 repeat_n(u64::MAX, n_words)
402 ),
403 (AllOr::Some(lhs_validity), AllOr::None) => {
404 fuse!(lhs_validity.chunks().iter_padded(), repeat_n(0, n_words))
405 }
406 (AllOr::Some(lhs_validity), AllOr::Some(rhs_validity)) => fuse!(
407 lhs_validity.chunks().iter_padded(),
408 rhs_validity.chunks().iter_padded()
409 ),
410 }
411}
412
413#[derive(Clone, Copy)]
414enum WordSource<'a> {
415 Fill(u64),
416 Bytes(&'a [u8]),
417}
418
419impl WordSource<'_> {
420 #[inline]
421 fn word_at(self, byte_offset: usize, len: usize) -> u64 {
422 match self {
423 Self::Fill(word) => word,
424 Self::Bytes(bytes) => read_u64_le(&bytes[byte_offset..byte_offset + len]),
425 }
426 }
427}
428
429fn fused_boolean_buffers_aligned(
430 len: usize,
431 lhs_values: &BitBuffer,
432 lhs_validity: &Mask,
433 rhs_values: &BitBuffer,
434 rhs_validity: &Mask,
435 operator: Operator,
436 nullability: Nullability,
437) -> VortexResult<Option<ArrayRef>> {
438 let Some(lhs_values) = word_source_from_bit_buffer(lhs_values) else {
439 return Ok(None);
440 };
441 let Some(rhs_values) = word_source_from_bit_buffer(rhs_values) else {
442 return Ok(None);
443 };
444 let Some(lhs_validity) = word_source_from_mask(lhs_validity) else {
445 return Ok(None);
446 };
447 let Some(rhs_validity) = word_source_from_mask(rhs_validity) else {
448 return Ok(None);
449 };
450
451 Ok(Some(fused_boolean_word_sources(
452 len,
453 lhs_values,
454 rhs_values,
455 lhs_validity,
456 rhs_validity,
457 operator,
458 nullability,
459 )?))
460}
461
462fn word_source_from_bit_buffer(buffer: &BitBuffer) -> Option<WordSource<'_>> {
463 buffer.byte_aligned_bytes().map(WordSource::Bytes)
464}
465
466fn word_source_from_mask(mask: &Mask) -> Option<WordSource<'_>> {
467 match mask.bit_buffer() {
468 AllOr::All => Some(WordSource::Fill(u64::MAX)),
469 AllOr::None => Some(WordSource::Fill(0)),
470 AllOr::Some(buffer) => word_source_from_bit_buffer(buffer),
471 }
472}
473
474fn fused_boolean_word_sources(
475 len: usize,
476 lhs_words: WordSource<'_>,
477 rhs_words: WordSource<'_>,
478 lhs_valid_words: WordSource<'_>,
479 rhs_valid_words: WordSource<'_>,
480 operator: Operator,
481 nullability: Nullability,
482) -> VortexResult<ArrayRef> {
483 match operator {
484 Operator::And => fused_boolean_and_word_sources(
485 len,
486 lhs_words,
487 rhs_words,
488 lhs_valid_words,
489 rhs_valid_words,
490 nullability,
491 ),
492 Operator::Or => fused_boolean_or_word_sources(
493 len,
494 lhs_words,
495 rhs_words,
496 lhs_valid_words,
497 rhs_valid_words,
498 nullability,
499 ),
500 other => vortex_bail!("Not a boolean operator: {other}"),
501 }
502}
503
504fn fused_boolean_and_word_sources(
505 len: usize,
506 lhs_words: WordSource<'_>,
507 rhs_words: WordSource<'_>,
508 lhs_valid_words: WordSource<'_>,
509 rhs_valid_words: WordSource<'_>,
510 nullability: Nullability,
511) -> VortexResult<ArrayRef> {
512 let n_bytes = len.div_ceil(8);
513 let n_words = n_bytes.div_ceil(8);
514 let full_bytes = n_bytes - n_bytes % 8;
515 let mut values = BufferMut::<u64>::with_capacity(n_words);
516 let mut validity = BufferMut::<u64>::with_capacity(n_words);
517
518 for byte_offset in (0..full_bytes).step_by(8) {
519 let lhs = lhs_words.word_at(byte_offset, 8);
520 let rhs = rhs_words.word_at(byte_offset, 8);
521 let lhs_valid = lhs_valid_words.word_at(byte_offset, 8);
522 let rhs_valid = rhs_valid_words.word_at(byte_offset, 8);
523
524 unsafe {
527 values.push_unchecked(lhs & rhs);
528 validity
529 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & !lhs) | (rhs_valid & !rhs));
530 }
531 }
532
533 if full_bytes != n_bytes {
534 let tail_len = n_bytes - full_bytes;
535 let lhs = lhs_words.word_at(full_bytes, tail_len);
536 let rhs = rhs_words.word_at(full_bytes, tail_len);
537 let lhs_valid = lhs_valid_words.word_at(full_bytes, tail_len);
538 let rhs_valid = rhs_valid_words.word_at(full_bytes, tail_len);
539
540 unsafe {
542 values.push_unchecked(lhs & rhs);
543 validity
544 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & !lhs) | (rhs_valid & !rhs));
545 }
546 }
547
548 finish_fused_boolean_words(len, n_bytes, values, validity, nullability)
549}
550
551fn fused_boolean_or_word_sources(
552 len: usize,
553 lhs_words: WordSource<'_>,
554 rhs_words: WordSource<'_>,
555 lhs_valid_words: WordSource<'_>,
556 rhs_valid_words: WordSource<'_>,
557 nullability: Nullability,
558) -> VortexResult<ArrayRef> {
559 let n_bytes = len.div_ceil(8);
560 let n_words = n_bytes.div_ceil(8);
561 let full_bytes = n_bytes - n_bytes % 8;
562 let mut values = BufferMut::<u64>::with_capacity(n_words);
563 let mut validity = BufferMut::<u64>::with_capacity(n_words);
564
565 for byte_offset in (0..full_bytes).step_by(8) {
566 let lhs = lhs_words.word_at(byte_offset, 8);
567 let rhs = rhs_words.word_at(byte_offset, 8);
568 let lhs_valid = lhs_valid_words.word_at(byte_offset, 8);
569 let rhs_valid = rhs_valid_words.word_at(byte_offset, 8);
570
571 unsafe {
574 values.push_unchecked(lhs | rhs);
575 validity
576 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & lhs) | (rhs_valid & rhs));
577 }
578 }
579
580 if full_bytes != n_bytes {
581 let tail_len = n_bytes - full_bytes;
582 let lhs = lhs_words.word_at(full_bytes, tail_len);
583 let rhs = rhs_words.word_at(full_bytes, tail_len);
584 let lhs_valid = lhs_valid_words.word_at(full_bytes, tail_len);
585 let rhs_valid = rhs_valid_words.word_at(full_bytes, tail_len);
586
587 unsafe {
589 values.push_unchecked(lhs | rhs);
590 validity
591 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & lhs) | (rhs_valid & rhs));
592 }
593 }
594
595 finish_fused_boolean_words(len, n_bytes, values, validity, nullability)
596}
597
598fn finish_fused_boolean_words(
599 len: usize,
600 n_bytes: usize,
601 values: BufferMut<u64>,
602 validity: BufferMut<u64>,
603 nullability: Nullability,
604) -> VortexResult<ArrayRef> {
605 let mut values = values.into_byte_buffer();
606 values.truncate(n_bytes);
607 let mut validity = validity.into_byte_buffer();
608 validity.truncate(n_bytes);
609 Ok(BoolArray::try_new(
610 BitBuffer::new(values.freeze(), len),
611 Validity::from_mask(
612 Mask::from_buffer(BitBuffer::new(validity.freeze(), len)),
613 nullability,
614 ),
615 )?
616 .into_array())
617}
618
619fn fused_boolean_words<L, R, LV, RV>(
620 len: usize,
621 lhs_words: L,
622 rhs_words: R,
623 lhs_valid_words: LV,
624 rhs_valid_words: RV,
625 operator: Operator,
626 nullability: Nullability,
627) -> VortexResult<ArrayRef>
628where
629 L: Iterator<Item = u64>,
630 R: Iterator<Item = u64>,
631 LV: Iterator<Item = u64>,
632 RV: Iterator<Item = u64>,
633{
634 match operator {
635 Operator::And => fused_boolean_and_words(
636 len,
637 lhs_words,
638 rhs_words,
639 lhs_valid_words,
640 rhs_valid_words,
641 nullability,
642 ),
643 Operator::Or => fused_boolean_or_words(
644 len,
645 lhs_words,
646 rhs_words,
647 lhs_valid_words,
648 rhs_valid_words,
649 nullability,
650 ),
651 other => vortex_bail!("Not a boolean operator: {other}"),
652 }
653}
654
655fn fused_boolean_and_words<L, R, LV, RV>(
656 len: usize,
657 lhs_words: L,
658 rhs_words: R,
659 lhs_valid_words: LV,
660 rhs_valid_words: RV,
661 nullability: Nullability,
662) -> VortexResult<ArrayRef>
663where
664 L: Iterator<Item = u64>,
665 R: Iterator<Item = u64>,
666 LV: Iterator<Item = u64>,
667 RV: Iterator<Item = u64>,
668{
669 let n_words = len.div_ceil(64);
670 let mut values = BufferMut::<u64>::with_capacity(n_words);
671 let mut validity = BufferMut::<u64>::with_capacity(n_words);
672
673 for (((lhs, rhs), lhs_valid), rhs_valid) in lhs_words
674 .zip(rhs_words)
675 .zip(lhs_valid_words)
676 .zip(rhs_valid_words)
677 .take(n_words)
678 {
679 unsafe {
682 values.push_unchecked(lhs & rhs);
683 validity
684 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & !lhs) | (rhs_valid & !rhs));
685 }
686 }
687
688 finish_fused_boolean_words(len, len.div_ceil(8), values, validity, nullability)
689}
690
691fn fused_boolean_or_words<L, R, LV, RV>(
692 len: usize,
693 lhs_words: L,
694 rhs_words: R,
695 lhs_valid_words: LV,
696 rhs_valid_words: RV,
697 nullability: Nullability,
698) -> VortexResult<ArrayRef>
699where
700 L: Iterator<Item = u64>,
701 R: Iterator<Item = u64>,
702 LV: Iterator<Item = u64>,
703 RV: Iterator<Item = u64>,
704{
705 let n_words = len.div_ceil(64);
706 let mut values = BufferMut::<u64>::with_capacity(n_words);
707 let mut validity = BufferMut::<u64>::with_capacity(n_words);
708
709 for (((lhs, rhs), lhs_valid), rhs_valid) in lhs_words
710 .zip(rhs_words)
711 .zip(lhs_valid_words)
712 .zip(rhs_valid_words)
713 .take(n_words)
714 {
715 unsafe {
718 values.push_unchecked(lhs | rhs);
719 validity
720 .push_unchecked((lhs_valid & rhs_valid) | (lhs_valid & lhs) | (rhs_valid & rhs));
721 }
722 }
723
724 finish_fused_boolean_words(len, len.div_ceil(8), values, validity, nullability)
725}
726
727fn constant_bool_result(value: Option<bool>, len: usize, nullability: Nullability) -> ArrayRef {
728 let scalar = value
729 .map(|b| Scalar::bool(b, nullability))
730 .unwrap_or_else(|| Scalar::null(DType::Bool(nullability)));
731
732 ConstantArray::new(scalar, len).into_array()
733}
734
735fn cast_bool_nullability(array: &ArrayRef, nullability: Nullability) -> VortexResult<ArrayRef> {
736 let dtype = DType::Bool(nullability);
737 if array.dtype() == &dtype {
738 Ok(array.clone())
739 } else {
740 array.cast(dtype)
741 }
742}
743
744fn boolean_nullability(lhs: &ArrayRef, rhs: &ArrayRef) -> Nullability {
745 lhs.dtype().nullability() | rhs.dtype().nullability()
746}
747
748#[inline]
749fn is_boolean_operator(operator: Operator) -> bool {
750 matches!(operator, Operator::And | Operator::Or)
751}
752
753#[cfg(test)]
754mod tests {
755 use rstest::rstest;
756 use vortex_error::VortexResult;
757
758 use crate::ArrayRef;
759 use crate::IntoArray;
760 use crate::VortexSessionExecute;
761 use crate::array_session;
762 use crate::arrays::BoolArray;
763 use crate::arrays::ConstantArray;
764 use crate::assert_arrays_eq;
765 use crate::builtins::ArrayBuiltins;
766 #[expect(deprecated)]
767 use crate::canonical::ToCanonical as _;
768 use crate::dtype::DType;
769 use crate::dtype::Nullability;
770 use crate::scalar::Scalar;
771 use crate::scalar_fn::fns::operators::Operator;
772
773 #[test]
774 fn test_kleene_truth_table() -> VortexResult<()> {
775 let mut ctx = array_session().create_execution_ctx();
776 let lhs = BoolArray::from_iter([
777 Some(true),
778 Some(true),
779 Some(true),
780 Some(false),
781 Some(false),
782 Some(false),
783 None,
784 None,
785 None,
786 ])
787 .into_array();
788 let rhs = BoolArray::from_iter([
789 Some(true),
790 Some(false),
791 None,
792 Some(true),
793 Some(false),
794 None,
795 Some(true),
796 Some(false),
797 None,
798 ])
799 .into_array();
800
801 assert_arrays_eq!(
802 lhs.binary(rhs.clone(), Operator::And)?,
803 BoolArray::from_iter([
804 Some(true),
805 Some(false),
806 None,
807 Some(false),
808 Some(false),
809 Some(false),
810 None,
811 Some(false),
812 None,
813 ]),
814 &mut ctx
815 );
816
817 assert_arrays_eq!(
818 lhs.binary(rhs, Operator::Or)?,
819 BoolArray::from_iter([
820 Some(true),
821 Some(true),
822 Some(true),
823 Some(true),
824 Some(false),
825 None,
826 Some(true),
827 None,
828 None,
829 ]),
830 &mut ctx
831 );
832
833 Ok(())
834 }
835
836 #[test]
837 fn test_null_constant_kleene() -> VortexResult<()> {
838 let mut ctx = array_session().create_execution_ctx();
839 let lhs = BoolArray::from_iter([Some(false), Some(true), None]).into_array();
840 let null = ConstantArray::new(Scalar::null(DType::Bool(Nullability::Nullable)), lhs.len())
841 .into_array();
842
843 assert_arrays_eq!(
844 lhs.binary(null.clone(), Operator::And)?,
845 BoolArray::from_iter([Some(false), None, None]),
846 &mut ctx
847 );
848 assert_arrays_eq!(
849 lhs.binary(null, Operator::Or)?,
850 BoolArray::from_iter([None, Some(true), None]),
851 &mut ctx
852 );
853
854 Ok(())
855 }
856
857 #[rstest]
858 #[case(
859 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
860 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
861 )]
862 #[case(
863 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
864 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
865 )]
866 fn test_or(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
867 let r = lhs.binary(rhs, Operator::Or).unwrap();
868 #[expect(deprecated)]
869 let r = r.to_bool().into_array();
870
871 let v0 = r
872 .execute_scalar(0, &mut array_session().create_execution_ctx())
873 .unwrap()
874 .as_bool()
875 .value();
876 let v1 = r
877 .execute_scalar(1, &mut array_session().create_execution_ctx())
878 .unwrap()
879 .as_bool()
880 .value();
881 let v2 = r
882 .execute_scalar(2, &mut array_session().create_execution_ctx())
883 .unwrap()
884 .as_bool()
885 .value();
886 let v3 = r
887 .execute_scalar(3, &mut array_session().create_execution_ctx())
888 .unwrap()
889 .as_bool()
890 .value();
891
892 assert!(v0.unwrap());
893 assert!(v1.unwrap());
894 assert!(v2.unwrap());
895 assert!(!v3.unwrap());
896 }
897
898 #[rstest]
899 #[case(
900 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
901 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
902 )]
903 #[case(
904 BoolArray::from_iter([Some(true), Some(false), Some(true), Some(false)]).into_array(),
905 BoolArray::from_iter([Some(true), Some(true), Some(false), Some(false)]).into_array(),
906 )]
907 fn test_and(#[case] lhs: ArrayRef, #[case] rhs: ArrayRef) {
908 #[expect(deprecated)]
909 let r = lhs
910 .binary(rhs, Operator::And)
911 .unwrap()
912 .to_bool()
913 .into_array();
914
915 let v0 = r
916 .execute_scalar(0, &mut array_session().create_execution_ctx())
917 .unwrap()
918 .as_bool()
919 .value();
920 let v1 = r
921 .execute_scalar(1, &mut array_session().create_execution_ctx())
922 .unwrap()
923 .as_bool()
924 .value();
925 let v2 = r
926 .execute_scalar(2, &mut array_session().create_execution_ctx())
927 .unwrap()
928 .as_bool()
929 .value();
930 let v3 = r
931 .execute_scalar(3, &mut array_session().create_execution_ctx())
932 .unwrap()
933 .as_bool()
934 .value();
935
936 assert!(v0.unwrap());
937 assert!(!v1.unwrap());
938 assert!(!v2.unwrap());
939 assert!(!v3.unwrap());
940 }
941}