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