Skip to main content

vortex_array/scalar_fn/fns/
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_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    // TODO(joe): rename to KleeneAnd
33    And,
34    /// Boolean OR (∨).
35    // TODO(joe): rename to KleeneOr
36    Or,
37    /// The sum of the arguments.
38    ///
39    /// Errs at runtime if the sum would overflow or underflow.
40    Add,
41    /// The difference between the arguments.
42    ///
43    /// Errs at runtime if the sum would overflow or underflow.
44    ///
45    /// The result is null at any index that either input is null.
46    Sub,
47    /// Multiple two numbers
48    Mul,
49    /// Divide the left side by the right side
50    Div,
51}
52
53impl From<Operator> for i32 {
54    fn from(value: Operator) -> Self {
55        let op: BinaryOp = value.into();
56        op.into()
57    }
58}
59
60impl From<Operator> for BinaryOp {
61    fn from(value: Operator) -> Self {
62        match value {
63            Operator::Eq => BinaryOp::Eq,
64            Operator::NotEq => BinaryOp::NotEq,
65            Operator::Gt => BinaryOp::Gt,
66            Operator::Gte => BinaryOp::Gte,
67            Operator::Lt => BinaryOp::Lt,
68            Operator::Lte => BinaryOp::Lte,
69            Operator::And => BinaryOp::And,
70            Operator::Or => BinaryOp::Or,
71            Operator::Add => BinaryOp::Add,
72            Operator::Sub => BinaryOp::Sub,
73            Operator::Mul => BinaryOp::Mul,
74            Operator::Div => BinaryOp::Div,
75        }
76    }
77}
78
79impl TryFrom<i32> for Operator {
80    type Error = VortexError;
81
82    fn try_from(value: i32) -> Result<Self, Self::Error> {
83        Ok(BinaryOp::try_from(value)?.into())
84    }
85}
86
87impl From<BinaryOp> for Operator {
88    fn from(value: BinaryOp) -> Self {
89        match value {
90            BinaryOp::Eq => Operator::Eq,
91            BinaryOp::NotEq => Operator::NotEq,
92            BinaryOp::Gt => Operator::Gt,
93            BinaryOp::Gte => Operator::Gte,
94            BinaryOp::Lt => Operator::Lt,
95            BinaryOp::Lte => Operator::Lte,
96            BinaryOp::And => Operator::And,
97            BinaryOp::Or => Operator::Or,
98            BinaryOp::Add => Operator::Add,
99            BinaryOp::Sub => Operator::Sub,
100            BinaryOp::Mul => Operator::Mul,
101            BinaryOp::Div => Operator::Div,
102        }
103    }
104}
105
106impl Display for Operator {
107    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
108        let display = match &self {
109            Operator::Eq => "=",
110            Operator::NotEq => "!=",
111            Operator::Gt => ">",
112            Operator::Gte => ">=",
113            Operator::Lt => "<",
114            Operator::Lte => "<=",
115            Operator::And => "and",
116            Operator::Or => "or",
117            Operator::Add => "+",
118            Operator::Sub => "-",
119            Operator::Mul => "*",
120            Operator::Div => "/",
121        };
122        Display::fmt(display, f)
123    }
124}
125
126impl Operator {
127    pub fn inverse(self) -> Option<Self> {
128        match self {
129            Operator::Eq => Some(Operator::NotEq),
130            Operator::NotEq => Some(Operator::Eq),
131            Operator::Gt => Some(Operator::Lte),
132            Operator::Gte => Some(Operator::Lt),
133            Operator::Lt => Some(Operator::Gte),
134            Operator::Lte => Some(Operator::Gt),
135            Operator::And
136            | Operator::Or
137            | Operator::Add
138            | Operator::Sub
139            | Operator::Mul
140            | Operator::Div => None,
141        }
142    }
143
144    pub fn logical_inverse(self) -> Option<Self> {
145        match self {
146            Operator::And => Some(Operator::Or),
147            Operator::Or => Some(Operator::And),
148            _ => None,
149        }
150    }
151
152    /// Change the sides of the operator, so that changing lhs and rhs won't change the result of the operation
153    pub fn swap(self) -> Option<Self> {
154        match self {
155            Operator::Eq => Some(Operator::Eq),
156            Operator::NotEq => Some(Operator::NotEq),
157            Operator::Gt => Some(Operator::Lt),
158            Operator::Gte => Some(Operator::Lte),
159            Operator::Lt => Some(Operator::Gt),
160            Operator::Lte => Some(Operator::Gte),
161            Operator::And => Some(Operator::And),
162            Operator::Or => Some(Operator::Or),
163            Operator::Add => Some(Operator::Add),
164            Operator::Mul => Some(Operator::Mul),
165            Operator::Sub | Operator::Div => None,
166        }
167    }
168
169    pub fn is_arithmetic(&self) -> bool {
170        matches!(self, Self::Add | Self::Sub | Self::Mul | Self::Div)
171    }
172
173    pub fn is_comparison(&self) -> bool {
174        matches!(
175            self,
176            Self::Eq | Self::NotEq | Self::Gt | Self::Gte | Self::Lt | Self::Lte
177        )
178    }
179}
180
181/// The six comparison operators, providing compile-time guarantees that only
182/// comparison variants are used where comparisons are expected.
183#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Hash)]
184#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
185pub enum CompareOperator {
186    /// Expressions are equal.
187    Eq,
188    /// Expressions are not equal.
189    NotEq,
190    /// Expression is greater than another.
191    Gt,
192    /// Expression is greater or equal to another.
193    Gte,
194    /// Expression is less than another.
195    Lt,
196    /// Expression is less or equal to another.
197    Lte,
198}
199
200impl CompareOperator {
201    /// Return the logical inverse of this comparison operator.
202    pub fn inverse(self) -> Self {
203        match self {
204            CompareOperator::Eq => CompareOperator::NotEq,
205            CompareOperator::NotEq => CompareOperator::Eq,
206            CompareOperator::Gt => CompareOperator::Lte,
207            CompareOperator::Gte => CompareOperator::Lt,
208            CompareOperator::Lt => CompareOperator::Gte,
209            CompareOperator::Lte => CompareOperator::Gt,
210        }
211    }
212
213    /// Swap the sides of the operator so that swapping lhs and rhs preserves the result.
214    pub fn swap(self) -> Self {
215        match self {
216            CompareOperator::Eq => CompareOperator::Eq,
217            CompareOperator::NotEq => CompareOperator::NotEq,
218            CompareOperator::Gt => CompareOperator::Lt,
219            CompareOperator::Gte => CompareOperator::Lte,
220            CompareOperator::Lt => CompareOperator::Gt,
221            CompareOperator::Lte => CompareOperator::Gte,
222        }
223    }
224}
225
226impl Display for CompareOperator {
227    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
228        let display = match self {
229            CompareOperator::Eq => "=",
230            CompareOperator::NotEq => "!=",
231            CompareOperator::Gt => ">",
232            CompareOperator::Gte => ">=",
233            CompareOperator::Lt => "<",
234            CompareOperator::Lte => "<=",
235        };
236        Display::fmt(display, f)
237    }
238}
239
240impl From<CompareOperator> for Operator {
241    fn from(value: CompareOperator) -> Self {
242        match value {
243            CompareOperator::Eq => Operator::Eq,
244            CompareOperator::NotEq => Operator::NotEq,
245            CompareOperator::Gt => Operator::Gt,
246            CompareOperator::Gte => Operator::Gte,
247            CompareOperator::Lt => Operator::Lt,
248            CompareOperator::Lte => Operator::Lte,
249        }
250    }
251}
252
253impl TryFrom<Operator> for CompareOperator {
254    type Error = VortexError;
255
256    fn try_from(value: Operator) -> Result<Self, Self::Error> {
257        match value {
258            Operator::Eq => Ok(CompareOperator::Eq),
259            Operator::NotEq => Ok(CompareOperator::NotEq),
260            Operator::Gt => Ok(CompareOperator::Gt),
261            Operator::Gte => Ok(CompareOperator::Gte),
262            Operator::Lt => Ok(CompareOperator::Lt),
263            Operator::Lte => Ok(CompareOperator::Lte),
264            other => Err(vortex_error::vortex_err!(
265                InvalidArgument: "{other} is not a comparison operator"
266            )),
267        }
268    }
269}