prexel/ops/
checked.rs

1use num_traits::{FromPrimitive, Zero};
2use crate::error::*;
3use crate::function::{
4    Associativity, BinaryFunction, Function, Notation, Precedence, UnaryFunction,
5};
6use crate::num::checked::{CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub};
7use crate::Result;
8
9#[cfg(feature = "docs")]
10use crate::descriptions::Description;
11
12pub struct AddOperator;
13impl<N: CheckedAdd> BinaryFunction<N> for AddOperator {
14    fn name(&self) -> &str {
15        "+"
16    }
17
18    fn precedence(&self) -> Precedence {
19        Precedence::LOW
20    }
21
22    fn associativity(&self) -> Associativity {
23        Associativity::Left
24    }
25
26    fn call(&self, left: N, right: N) -> Result<N> {
27        left.checked_add(&right)
28            .ok_or_else(|| Error::from(ErrorKind::Overflow))
29    }
30
31    #[cfg(feature="docs")]
32    fn description(&self) -> Option<&str> {
33        Some(Description::Add.into())
34    }
35}
36
37pub struct SubOperator;
38impl<N: CheckedSub> BinaryFunction<N> for SubOperator {
39    fn name(&self) -> &str {
40        "-"
41    }
42
43    fn precedence(&self) -> Precedence {
44        Precedence::LOW
45    }
46
47    fn associativity(&self) -> Associativity {
48        Associativity::Left
49    }
50
51    fn call(&self, left: N, right: N) -> Result<N> {
52        left.checked_sub(&right)
53            .ok_or_else(|| Error::from(ErrorKind::Overflow))
54    }
55
56    #[cfg(feature="docs")]
57    fn description(&self) -> Option<&str> {
58        Some(Description::Sub.into())
59    }
60}
61
62pub struct MulOperator;
63impl<N: CheckedMul> BinaryFunction<N> for MulOperator {
64    fn name(&self) -> &str {
65        "*"
66    }
67
68    fn precedence(&self) -> Precedence {
69        Precedence::MEDIUM
70    }
71
72    fn associativity(&self) -> Associativity {
73        Associativity::Left
74    }
75
76    fn call(&self, left: N, right: N) -> Result<N> {
77        left.checked_mul(&right)
78            .ok_or_else(|| Error::from(ErrorKind::Overflow))
79    }
80
81    #[cfg(feature="docs")]
82    fn description(&self) -> Option<&str> {
83        Some(Description::Mul.into())
84    }
85}
86
87pub struct DivOperator;
88impl<N: CheckedDiv + Zero> BinaryFunction<N> for DivOperator {
89    fn name(&self) -> &str {
90        "/"
91    }
92
93    fn precedence(&self) -> Precedence {
94        Precedence::MEDIUM
95    }
96
97    fn associativity(&self) -> Associativity {
98        Associativity::Left
99    }
100
101    fn call(&self, left: N, right: N) -> Result<N> {
102        if right.is_zero() {
103            return Err(Error::from(ErrorKind::DivisionByZero));
104        }
105
106        left.checked_div(&right)
107            .ok_or_else(|| Error::from(ErrorKind::Overflow))
108    }
109
110    #[cfg(feature="docs")]
111    fn description(&self) -> Option<&str> {
112        Some(Description::Div.into())
113    }
114}
115
116pub struct ModOperator;
117impl<N: CheckedRem + Zero> BinaryFunction<N> for ModOperator {
118    fn name(&self) -> &str {
119        "mod"
120    }
121
122    fn precedence(&self) -> Precedence {
123        Precedence::MEDIUM
124    }
125
126    fn associativity(&self) -> Associativity {
127        Associativity::Left
128    }
129
130    fn call(&self, left: N, right: N) -> Result<N> {
131        if right.is_zero() {
132            return Err(Error::from(ErrorKind::DivisionByZero));
133        }
134
135        left.checked_rem(&right)
136            .ok_or_else(|| Error::from(ErrorKind::Overflow))
137    }
138
139    #[cfg(feature="docs")]
140    fn description(&self) -> Option<&str> {
141        Some(Description::Mod.into())
142    }
143}
144
145pub struct UnaryMinus;
146impl<N: CheckedNeg> UnaryFunction<N> for UnaryMinus {
147    fn name(&self) -> &str {
148        "-"
149    }
150
151    fn notation(&self) -> Notation {
152        Notation::Prefix
153    }
154
155    fn call(&self, value: N) -> Result<N> {
156        value.checked_neg().ok_or_else(|| Error::from(ErrorKind::Overflow))
157    }
158
159    #[cfg(feature="docs")]
160    fn description(&self) -> Option<&str> {
161        Some(Description::Neg.into())
162    }
163}
164
165pub struct AbsFunction;
166impl<N: Zero + PartialOrd + CheckedNeg + Clone> Function<N> for AbsFunction {
167    fn name(&self) -> &str {
168        "abs"
169    }
170
171    fn call(&self, args: &[N]) -> Result<N> {
172        if args.len() != 1 {
173            Err(Error::from(ErrorKind::InvalidArgumentCount))
174        } else if args[0] >= N::zero() {
175            Ok(args[0].clone())
176        } else {
177            args[0]
178                .checked_neg()
179                .ok_or_else(|| Error::from(ErrorKind::Overflow))
180        }
181    }
182
183    #[cfg(feature="docs")]
184    fn description(&self) -> Option<&str> {
185        Some(Description::Abs.into())
186    }
187}
188
189pub struct SumFunction;
190impl<N: CheckedAdd + Clone> Function<N> for SumFunction {
191    fn name(&self) -> &str {
192        "sum"
193    }
194
195    fn call(&self, args: &[N]) -> Result<N> {
196        let mut result = None;
197
198        for cur in args {
199            match result {
200                None => result = Some(cur.clone()),
201                Some(ref n) => {
202                    result = Some(n.checked_add(cur)
203                        .ok_or_else(|| Error::from(ErrorKind::Overflow))?);
204                }
205            }
206        }
207
208        result.ok_or_else(|| Error::from(ErrorKind::InvalidArgumentCount))
209    }
210
211    #[cfg(feature="docs")]
212    fn description(&self) -> Option<&str> {
213        Some(Description::Sum.into())
214    }
215}
216
217pub struct ProdFunction;
218impl<N: CheckedMul + Clone> Function<N> for ProdFunction {
219    fn name(&self) -> &str {
220        "product"
221    }
222
223    fn call(&self, args: &[N]) -> Result<N> {
224        let mut result = None;
225
226        for cur in args {
227            match result {
228                None => result = Some(cur.clone()),
229                Some(ref n) => {
230                    result = Some(n.checked_mul(cur)
231                        .ok_or_else(|| Error::from(ErrorKind::Overflow))?);
232                }
233            }
234        }
235
236        result.ok_or_else(|| Error::from(ErrorKind::InvalidArgumentCount))
237    }
238
239    #[cfg(feature="docs")]
240    fn description(&self) -> Option<&str> {
241        Some(Description::Prod.into())
242    }
243}
244
245pub struct AvgFunction;
246impl<N: CheckedAdd + CheckedDiv + FromPrimitive + Clone> Function<N> for AvgFunction {
247    fn name(&self) -> &str {
248        "avg"
249    }
250
251    fn call(&self, args: &[N]) -> Result<N> {
252        let mut sum = None;
253
254        for cur in args {
255            match sum {
256                None => sum = Some(cur.clone()),
257                Some(ref n) => {
258                    sum = Some(n.checked_add(cur)
259                        .ok_or_else(|| Error::from(ErrorKind::Overflow))?);
260                }
261            }
262        }
263
264        match sum {
265            Some(n) => {
266                let result = n
267                    .checked_div(&N::from_usize(args.len()).unwrap())
268                    .ok_or_else(|| Error::from(ErrorKind::Overflow))?;
269
270                Ok(result)
271            }
272            None => Err(Error::from(ErrorKind::InvalidArgumentCount)),
273        }
274    }
275
276    #[cfg(feature="docs")]
277    fn description(&self) -> Option<&str> {
278        Some(Description::Avg.into())
279    }
280}
281
282#[cfg(test)]
283mod tests{
284    use super::*;
285    fn empty_array<T>() -> Box<[T]>{
286        vec![].into_boxed_slice()
287    }
288
289    #[test]
290    fn add_test(){
291        let instance = AddOperator;
292
293        assert_eq!(instance.call(10_f64, 4_f64), Ok(14_f64));
294        assert_eq!(instance.call(3, 7), Ok(10));
295        assert!(instance.call(i32::MAX, 10).is_err());
296    }
297
298    #[test]
299    fn sub_test(){
300        let instance = SubOperator;
301
302        assert_eq!(instance.call(10_f64, 4_f64), Ok(6_f64));
303        assert_eq!(instance.call(3, 7), Ok(-4));
304        assert!(instance.call(i32::MIN, 10).is_err());
305    }
306
307    #[test]
308    fn mul_test(){
309        let instance = MulOperator;
310
311        assert_eq!(instance.call(10_f64, 4_f64), Ok(40_f64));
312        assert_eq!(instance.call(3, 7), Ok(21));
313        assert!(instance.call(i32::MAX, 10).is_err());
314    }
315
316    #[test]
317    fn div_test(){
318        let instance = DivOperator;
319
320        assert_eq!(instance.call(10_f64, 4_f64), Ok(2.5_f64));
321        assert_eq!(instance.call(20, 4), Ok(5));
322        assert!(instance.call(5, 0).is_err());
323    }
324
325    #[test]
326    fn mod_test(){
327        let instance = ModOperator;
328
329        assert_eq!(instance.call(10_f64, 4_f64), Ok(2_f64));
330        assert_eq!(instance.call(20, 4), Ok(0));
331        assert!(instance.call(5, 0).is_err());
332    }
333
334    #[test]
335    fn unary_minus_test(){
336        let instance = UnaryMinus;
337
338        assert_eq!(instance.call(10_f64), Ok(-10_f64));
339        assert_eq!(instance.call(-5), Ok(5));
340
341        assert!(instance.call(i32::MIN).is_err());
342    }
343
344    #[test]
345    fn abs_test(){
346        let instance = AbsFunction;
347
348        assert_eq!(instance.call(&[-5_f64]), Ok(5_f64));
349        assert_eq!(instance.call(&[3]), Ok(3));
350
351        assert!(instance.call(&[i32::MIN]).is_err());
352    }
353
354    #[test]
355    fn sum_test(){
356        let instance = SumFunction;
357
358        assert_eq!(instance.call(&[1_f64, 2_f64, 3_f64]), Ok(6_f64));
359        assert_eq!(instance.call(&[2, 4, 6]), Ok(12));
360
361        assert!(instance.call(&[2]).is_ok());
362        assert!(instance.call(empty_array::<i64>().as_ref()).is_err());
363        assert!(instance.call(&[i32::MAX, 10, 20]).is_err());
364    }
365
366    #[test]
367    fn prod_test(){
368        let instance = ProdFunction;
369
370        assert_eq!(instance.call(&[2_f64, 3_f64, 4_f64]), Ok(24_f64));
371        assert_eq!(instance.call(&[2, 4, 6]), Ok(48));
372
373        assert!(instance.call(&[2]).is_ok());
374        assert!(instance.call(empty_array::<i64>().as_ref()).is_err());
375        assert!(instance.call(&[i32::MAX, 10, 20]).is_err());
376    }
377
378    #[test]
379    fn avg_test(){
380        let instance = AvgFunction;
381
382        assert_eq!(instance.call(&[1_f64, 2_f64, 3_f64, 4_f64]), Ok(2.5_f64));
383        assert_eq!(instance.call(&[2, 4, 6]), Ok(4));
384
385        assert!(instance.call(&[2]).is_ok());
386        assert!(instance.call(empty_array::<i64>().as_ref()).is_err());
387        assert!(instance.call(&[i32::MAX, 10, 20, 30]).is_err());
388    }
389}