Skip to main content

vortex_array/expr/exprs/
operators.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use core::fmt;
5use std::fmt::Display;
6use std::fmt::Formatter;
7
8use vortex_error::VortexError;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_proto::expr::binary_opts::BinaryOp;
12
13use crate::compute;
14
15/// Equalities, inequalities, and boolean operations over possibly null values.
16///
17/// For most operations, if either side is null, the result is null.
18///
19/// The Boolean operators (And, Or) obey [Kleene (three-valued) logic](https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics).
20#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub enum Operator {
23    /// Expressions are equal.
24    Eq,
25    /// Expressions are not equal.
26    NotEq,
27    /// Expression is greater than another
28    Gt,
29    /// Expression is greater or equal to another
30    Gte,
31    /// Expression is less than another
32    Lt,
33    /// Expression is less or equal to another
34    Lte,
35    /// Boolean AND (∧).
36    And,
37    /// Boolean OR (∨).
38    // TODO(joe): rename to KleeneOr
39    Or,
40    /// The sum of the arguments.
41    ///
42    /// Errs at runtime if the sum would overflow or underflow.
43    // TODO(joe): rename to KleeneAnd
44    Add,
45    /// The difference between the arguments.
46    ///
47    /// Errs at runtime if the sum would overflow or underflow.
48    ///
49    /// The result is null at any index that either input is null.
50    Sub,
51    /// Multiple two numbers
52    Mul,
53    /// Divide the left side by the right side
54    Div,
55}
56
57impl From<Operator> for i32 {
58    fn from(value: Operator) -> Self {
59        let op: BinaryOp = value.into();
60        op.into()
61    }
62}
63
64impl From<Operator> for BinaryOp {
65    fn from(value: Operator) -> Self {
66        match value {
67            Operator::Eq => BinaryOp::Eq,
68            Operator::NotEq => BinaryOp::NotEq,
69            Operator::Gt => BinaryOp::Gt,
70            Operator::Gte => BinaryOp::Gte,
71            Operator::Lt => BinaryOp::Lt,
72            Operator::Lte => BinaryOp::Lte,
73            Operator::And => BinaryOp::And,
74            Operator::Or => BinaryOp::Or,
75            Operator::Add => BinaryOp::Add,
76            Operator::Sub => BinaryOp::Sub,
77            Operator::Mul => BinaryOp::Mul,
78            Operator::Div => BinaryOp::Div,
79        }
80    }
81}
82
83impl TryFrom<i32> for Operator {
84    type Error = VortexError;
85
86    fn try_from(value: i32) -> Result<Self, Self::Error> {
87        Ok(BinaryOp::try_from(value)?.into())
88    }
89}
90
91impl From<BinaryOp> for Operator {
92    fn from(value: BinaryOp) -> Self {
93        match value {
94            BinaryOp::Eq => Operator::Eq,
95            BinaryOp::NotEq => Operator::NotEq,
96            BinaryOp::Gt => Operator::Gt,
97            BinaryOp::Gte => Operator::Gte,
98            BinaryOp::Lt => Operator::Lt,
99            BinaryOp::Lte => Operator::Lte,
100            BinaryOp::And => Operator::And,
101            BinaryOp::Or => Operator::Or,
102            BinaryOp::Add => Operator::Add,
103            BinaryOp::Sub => Operator::Sub,
104            BinaryOp::Mul => Operator::Mul,
105            BinaryOp::Div => Operator::Div,
106        }
107    }
108}
109
110impl Display for Operator {
111    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
112        let display = match &self {
113            Operator::Eq => "=",
114            Operator::NotEq => "!=",
115            Operator::Gt => ">",
116            Operator::Gte => ">=",
117            Operator::Lt => "<",
118            Operator::Lte => "<=",
119            Operator::And => "and",
120            Operator::Or => "or",
121            Operator::Add => "+",
122            Operator::Sub => "-",
123            Operator::Mul => "*",
124            Operator::Div => "/",
125        };
126        Display::fmt(display, f)
127    }
128}
129
130impl Operator {
131    pub fn inverse(self) -> Option<Self> {
132        match self {
133            Operator::Eq => Some(Operator::NotEq),
134            Operator::NotEq => Some(Operator::Eq),
135            Operator::Gt => Some(Operator::Lte),
136            Operator::Gte => Some(Operator::Lt),
137            Operator::Lt => Some(Operator::Gte),
138            Operator::Lte => Some(Operator::Gt),
139            Operator::And
140            | Operator::Or
141            | Operator::Add
142            | Operator::Sub
143            | Operator::Mul
144            | Operator::Div => None,
145        }
146    }
147
148    pub fn logical_inverse(self) -> Option<Self> {
149        match self {
150            Operator::And => Some(Operator::Or),
151            Operator::Or => Some(Operator::And),
152            _ => None,
153        }
154    }
155
156    /// Change the sides of the operator, so that changing lhs and rhs won't change the result of the operation
157    pub fn swap(self) -> Option<Self> {
158        match self {
159            Operator::Eq => Some(Operator::Eq),
160            Operator::NotEq => Some(Operator::NotEq),
161            Operator::Gt => Some(Operator::Lt),
162            Operator::Gte => Some(Operator::Lte),
163            Operator::Lt => Some(Operator::Gt),
164            Operator::Lte => Some(Operator::Gte),
165            Operator::And => Some(Operator::And),
166            Operator::Or => Some(Operator::Or),
167            Operator::Add => Some(Operator::Add),
168            Operator::Mul => Some(Operator::Mul),
169            Operator::Sub | Operator::Div => None,
170        }
171    }
172
173    pub fn maybe_cmp_operator(self) -> Option<compute::Operator> {
174        match self {
175            Operator::Eq => Some(compute::Operator::Eq),
176            Operator::NotEq => Some(compute::Operator::NotEq),
177            Operator::Lt => Some(compute::Operator::Lt),
178            Operator::Lte => Some(compute::Operator::Lte),
179            Operator::Gt => Some(compute::Operator::Gt),
180            Operator::Gte => Some(compute::Operator::Gte),
181            _ => None,
182        }
183    }
184
185    pub fn is_arithmetic(&self) -> bool {
186        matches!(self, Self::Add | Self::Sub | Self::Mul | Self::Div)
187    }
188
189    pub fn is_comparison(&self) -> bool {
190        matches!(
191            self,
192            Self::Eq | Self::NotEq | Self::Gt | Self::Gte | Self::Lt | Self::Lte
193        )
194    }
195}
196
197impl From<compute::Operator> for Operator {
198    fn from(cmp_operator: compute::Operator) -> Self {
199        match cmp_operator {
200            compute::Operator::Eq => Operator::Eq,
201            compute::Operator::NotEq => Operator::NotEq,
202            compute::Operator::Gt => Operator::Gt,
203            compute::Operator::Gte => Operator::Gte,
204            compute::Operator::Lt => Operator::Lt,
205            compute::Operator::Lte => Operator::Lte,
206        }
207    }
208}
209
210impl TryInto<compute::Operator> for Operator {
211    type Error = VortexError;
212
213    fn try_into(self) -> VortexResult<compute::Operator> {
214        Ok(match self {
215            Operator::Eq => compute::Operator::Eq,
216            Operator::NotEq => compute::Operator::NotEq,
217            Operator::Gt => compute::Operator::Gt,
218            Operator::Gte => compute::Operator::Gte,
219            Operator::Lt => compute::Operator::Lt,
220            Operator::Lte => compute::Operator::Lte,
221            _ => vortex_bail!("Not a compute operator: {}", self),
222        })
223    }
224}
225
226impl TryFrom<compute::BooleanOperator> for Operator {
227    type Error = VortexError;
228
229    fn try_from(value: compute::BooleanOperator) -> VortexResult<Self> {
230        match value {
231            compute::BooleanOperator::AndKleene => Ok(Operator::And),
232            compute::BooleanOperator::OrKleene => Ok(Operator::Or),
233            other => vortex_bail!(
234                "Non-Kleene boolean operator {other:?} cannot be represented as an expression Operator"
235            ),
236        }
237    }
238}