1use num_traits::AsPrimitive;
5use vortex_array::ArrayRef;
6use vortex_array::ArrayView;
7use vortex_array::ExecutionCtx;
8use vortex_array::IntoArray;
9use vortex_array::arrays::Constant;
10use vortex_array::arrays::PrimitiveArray;
11use vortex_array::arrays::dict::TakeExecute;
12use vortex_array::buffer::BufferHandle;
13use vortex_array::dtype::DType;
14use vortex_array::match_each_integer_ptype;
15use vortex_array::scalar_fn::fns::binary::BooleanKernel;
16use vortex_array::scalar_fn::fns::binary::kleene_boolean_buffer_scalar;
17use vortex_array::scalar_fn::fns::binary::kleene_boolean_buffers;
18use vortex_array::scalar_fn::fns::cast::CastKernel;
19use vortex_array::scalar_fn::fns::cast::CastReduce;
20use vortex_array::scalar_fn::fns::mask::MaskReduce;
21use vortex_array::scalar_fn::fns::operators::Operator;
22use vortex_array::validity::Validity;
23use vortex_buffer::BitBuffer;
24use vortex_buffer::ByteBuffer;
25use vortex_error::VortexResult;
26use vortex_error::vortex_err;
27
28use super::ByteBool;
29
30impl CastReduce for ByteBool {
31 fn cast(array: ArrayView<'_, Self>, dtype: &DType) -> VortexResult<Option<ArrayRef>> {
32 if !dtype.is_boolean() {
37 return Ok(None);
38 }
39
40 let Some(new_validity) = array
41 .validity()?
42 .trivially_cast_nullability(dtype.nullability(), array.len())?
43 else {
44 return Ok(None);
45 };
46
47 Ok(Some(
48 ByteBool::new(array.buffer().clone(), new_validity).into_array(),
49 ))
50 }
51}
52
53impl CastKernel for ByteBool {
54 fn cast(
55 array: ArrayView<'_, Self>,
56 dtype: &DType,
57 ctx: &mut ExecutionCtx,
58 ) -> VortexResult<Option<ArrayRef>> {
59 if !dtype.is_boolean() {
61 return Ok(None);
62 }
63
64 let new_validity =
65 array
66 .validity()?
67 .cast_nullability(dtype.nullability(), array.len(), ctx)?;
68
69 Ok(Some(
70 ByteBool::new(array.buffer().clone(), new_validity).into_array(),
71 ))
72 }
73}
74
75impl MaskReduce for ByteBool {
76 fn mask(array: ArrayView<'_, Self>, mask: &ArrayRef) -> VortexResult<Option<ArrayRef>> {
77 Ok(Some(
78 ByteBool::new(
79 array.buffer().clone(),
80 array.validity()?.and(Validity::Array(mask.clone()))?,
81 )
82 .into_array(),
83 ))
84 }
85}
86
87impl TakeExecute for ByteBool {
88 fn take(
89 array: ArrayView<'_, Self>,
90 indices: &ArrayRef,
91 ctx: &mut ExecutionCtx,
92 ) -> VortexResult<Option<ArrayRef>> {
93 let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
94 let values = array.truthy_bytes();
95
96 let validity = array.validity()?.take(&indices.clone().into_array())?;
98
99 let taken = match_each_integer_ptype!(indices.ptype(), |I| {
100 indices
101 .as_slice::<I>()
102 .iter()
103 .map(|&idx| {
104 let idx: usize = idx.as_();
105 values[idx]
106 })
107 .collect::<ByteBuffer>()
108 });
109
110 Ok(Some(
111 ByteBool::new(BufferHandle::new_host(taken), validity).into_array(),
112 ))
113 }
114}
115
116impl BooleanKernel for ByteBool {
117 fn boolean(
118 lhs: ArrayView<'_, Self>,
119 rhs: &ArrayRef,
120 operator: Operator,
121 ctx: &mut ExecutionCtx,
122 ) -> VortexResult<Option<ArrayRef>> {
123 let nullability = lhs.dtype().nullability() | rhs.dtype().nullability();
124 let lhs_values = truthy_bit_buffer(lhs);
125
126 if let Some(rhs) = rhs.as_opt::<Constant>() {
127 let rhs = rhs
128 .scalar()
129 .as_bool_opt()
130 .ok_or_else(|| vortex_err!("expected boolean scalar"))?;
131 return kleene_boolean_buffer_scalar(
132 lhs_values,
133 lhs.validity()?,
134 &rhs,
135 operator,
136 nullability,
137 ctx,
138 )
139 .map(Some);
140 }
141
142 let Some(rhs) = rhs.as_opt::<ByteBool>() else {
143 return Ok(None);
144 };
145
146 kleene_boolean_buffers(
147 lhs_values,
148 lhs.validity()?,
149 truthy_bit_buffer(rhs),
150 rhs.validity()?,
151 operator,
152 nullability,
153 ctx,
154 )
155 .map(Some)
156 }
157}
158
159fn truthy_bit_buffer(array: ArrayView<'_, ByteBool>) -> BitBuffer {
160 let bytes = array.truthy_bytes();
161 BitBuffer::collect_bool(bytes.len(), |idx| bytes[idx] != 0)
162}
163
164#[cfg(test)]
165mod tests {
166 use std::sync::LazyLock;
167
168 use rstest::rstest;
169 use vortex_array::VortexSessionExecute;
170 use vortex_array::array_session;
171 use vortex_array::arrays::BoolArray;
172 use vortex_array::assert_arrays_eq;
173 use vortex_array::builtins::ArrayBuiltins;
174 use vortex_array::compute::conformance::cast::test_cast_conformance;
175 use vortex_array::compute::conformance::consistency::test_array_consistency;
176 use vortex_array::compute::conformance::filter::test_filter_conformance;
177 use vortex_array::compute::conformance::mask::test_mask_conformance;
178 use vortex_array::compute::conformance::take::test_take_conformance;
179 use vortex_array::dtype::DType;
180 use vortex_array::dtype::Nullability;
181 use vortex_array::scalar_fn::fns::operators::Operator;
182 use vortex_error::vortex_err;
183 use vortex_session::VortexSession;
184
185 use super::*;
186 use crate::ByteBoolArray;
187
188 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
189 let session = array_session();
190 crate::initialize(&session);
191 session
192 });
193
194 fn bb(v: Vec<bool>) -> ByteBoolArray {
195 ByteBool::from_vec(v, Validity::AllValid)
196 }
197
198 fn bb_opt(v: Vec<Option<bool>>) -> ByteBoolArray {
199 ByteBool::from_option_vec(v)
200 }
201
202 #[test]
203 fn test_slice() {
204 let original = vec![Some(true), Some(true), None, Some(false), None];
205 let vortex_arr = bb_opt(original);
206
207 let sliced_arr = vortex_arr.slice(1..4).unwrap();
208
209 let expected = bb_opt(vec![Some(true), None, Some(false)]);
210 assert_arrays_eq!(
211 sliced_arr,
212 expected.into_array(),
213 &mut SESSION.create_execution_ctx()
214 );
215 }
216
217 #[test]
218 fn test_compare_all_equal() {
219 let lhs = bb(vec![true; 5]);
220 let rhs = bb(vec![true; 5]);
221
222 let arr = lhs
223 .into_array()
224 .binary(rhs.into_array(), Operator::Eq)
225 .unwrap();
226
227 let expected = bb(vec![true; 5]);
228 assert_arrays_eq!(
229 arr,
230 expected.into_array(),
231 &mut SESSION.create_execution_ctx()
232 );
233 }
234
235 #[test]
236 fn test_compare_all_different() {
237 let lhs = bb(vec![false; 5]);
238 let rhs = bb(vec![true; 5]);
239
240 let arr = lhs
241 .into_array()
242 .binary(rhs.into_array(), Operator::Eq)
243 .unwrap();
244
245 let expected = bb(vec![false; 5]);
246 assert_arrays_eq!(
247 arr,
248 expected.into_array(),
249 &mut SESSION.create_execution_ctx()
250 );
251 }
252
253 #[test]
254 fn test_compare_with_nulls() {
255 let lhs = bb(vec![true; 5]);
256 let rhs = bb_opt(vec![Some(true), Some(true), Some(true), Some(false), None]);
257
258 let arr = lhs
259 .into_array()
260 .binary(rhs.into_array(), Operator::Eq)
261 .unwrap();
262
263 let expected = bb_opt(vec![Some(true), Some(true), Some(true), Some(false), None]);
264 assert_arrays_eq!(
265 arr,
266 expected.into_array(),
267 &mut SESSION.create_execution_ctx()
268 );
269 }
270
271 #[test]
272 fn test_boolean_kernel_kleene() -> VortexResult<()> {
273 let lhs = bb_opt(vec![Some(false), Some(true), None, Some(false), None]);
274 let rhs = bb_opt(vec![None, None, Some(true), Some(false), None]).into_array();
275 let mut ctx = SESSION.create_execution_ctx();
276
277 let and_result =
278 <ByteBool as BooleanKernel>::boolean(lhs.as_view(), &rhs, Operator::And, &mut ctx)?
279 .ok_or_else(|| vortex_err!("ByteBool should handle ByteBool boolean AND"))?;
280 assert_arrays_eq!(
281 and_result,
282 BoolArray::from_iter([Some(false), None, None, Some(false), None]),
283 &mut ctx
284 );
285
286 let or_result =
287 <ByteBool as BooleanKernel>::boolean(lhs.as_view(), &rhs, Operator::Or, &mut ctx)?
288 .ok_or_else(|| vortex_err!("ByteBool should handle ByteBool boolean OR"))?;
289 assert_arrays_eq!(
290 or_result,
291 BoolArray::from_iter([None, Some(true), Some(true), Some(false), None]),
292 &mut ctx
293 );
294
295 Ok(())
296 }
297
298 #[test]
299 fn test_mask_byte_bool() {
300 test_mask_conformance(&bb(vec![true, false, true, true, false]).into_array());
301 test_mask_conformance(
302 &bb_opt(vec![Some(true), Some(true), None, Some(false), None]).into_array(),
303 );
304 }
305
306 #[test]
307 fn test_filter_byte_bool() {
308 test_filter_conformance(&bb(vec![true, false, true, true, false]).into_array());
309 test_filter_conformance(
310 &bb_opt(vec![Some(true), Some(true), None, Some(false), None]).into_array(),
311 );
312 }
313
314 #[rstest]
315 #[case(bb(vec![true, false, true, true, false]))]
316 #[case(bb_opt(vec![Some(true), Some(true), None, Some(false), None]))]
317 #[case(bb(vec![true, false]))]
318 #[case(bb(vec![true]))]
319 fn test_take_byte_bool_conformance(#[case] array: ByteBoolArray) {
320 test_take_conformance(&array.into_array());
321 }
322
323 #[test]
324 fn test_cast_bytebool_to_nullable() {
325 let array = bb(vec![true, false, true, false]);
326 let casted = array
327 .into_array()
328 .cast(DType::Bool(Nullability::Nullable))
329 .unwrap();
330 assert_eq!(casted.dtype(), &DType::Bool(Nullability::Nullable));
331 assert_eq!(casted.len(), 4);
332 }
333
334 #[rstest]
335 #[case(bb(vec![true, false, true, true, false]))]
336 #[case(bb_opt(vec![Some(true), Some(false), None, Some(true), None]))]
337 #[case(bb(vec![false]))]
338 #[case(bb(vec![true]))]
339 #[case(bb_opt(vec![Some(true), None]))]
340 fn test_cast_bytebool_conformance(#[case] array: ByteBoolArray) {
341 test_cast_conformance(&array.into_array());
342 }
343
344 #[rstest]
345 #[case::non_nullable(bb(vec![true, false, true, true, false]))]
346 #[case::nullable(bb_opt(vec![Some(true), Some(false), None, Some(true), None]))]
347 #[case::all_true(bb(vec![true, true, true, true]))]
348 #[case::all_false(bb(vec![false, false, false, false]))]
349 #[case::single_true(bb(vec![true]))]
350 #[case::single_false(bb(vec![false]))]
351 #[case::single_null(bb_opt(vec![None]))]
352 #[case::mixed_with_nulls(bb_opt(vec![Some(true), None, Some(false), None, Some(true)]))]
353 fn test_bytebool_consistency(#[case] array: ByteBoolArray) {
354 let ctx = &mut array_session().create_execution_ctx();
355 test_array_consistency(&array.into_array(), ctx);
356 }
357}