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