1use reifydb_core::interface::ColumnSaturationPolicy;
5use reifydb_type::{
6 Error, GetType, IsNumber, LazyFragment, Promote, SafeAdd, SafeDiv, SafeMul, SafeRemainder, SafeSub,
7 diagnostic::number::number_out_of_range, return_error,
8};
9
10use crate::evaluate::ColumnEvaluationContext;
11
12impl ColumnEvaluationContext<'_> {
13 pub fn add<'a, L, R>(
14 &self,
15 l: &L,
16 r: &R,
17 fragment: impl LazyFragment<'a> + Copy,
18 ) -> reifydb_core::Result<Option<<L as Promote<R>>::Output>>
19 where
20 L: Promote<R>,
21 R: IsNumber,
22 <L as Promote<R>>::Output: IsNumber,
23 <L as Promote<R>>::Output: SafeAdd,
24 {
25 match &self.saturation_policy() {
26 ColumnSaturationPolicy::Error => {
27 let Some((lp, rp)) = l.checked_promote(r) else {
28 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
29 return_error!(number_out_of_range(
30 fragment.fragment(),
31 <L as Promote<R>>::Output::get_type(),
32 descriptor.as_ref(),
33 ));
34 };
35
36 lp.checked_add(&rp)
37 .ok_or_else(|| {
38 let descriptor =
39 self.target.as_ref().and_then(|c| c.to_number_descriptor());
40 Error(number_out_of_range(
41 fragment.fragment(),
42 <L as Promote<R>>::Output::get_type(),
43 descriptor.as_ref(),
44 ))
45 })
46 .map(Some)
47 }
48 ColumnSaturationPolicy::Undefined => {
49 let Some((lp, rp)) = l.checked_promote(r) else {
50 return Ok(None);
51 };
52
53 match lp.checked_add(&rp) {
54 None => Ok(None),
55 Some(value) => Ok(Some(value)),
56 }
57 }
58 }
59 }
60}
61
62impl ColumnEvaluationContext<'_> {
63 pub fn sub<'a, L, R>(
64 &self,
65 l: &L,
66 r: &R,
67 fragment: impl LazyFragment<'a> + Copy,
68 ) -> reifydb_core::Result<Option<<L as Promote<R>>::Output>>
69 where
70 L: Promote<R>,
71 R: IsNumber,
72 <L as Promote<R>>::Output: IsNumber,
73 <L as Promote<R>>::Output: SafeSub,
74 {
75 match &self.saturation_policy() {
76 ColumnSaturationPolicy::Error => {
77 let Some((lp, rp)) = l.checked_promote(r) else {
78 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
79 return_error!(number_out_of_range(
80 fragment.fragment(),
81 <L as Promote<R>>::Output::get_type(),
82 descriptor.as_ref(),
83 ));
84 };
85
86 lp.checked_sub(&rp)
87 .ok_or_else(|| {
88 let descriptor =
89 self.target.as_ref().and_then(|c| c.to_number_descriptor());
90 Error(number_out_of_range(
91 fragment.fragment(),
92 <L as Promote<R>>::Output::get_type(),
93 descriptor.as_ref(),
94 ))
95 })
96 .map(Some)
97 }
98 ColumnSaturationPolicy::Undefined => {
99 let Some((lp, rp)) = l.checked_promote(r) else {
100 return Ok(None);
101 };
102
103 match lp.checked_sub(&rp) {
104 None => Ok(None),
105 Some(value) => Ok(Some(value)),
106 }
107 }
108 }
109 }
110}
111
112impl ColumnEvaluationContext<'_> {
113 pub fn mul<'a, L, R>(
114 &self,
115 l: &L,
116 r: &R,
117 fragment: impl LazyFragment<'a> + Copy,
118 ) -> reifydb_core::Result<Option<<L as Promote<R>>::Output>>
119 where
120 L: Promote<R>,
121 R: IsNumber,
122 <L as Promote<R>>::Output: IsNumber,
123 <L as Promote<R>>::Output: SafeMul,
124 {
125 match &self.saturation_policy() {
126 ColumnSaturationPolicy::Error => {
127 let Some((lp, rp)) = l.checked_promote(r) else {
128 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
129 return_error!(number_out_of_range(
130 fragment.fragment(),
131 <L as Promote<R>>::Output::get_type(),
132 descriptor.as_ref(),
133 ));
134 };
135
136 lp.checked_mul(&rp)
137 .ok_or_else(|| {
138 let descriptor =
139 self.target.as_ref().and_then(|c| c.to_number_descriptor());
140 Error(number_out_of_range(
141 fragment.fragment(),
142 <L as Promote<R>>::Output::get_type(),
143 descriptor.as_ref(),
144 ))
145 })
146 .map(Some)
147 }
148 ColumnSaturationPolicy::Undefined => {
149 let Some((lp, rp)) = l.checked_promote(r) else {
150 return Ok(None);
151 };
152
153 match lp.checked_mul(&rp) {
154 None => Ok(None),
155 Some(value) => Ok(Some(value)),
156 }
157 }
158 }
159 }
160}
161
162impl ColumnEvaluationContext<'_> {
163 pub fn div<'a, L, R>(
164 &self,
165 l: &L,
166 r: &R,
167 fragment: impl LazyFragment<'a> + Copy,
168 ) -> reifydb_core::Result<Option<<L as Promote<R>>::Output>>
169 where
170 L: Promote<R>,
171 R: IsNumber,
172 <L as Promote<R>>::Output: IsNumber,
173 <L as Promote<R>>::Output: SafeDiv,
174 {
175 match &self.saturation_policy() {
176 ColumnSaturationPolicy::Error => {
177 let Some((lp, rp)) = l.checked_promote(r) else {
178 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
179 return_error!(number_out_of_range(
180 fragment.fragment(),
181 <L as Promote<R>>::Output::get_type(),
182 descriptor.as_ref(),
183 ));
184 };
185
186 lp.checked_div(&rp)
187 .ok_or_else(|| {
188 let descriptor =
189 self.target.as_ref().and_then(|c| c.to_number_descriptor());
190 Error(number_out_of_range(
191 fragment.fragment(),
192 <L as Promote<R>>::Output::get_type(),
193 descriptor.as_ref(),
194 ))
195 })
196 .map(Some)
197 }
198 ColumnSaturationPolicy::Undefined => {
199 let Some((lp, rp)) = l.checked_promote(r) else {
200 return Ok(None);
201 };
202
203 match lp.checked_div(&rp) {
204 None => Ok(None),
205 Some(value) => Ok(Some(value)),
206 }
207 }
208 }
209 }
210}
211
212impl ColumnEvaluationContext<'_> {
213 pub fn remainder<'a, L, R>(
214 &self,
215 l: &L,
216 r: &R,
217 fragment: impl LazyFragment<'a> + Copy,
218 ) -> reifydb_core::Result<Option<<L as Promote<R>>::Output>>
219 where
220 L: Promote<R>,
221 R: IsNumber,
222 <L as Promote<R>>::Output: IsNumber,
223 <L as Promote<R>>::Output: SafeRemainder,
224 {
225 match &self.saturation_policy() {
226 ColumnSaturationPolicy::Error => {
227 let Some((lp, rp)) = l.checked_promote(r) else {
228 let descriptor = self.target.as_ref().and_then(|c| c.to_number_descriptor());
229 return_error!(number_out_of_range(
230 fragment.fragment(),
231 <L as Promote<R>>::Output::get_type(),
232 descriptor.as_ref(),
233 ));
234 };
235
236 lp.checked_rem(&rp)
237 .ok_or_else(|| {
238 let descriptor =
239 self.target.as_ref().and_then(|c| c.to_number_descriptor());
240 Error(number_out_of_range(
241 fragment.fragment(),
242 <L as Promote<R>>::Output::get_type(),
243 descriptor.as_ref(),
244 ))
245 })
246 .map(Some)
247 }
248 ColumnSaturationPolicy::Undefined => {
249 let Some((lp, rp)) = l.checked_promote(r) else {
250 return Ok(None);
251 };
252
253 match lp.checked_rem(&rp) {
254 None => Ok(None),
255 Some(value) => Ok(Some(value)),
256 }
257 }
258 }
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use reifydb_type::Fragment;
265
266 use crate::evaluate::ColumnEvaluationContext;
267
268 #[test]
269 fn test_add() {
270 let test_instance = ColumnEvaluationContext::testing();
271 let result = test_instance.add(&1i8, &255i16, || Fragment::testing_empty());
272 assert_eq!(result, Ok(Some(256i128)));
273 }
274
275 #[test]
276 fn test_sub() {
277 let test_instance = ColumnEvaluationContext::testing();
278 let result = test_instance.sub(&1i8, &255i16, || Fragment::testing_empty());
279 assert_eq!(result, Ok(Some(-254i128)));
280 }
281
282 #[test]
283 fn test_mul() {
284 let test_instance = ColumnEvaluationContext::testing();
285 let result = test_instance.mul(&23i8, &255i16, || Fragment::testing_empty());
286 assert_eq!(result, Ok(Some(5865i128)));
287 }
288
289 #[test]
290 fn test_div() {
291 let test_instance = ColumnEvaluationContext::testing();
292 let result = test_instance.div(&120i8, &20i16, || Fragment::testing_empty());
293 assert_eq!(result, Ok(Some(6i128)));
294 }
295
296 #[test]
297 fn test_remainder() {
298 let test_instance = ColumnEvaluationContext::testing();
299 let result = test_instance.remainder(&120i8, &21i16, || Fragment::testing_empty());
300 assert_eq!(result, Ok(Some(15i128)));
301 }
302}