1use uom::{
29 Conversion,
30 fmt::DisplayStyle,
31 num_traits::{AsPrimitive, FromPrimitive, Num},
32 si::{self, Dimension, Quantity, Unit, Units, fmt::QuantityArguments},
33};
34
35use crate::random_variable::{RandomVariable, Sample};
36use core::{borrow::Borrow, fmt, marker::PhantomData};
37
38pub struct RandomQuantity<B, D, U, V, N, const M: usize>
59where
60 B: Borrow<RandomVariable<Quantity<D, U, V>, M>>,
61 D: Dimension + ?Sized,
62 U: Units<V> + ?Sized,
63 V: Num + Conversion<V>,
64 Quantity<D, U, V>: Sample,
65{
66 rv: B,
68 style: DisplayStyle,
70 unit: N,
72 _marker: PhantomData<Quantity<D, U, V>>,
74}
75
76impl<B, D, U, V, N, const M: usize> RandomQuantity<B, D, U, V, N, M>
77where
78 B: Borrow<RandomVariable<Quantity<D, U, V>, M>>,
79 D: Dimension + ?Sized,
80 U: Units<V> + ?Sized,
81 V: Num + Conversion<V>,
82 Quantity<D, U, V>: Sample,
83{
84 const fn new(rv: B, style: DisplayStyle, unit: N) -> Self {
85 Self {
86 rv,
87 style,
88 unit,
89 _marker: PhantomData,
90 }
91 }
92
93 pub fn into_inner(self) -> B {
95 self.rv
96 }
97}
98
99pub trait Compatible<D: Dimension + ?Sized>: Unit {
108 fn format_args<U: Units<V> + ?Sized, V: Num + Conversion<V>>(
111 self,
112 value: Quantity<D, U, V>,
113 style: DisplayStyle,
114 ) -> QuantityArguments<D, U, V, Self>;
115}
116
117impl<D, U, V> Sample for Quantity<D, U, V>
119where
120 D: Dimension + ?Sized,
121 U: Units<V> + ?Sized,
122 V: Conversion<V> + Num + AsPrimitive<f64> + FromPrimitive + PartialEq + PartialOrd,
123{
124 fn quantify(&self) -> f64 {
125 self.value.as_()
126 }
127
128 fn qualify(val: f64) -> Self {
129 Quantity {
130 dimension: PhantomData,
131 units: PhantomData,
132 value: V::from_f64(val).unwrap(),
133 }
134 }
135}
136
137impl<D, U, V, const M: usize> RandomVariable<Quantity<D, U, V>, M>
139where
140 Quantity<D, U, V>: Sample,
141 D: Dimension + ?Sized,
142 U: Units<V> + ?Sized,
143 V: Conversion<V> + Num,
144{
145 pub fn display<N>(&self, unit: N) -> RandomQuantity<&'_ Self, D, U, V, N, M>
151 where
152 N: Compatible<D> + Conversion<V, T = <V as Conversion<V>>::T>,
153 {
154 RandomQuantity::new(self, DisplayStyle::Abbreviation, unit)
155 }
156
157 pub fn displayed<N>(self, unit: N) -> RandomQuantity<Self, D, U, V, N, M>
163 where
164 N: Compatible<D> + Conversion<V, T = <V as Conversion<V>>::T>,
165 {
166 RandomQuantity::new(self, DisplayStyle::Abbreviation, unit)
167 }
168
169 pub fn display_full<N>(&self, unit: N) -> RandomQuantity<&'_ Self, D, U, V, N, M>
175 where
176 N: Compatible<D> + Conversion<V, T = <V as Conversion<V>>::T>,
177 {
178 RandomQuantity::new(self, DisplayStyle::Description, unit)
179 }
180
181 pub fn displayed_full<N>(self, unit: N) -> RandomQuantity<Self, D, U, V, N, M>
187 where
188 N: Compatible<D> + Conversion<V, T = <V as Conversion<V>>::T>,
189 {
190 RandomQuantity::new(self, DisplayStyle::Description, unit)
191 }
192}
193
194macro_rules! impl_debug_fmt {
196 ($($n:tt),* $(,)?) => {$(
197 impl<B, D, U, V, N> fmt::Debug for RandomQuantity<B, D, U, V, N, $n>
198 where
199 Quantity<D, U, V>: Sample,
200 B: Borrow<RandomVariable<Quantity<D, U, V>, $n>>,
201 D: Dimension + ?Sized,
202 U: Units<V> + ?Sized,
203 V: Conversion<V> + Num + fmt::Debug,
204 N: Compatible<D> + Conversion<V, T = <V as Conversion<V>>::T>,
205 {
206 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
207 let mut dbs = f.debug_struct("RandomVariable");
208 let rv = self.rv.borrow();
209 let len = rv.count();
210
211 dbs.field("n", &len);
212
213 if len > 0 {
214 impl_debug_fmt!(@inner_impl self, rv, dbs, $n);
215 }
216
217 dbs.finish()
218 }
219 }
220 )*};
221
222 (@inner_impl $self:ident, $rv:ident, $debug:ident, 0) => {
223 $debug
224 .field("min", &$self.unit.format_args($rv.min().unwrap(), $self.style))
225 .field("max", &$self.unit.format_args($rv.max().unwrap(), $self.style));
226 };
227
228 (@inner_impl $self:ident, $rv:ident, $debug:ident, 1) => {
229 impl_debug_fmt!(@inner_impl $self, $rv, $debug, 0);
230 $debug.field(
231 "mean",
232 &$self.unit.format_args(Quantity::qualify($rv.mean()), $self.style)
233 );
234 };
235
236 (@inner_impl $self:ident, $rv:ident, $debug:ident, 2) => {
237 impl_debug_fmt!(@inner_impl $self, $rv, $debug, 1);
238 #[cfg(any(feature = "std", feature = "libm"))]
239 {
240 $debug.field(
241 "sdev",
242 &$self.unit.format_args(Quantity::qualify($rv.std_dev()), $self.style)
243 );
244 }
245 #[cfg(not(any(feature = "std", feature = "libm")))]
246 {
247 $debug.field("variance", &$rv.variance());
248 }
249 };
250
251 (@inner_impl $self:ident, $rv:ident, $debug:ident, 3) => {
252 impl_debug_fmt!(@inner_impl $self, $rv, $debug, 2);
253 $debug.field("skew", &$rv.skew());
254 };
255
256 (@inner_impl $self:ident, $rv:ident, $debug:ident, 4) => {
257 impl_debug_fmt!(@inner_impl $self, $rv, $debug, 3);
258 $debug.field("kurt", &$rv.kurtosis());
259 };
260}
261
262impl_debug_fmt!(0, 1, 2);
264
265#[cfg(any(feature = "std", feature = "libm"))]
266impl_debug_fmt!(3, 4);
267
268macro_rules! uom_compatible {
273 ($U:ident, $T:ident) => {
274 impl<T> Compatible<::uom::si::$U::Dimension> for T
275 where
276 T: si::$U::Unit,
277 {
278 fn format_args<U: si::Units<V> + ?Sized, V: Num + Conversion<V>>(
279 self,
280 value: si::$U::$T<U, V>,
281 style: DisplayStyle,
282 ) -> QuantityArguments<si::$U::Dimension, U, V, Self> {
283 value.into_format_args(self, style)
284 }
285 }
286 };
287}
288
289super::uom_quantity!(uom_compatible!);