vortex_expr/binary.rs
1use std::any::Any;
2use std::fmt::Display;
3use std::hash::Hash;
4use std::sync::Arc;
5
6use vortex_array::compute::{Operator as ArrayOperator, and_kleene, compare, or_kleene};
7use vortex_array::{Array, ArrayRef};
8use vortex_dtype::DType;
9use vortex_error::VortexResult;
10
11use crate::{ExprRef, Operator, VortexExpr};
12
13#[derive(Debug, Clone, Eq, Hash)]
14#[allow(clippy::derived_hash_with_manual_eq)]
15pub struct BinaryExpr {
16 lhs: ExprRef,
17 operator: Operator,
18 rhs: ExprRef,
19}
20
21impl BinaryExpr {
22 pub fn new_expr(lhs: ExprRef, operator: Operator, rhs: ExprRef) -> ExprRef {
23 Arc::new(Self { lhs, operator, rhs })
24 }
25
26 pub fn lhs(&self) -> &ExprRef {
27 &self.lhs
28 }
29
30 pub fn rhs(&self) -> &ExprRef {
31 &self.rhs
32 }
33
34 pub fn op(&self) -> Operator {
35 self.operator
36 }
37}
38
39impl Display for BinaryExpr {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 write!(f, "({} {} {})", self.lhs, self.operator, self.rhs)
42 }
43}
44
45impl VortexExpr for BinaryExpr {
46 fn as_any(&self) -> &dyn Any {
47 self
48 }
49
50 fn unchecked_evaluate(&self, batch: &dyn Array) -> VortexResult<ArrayRef> {
51 let lhs = self.lhs.evaluate(batch)?;
52 let rhs = self.rhs.evaluate(batch)?;
53
54 match self.operator {
55 Operator::Eq => compare(&lhs, &rhs, ArrayOperator::Eq),
56 Operator::NotEq => compare(&lhs, &rhs, ArrayOperator::NotEq),
57 Operator::Lt => compare(&lhs, &rhs, ArrayOperator::Lt),
58 Operator::Lte => compare(&lhs, &rhs, ArrayOperator::Lte),
59 Operator::Gt => compare(&lhs, &rhs, ArrayOperator::Gt),
60 Operator::Gte => compare(&lhs, &rhs, ArrayOperator::Gte),
61 Operator::And => and_kleene(&lhs, &rhs),
62 Operator::Or => or_kleene(&lhs, &rhs),
63 }
64 }
65
66 fn children(&self) -> Vec<&ExprRef> {
67 vec![&self.lhs, &self.rhs]
68 }
69
70 fn replacing_children(self: Arc<Self>, children: Vec<ExprRef>) -> ExprRef {
71 assert_eq!(children.len(), 2);
72 BinaryExpr::new_expr(children[0].clone(), self.operator, children[1].clone())
73 }
74
75 fn return_dtype(&self, scope_dtype: &DType) -> VortexResult<DType> {
76 let lhs = self.lhs.return_dtype(scope_dtype)?;
77 let rhs = self.rhs.return_dtype(scope_dtype)?;
78 Ok(DType::Bool((lhs.is_nullable() || rhs.is_nullable()).into()))
79 }
80}
81
82impl PartialEq for BinaryExpr {
83 fn eq(&self, other: &BinaryExpr) -> bool {
84 other.operator == self.operator && other.lhs.eq(&self.lhs) && other.rhs.eq(&self.rhs)
85 }
86}
87
88/// Create a new `BinaryExpr` using the `Eq` operator.
89///
90/// ## Example usage
91///
92/// ```
93/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
94/// use vortex_array::{Array, IntoArray, ToCanonical};
95/// use vortex_array::validity::Validity;
96/// use vortex_buffer::buffer;
97/// use vortex_expr::{eq, ident, lit};
98///
99/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
100/// let result = eq(ident(), lit(3)).evaluate(&xs).unwrap();
101///
102/// assert_eq!(
103/// result.to_bool().unwrap().boolean_buffer(),
104/// BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
105/// );
106/// ```
107pub fn eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
108 BinaryExpr::new_expr(lhs, Operator::Eq, rhs)
109}
110
111/// Create a new `BinaryExpr` using the `NotEq` operator.
112///
113/// ## Example usage
114///
115/// ```
116/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
117/// use vortex_array::{IntoArray, ToCanonical};
118/// use vortex_array::validity::Validity;
119/// use vortex_buffer::buffer;
120/// use vortex_expr::{ident, lit, not_eq};
121///
122/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
123/// let result = not_eq(ident(), lit(3)).evaluate(&xs).unwrap();
124///
125/// assert_eq!(
126/// result.to_bool().unwrap().boolean_buffer(),
127/// BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
128/// );
129/// ```
130pub fn not_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
131 BinaryExpr::new_expr(lhs, Operator::NotEq, rhs)
132}
133
134/// Create a new `BinaryExpr` using the `Gte` operator.
135///
136/// ## Example usage
137///
138/// ```
139/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
140/// use vortex_array::{IntoArray, ToCanonical};
141/// use vortex_array::validity::Validity;
142/// use vortex_buffer::buffer;
143/// use vortex_expr::{gt_eq, ident, lit};
144///
145/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
146/// let result = gt_eq(ident(), lit(3)).evaluate(&xs).unwrap();
147///
148/// assert_eq!(
149/// result.to_bool().unwrap().boolean_buffer(),
150/// BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
151/// );
152/// ```
153pub fn gt_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
154 BinaryExpr::new_expr(lhs, Operator::Gte, rhs)
155}
156
157/// Create a new `BinaryExpr` using the `Gt` operator.
158///
159/// ## Example usage
160///
161/// ```
162/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
163/// use vortex_array::{IntoArray, ToCanonical};
164/// use vortex_array::validity::Validity;
165/// use vortex_buffer::buffer;
166/// use vortex_expr::{gt, ident, lit};
167///
168/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
169/// let result = gt(ident(), lit(2)).evaluate(&xs).unwrap();
170///
171/// assert_eq!(
172/// result.to_bool().unwrap().boolean_buffer(),
173/// BoolArray::from_iter(vec![false, false, true]).boolean_buffer(),
174/// );
175/// ```
176pub fn gt(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
177 BinaryExpr::new_expr(lhs, Operator::Gt, rhs)
178}
179
180/// Create a new `BinaryExpr` using the `Lte` operator.
181///
182/// ## Example usage
183///
184/// ```
185/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
186/// use vortex_array::{IntoArray, ToCanonical};
187/// use vortex_array::validity::Validity;
188/// use vortex_buffer::buffer;
189/// use vortex_expr::{ident, lit, lt_eq};
190///
191/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
192/// let result = lt_eq(ident(), lit(2)).evaluate(&xs).unwrap();
193///
194/// assert_eq!(
195/// result.to_bool().unwrap().boolean_buffer(),
196/// BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
197/// );
198/// ```
199pub fn lt_eq(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
200 BinaryExpr::new_expr(lhs, Operator::Lte, rhs)
201}
202
203/// Create a new `BinaryExpr` using the `Lt` operator.
204///
205/// ## Example usage
206///
207/// ```
208/// use vortex_array::arrays::{BoolArray, PrimitiveArray };
209/// use vortex_array::{IntoArray, ToCanonical};
210/// use vortex_array::validity::Validity;
211/// use vortex_buffer::buffer;
212/// use vortex_expr::{ident, lit, lt};
213///
214/// let xs = PrimitiveArray::new(buffer![1i32, 2i32, 3i32], Validity::NonNullable);
215/// let result = lt(ident(), lit(3)).evaluate(&xs).unwrap();
216///
217/// assert_eq!(
218/// result.to_bool().unwrap().boolean_buffer(),
219/// BoolArray::from_iter(vec![true, true, false]).boolean_buffer(),
220/// );
221/// ```
222pub fn lt(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
223 BinaryExpr::new_expr(lhs, Operator::Lt, rhs)
224}
225
226/// Create a new `BinaryExpr` using the `Or` operator.
227///
228/// ## Example usage
229///
230/// ```
231/// use vortex_array::arrays::BoolArray;
232/// use vortex_array::{IntoArray, ToCanonical};
233/// use vortex_expr::{ ident, lit, or};
234///
235/// let xs = BoolArray::from_iter(vec![true, false, true]);
236/// let result = or(ident(), lit(false)).evaluate(&xs).unwrap();
237///
238/// assert_eq!(
239/// result.to_bool().unwrap().boolean_buffer(),
240/// BoolArray::from_iter(vec![true, false, true]).boolean_buffer(),
241/// );
242/// ```
243pub fn or(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
244 BinaryExpr::new_expr(lhs, Operator::Or, rhs)
245}
246
247/// Create a new `BinaryExpr` using the `And` operator.
248///
249/// ## Example usage
250///
251/// ```
252/// use vortex_array::arrays::BoolArray;
253/// use vortex_array::{IntoArray, ToCanonical};
254/// use vortex_expr::{and, ident, lit};
255///
256/// let xs = BoolArray::from_iter(vec![true, false, true]);
257/// let result = and(ident(), lit(true)).evaluate(&xs).unwrap();
258///
259/// assert_eq!(
260/// result.to_bool().unwrap().boolean_buffer(),
261/// BoolArray::from_iter(vec![true, false, true]).boolean_buffer(),
262/// );
263/// ```
264pub fn and(lhs: ExprRef, rhs: ExprRef) -> ExprRef {
265 BinaryExpr::new_expr(lhs, Operator::And, rhs)
266}
267
268#[cfg(test)]
269mod tests {
270 use std::sync::Arc;
271
272 use vortex_dtype::{DType, Nullability};
273
274 use crate::{VortexExpr, and, col, eq, gt, gt_eq, lt, lt_eq, not_eq, or, test_harness};
275
276 #[test]
277 fn dtype() {
278 let dtype = test_harness::struct_dtype();
279 let bool1: Arc<dyn VortexExpr> = col("bool1");
280 let bool2: Arc<dyn VortexExpr> = col("bool2");
281 assert_eq!(
282 and(bool1.clone(), bool2.clone())
283 .return_dtype(&dtype)
284 .unwrap(),
285 DType::Bool(Nullability::NonNullable)
286 );
287 assert_eq!(
288 or(bool1.clone(), bool2.clone())
289 .return_dtype(&dtype)
290 .unwrap(),
291 DType::Bool(Nullability::NonNullable)
292 );
293
294 let col1: Arc<dyn VortexExpr> = col("col1");
295 let col2: Arc<dyn VortexExpr> = col("col2");
296
297 assert_eq!(
298 eq(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
299 DType::Bool(Nullability::Nullable)
300 );
301 assert_eq!(
302 not_eq(col1.clone(), col2.clone())
303 .return_dtype(&dtype)
304 .unwrap(),
305 DType::Bool(Nullability::Nullable)
306 );
307 assert_eq!(
308 gt(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
309 DType::Bool(Nullability::Nullable)
310 );
311 assert_eq!(
312 gt_eq(col1.clone(), col2.clone())
313 .return_dtype(&dtype)
314 .unwrap(),
315 DType::Bool(Nullability::Nullable)
316 );
317 assert_eq!(
318 lt(col1.clone(), col2.clone()).return_dtype(&dtype).unwrap(),
319 DType::Bool(Nullability::Nullable)
320 );
321 assert_eq!(
322 lt_eq(col1.clone(), col2.clone())
323 .return_dtype(&dtype)
324 .unwrap(),
325 DType::Bool(Nullability::Nullable)
326 );
327
328 assert_eq!(
329 or(
330 lt(col1.clone(), col2.clone()),
331 not_eq(col1.clone(), col2.clone())
332 )
333 .return_dtype(&dtype)
334 .unwrap(),
335 DType::Bool(Nullability::Nullable)
336 );
337 }
338}