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    Or,
39    /// The sum of the arguments.
40    ///
41    /// Errs at runtime if the sum would overflow or underflow.
42    Add,
43    /// The difference between the arguments.
44    ///
45    /// Errs at runtime if the sum would overflow or underflow.
46    ///
47    /// The result is null at any index that either input is null.
48    Sub,
49    /// Multiple two numbers
50    Mul,
51    /// Divide the left side by the right side
52    Div,
53}
54
55impl From<Operator> for i32 {
56    fn from(value: Operator) -> Self {
57        let op: BinaryOp = value.into();
58        op.into()
59    }
60}
61
62impl From<Operator> for BinaryOp {
63    fn from(value: Operator) -> Self {
64        match value {
65            Operator::Eq => BinaryOp::Eq,
66            Operator::NotEq => BinaryOp::NotEq,
67            Operator::Gt => BinaryOp::Gt,
68            Operator::Gte => BinaryOp::Gte,
69            Operator::Lt => BinaryOp::Lt,
70            Operator::Lte => BinaryOp::Lte,
71            Operator::And => BinaryOp::And,
72            Operator::Or => BinaryOp::Or,
73            Operator::Add => BinaryOp::Add,
74            Operator::Sub => BinaryOp::Sub,
75            Operator::Mul => BinaryOp::Mul,
76            Operator::Div => BinaryOp::Div,
77        }
78    }
79}
80
81impl TryFrom<i32> for Operator {
82    type Error = VortexError;
83
84    fn try_from(value: i32) -> Result<Self, Self::Error> {
85        Ok(BinaryOp::try_from(value)?.into())
86    }
87}
88
89impl From<BinaryOp> for Operator {
90    fn from(value: BinaryOp) -> Self {
91        match value {
92            BinaryOp::Eq => Operator::Eq,
93            BinaryOp::NotEq => Operator::NotEq,
94            BinaryOp::Gt => Operator::Gt,
95            BinaryOp::Gte => Operator::Gte,
96            BinaryOp::Lt => Operator::Lt,
97            BinaryOp::Lte => Operator::Lte,
98            BinaryOp::And => Operator::And,
99            BinaryOp::Or => Operator::Or,
100            BinaryOp::Add => Operator::Add,
101            BinaryOp::Sub => Operator::Sub,
102            BinaryOp::Mul => Operator::Mul,
103            BinaryOp::Div => Operator::Div,
104        }
105    }
106}
107
108impl Display for Operator {
109    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
110        let display = match &self {
111            Operator::Eq => "=",
112            Operator::NotEq => "!=",
113            Operator::Gt => ">",
114            Operator::Gte => ">=",
115            Operator::Lt => "<",
116            Operator::Lte => "<=",
117            Operator::And => "and",
118            Operator::Or => "or",
119            Operator::Add => "+",
120            Operator::Sub => "-",
121            Operator::Mul => "*",
122            Operator::Div => "/",
123        };
124        Display::fmt(display, f)
125    }
126}
127
128impl Operator {
129    pub fn inverse(self) -> Option<Self> {
130        match self {
131            Operator::Eq => Some(Operator::NotEq),
132            Operator::NotEq => Some(Operator::Eq),
133            Operator::Gt => Some(Operator::Lte),
134            Operator::Gte => Some(Operator::Lt),
135            Operator::Lt => Some(Operator::Gte),
136            Operator::Lte => Some(Operator::Gt),
137            Operator::And
138            | Operator::Or
139            | Operator::Add
140            | Operator::Sub
141            | Operator::Mul
142            | Operator::Div => None,
143        }
144    }
145
146    pub fn logical_inverse(self) -> Option<Self> {
147        match self {
148            Operator::And => Some(Operator::Or),
149            Operator::Or => Some(Operator::And),
150            _ => None,
151        }
152    }
153
154    /// Change the sides of the operator, so that changing lhs and rhs won't change the result of the operation
155    pub fn swap(self) -> Option<Self> {
156        match self {
157            Operator::Eq => Some(Operator::Eq),
158            Operator::NotEq => Some(Operator::NotEq),
159            Operator::Gt => Some(Operator::Lt),
160            Operator::Gte => Some(Operator::Lte),
161            Operator::Lt => Some(Operator::Gt),
162            Operator::Lte => Some(Operator::Gte),
163            Operator::And => Some(Operator::And),
164            Operator::Or => Some(Operator::Or),
165            Operator::Add => Some(Operator::Add),
166            Operator::Mul => Some(Operator::Mul),
167            Operator::Sub | Operator::Div => None,
168        }
169    }
170
171    pub fn maybe_cmp_operator(self) -> Option<compute::Operator> {
172        match self {
173            Operator::Eq => Some(compute::Operator::Eq),
174            Operator::NotEq => Some(compute::Operator::NotEq),
175            Operator::Lt => Some(compute::Operator::Lt),
176            Operator::Lte => Some(compute::Operator::Lte),
177            Operator::Gt => Some(compute::Operator::Gt),
178            Operator::Gte => Some(compute::Operator::Gte),
179            _ => None,
180        }
181    }
182
183    pub fn is_arithmetic(&self) -> bool {
184        matches!(self, Self::Add | Self::Sub | Self::Mul | Self::Div)
185    }
186}
187
188impl From<compute::Operator> for Operator {
189    fn from(cmp_operator: compute::Operator) -> Self {
190        match cmp_operator {
191            compute::Operator::Eq => Operator::Eq,
192            compute::Operator::NotEq => Operator::NotEq,
193            compute::Operator::Gt => Operator::Gt,
194            compute::Operator::Gte => Operator::Gte,
195            compute::Operator::Lt => Operator::Lt,
196            compute::Operator::Lte => Operator::Lte,
197        }
198    }
199}
200
201impl TryInto<compute::Operator> for Operator {
202    type Error = VortexError;
203
204    fn try_into(self) -> VortexResult<compute::Operator> {
205        Ok(match self {
206            Operator::Eq => compute::Operator::Eq,
207            Operator::NotEq => compute::Operator::NotEq,
208            Operator::Gt => compute::Operator::Gt,
209            Operator::Gte => compute::Operator::Gte,
210            Operator::Lt => compute::Operator::Lt,
211            Operator::Lte => compute::Operator::Lte,
212            _ => vortex_bail!("Not a compute operator: {}", self),
213        })
214    }
215}