Skip to main content

ferroui_dynamic_data/aggregation/
aggregate_number.rs

1//! The numbers of the aggregations: the value types of the overloads of `Sum` (`SumEx`) and `Avg`
2//! (`AvgEx`).
3//!
4//! Upstream has one overload per numeric type (`int`, `long`, `double`, `decimal`, `float`) and
5//! its nullable form. The port has one generic method each, over the traits of this module:
6//! [`AggregateNumber`], implemented for `i32`, `i64`, `f32` and `f64`, and [`AggregateValue`], the
7//! value a selector returns: a number, or an `Option` of one, whose `None` counts as zero
8//! (upstream's `GetValueOrDefault()`, which keeps the item in the count of an average).
9//!
10//! The arithmetic is that of C#'s unchecked context: the integers wrap on overflow.
11//!
12//! Not ported: the `decimal` overloads - no decimal type (phase 1b).
13
14use std::fmt::Debug;
15
16/// A number that the aggregations add up and average (`int`, `long`, `float`, `double`).
17pub trait AggregateNumber: Copy + PartialEq + PartialOrd + Debug + 'static {
18    /// The type of an average: `f64` for `i32`, `i64` and `f64` (`double`), `f32` for `f32`
19    /// (`float`), as upstream's overloads of `Avg` return.
20    type Average: Copy + PartialEq + PartialOrd + Debug + 'static;
21
22    /// Zero, the sum of no values.
23    const ZERO: Self;
24
25    /// `self + other`, wrapping on overflow for the integers.
26    fn plus(self, other: Self) -> Self;
27
28    /// `self - other`, wrapping on overflow for the integers.
29    fn minus(self, other: Self) -> Self;
30
31    /// The average of `count` values (at least one) whose sum is `sum`: `sum / (double)count` for
32    /// the integers and `double`, `sum / count` in `float` for `float`.
33    fn average(sum: Self, count: i32) -> Self::Average;
34
35    /// The number as an average (the implicit conversion of upstream's `emptyValue`).
36    fn to_average(self) -> Self::Average;
37}
38
39impl AggregateNumber for i32 {
40    type Average = f64;
41    const ZERO: i32 = 0;
42
43    fn plus(self, other: i32) -> i32 {
44        self.wrapping_add(other)
45    }
46
47    fn minus(self, other: i32) -> i32 {
48        self.wrapping_sub(other)
49    }
50
51    fn average(sum: i32, count: i32) -> f64 {
52        f64::from(sum) / f64::from(count)
53    }
54
55    fn to_average(self) -> f64 {
56        f64::from(self)
57    }
58}
59
60impl AggregateNumber for i64 {
61    type Average = f64;
62    const ZERO: i64 = 0;
63
64    fn plus(self, other: i64) -> i64 {
65        self.wrapping_add(other)
66    }
67
68    fn minus(self, other: i64) -> i64 {
69        self.wrapping_sub(other)
70    }
71
72    fn average(sum: i64, count: i32) -> f64 {
73        sum as f64 / f64::from(count)
74    }
75
76    fn to_average(self) -> f64 {
77        self as f64
78    }
79}
80
81impl AggregateNumber for f32 {
82    type Average = f32;
83    const ZERO: f32 = 0.0;
84
85    fn plus(self, other: f32) -> f32 {
86        self + other
87    }
88
89    fn minus(self, other: f32) -> f32 {
90        self - other
91    }
92
93    fn average(sum: f32, count: i32) -> f32 {
94        sum / count as f32
95    }
96
97    fn to_average(self) -> f32 {
98        self
99    }
100}
101
102impl AggregateNumber for f64 {
103    type Average = f64;
104    const ZERO: f64 = 0.0;
105
106    fn plus(self, other: f64) -> f64 {
107        self + other
108    }
109
110    fn minus(self, other: f64) -> f64 {
111        self - other
112    }
113
114    fn average(sum: f64, count: i32) -> f64 {
115        sum / f64::from(count)
116    }
117
118    fn to_average(self) -> f64 {
119        self
120    }
121}
122
123/// The value a selector of `Sum` or `Avg` returns: a number, or an `Option` of a number whose
124/// `None` counts as zero (the nullable overloads, `valueSelector(t).GetValueOrDefault()`).
125pub trait AggregateValue: Copy + 'static {
126    /// The number the value counts as.
127    type Number: AggregateNumber;
128
129    /// The number the value counts as: the value itself, or zero for a `None`.
130    fn number(self) -> Self::Number;
131}
132
133macro_rules! impl_aggregate_value {
134    ($($ty:ty),*) => {
135        $(
136            impl AggregateValue for $ty {
137                type Number = $ty;
138
139                fn number(self) -> $ty {
140                    self
141                }
142            }
143        )*
144    };
145}
146
147impl_aggregate_value!(i32, i64, f32, f64);
148
149impl<X: AggregateNumber> AggregateValue for Option<X> {
150    type Number = X;
151
152    fn number(self) -> X {
153        self.unwrap_or(X::ZERO)
154    }
155}