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