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