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