1use std::{
2 cmp::Ordering,
3 fmt::Display,
4 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
5};
6
7use crate::axis::Axis;
8
9use super::histogram::{Histogram, Item, Iter, IterMut, ValuesMut};
10
11#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct VecHistogram<A, V> {
17 axes: A,
18 values: Vec<V>,
19}
20
21impl<A: Axis, V: Default + Clone> VecHistogram<A, V> {
22 pub fn new(axes: A) -> Self {
25 let size = axes.num_bins();
26 Self {
27 axes,
28 values: vec![V::default(); size],
29 }
30 }
31}
32
33impl<A: Axis, V> Histogram<A, V> for VecHistogram<A, V> {
34 fn value(&self, coordinate: &A::Coordinate) -> Option<&V> {
35 let index = self.axes.index(coordinate)?;
36 self.values.get(index)
37 }
38
39 #[inline]
40 fn axes(&self) -> &A {
41 &self.axes
42 }
43
44 fn value_at_index(&self, index: usize) -> Option<&V> {
45 self.values.get(index)
46 }
47
48 fn values<'a>(&'a self) -> Box<dyn Iterator<Item = &'a V> + 'a> {
49 Box::new(self.values.iter())
50 }
51
52 fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = Item<A::BinInterval, &'a V>> + 'a> {
53 Box::new(self.axes().iter().map(move |(index, binrange)| {
54 Item {
55 index,
56 bin: binrange,
57 value: self
58 .value_at_index(index)
59 .expect("iter() indices are always in range"),
60 }
61 }))
62 }
63
64 fn value_at_index_mut(&mut self, index: usize) -> Option<&mut V> {
65 self.values.get_mut(index)
66 }
67
68 fn values_mut(&mut self) -> ValuesMut<'_, V> {
69 Box::new(self.values.iter_mut())
70 }
71
72 fn iter_mut(&mut self) -> IterMut<'_, A, V> {
73 Box::new(
74 self.axes
75 .iter()
76 .zip(self.values.iter_mut())
77 .map(|((index, bin), value)| Item { index, bin, value }),
78 )
79 }
80}
81
82impl<'a, A: Axis, V> IntoIterator for &'a VecHistogram<A, V> {
83 type Item = Item<A::BinInterval, &'a V>;
84
85 type IntoIter = Iter<'a, A, V>;
86
87 fn into_iter(self) -> Self::IntoIter {
88 self.iter()
89 }
90}
91
92impl<'a, A: Axis, V: 'a> IntoIterator for &'a mut VecHistogram<A, V> {
93 type Item = Item<A::BinInterval, &'a mut V>;
94
95 type IntoIter = IterMut<'a, A, V>;
96
97 fn into_iter(self) -> Self::IntoIter {
98 self.iter_mut()
99 }
100}
101
102impl<A: Axis, V> Display for VecHistogram<A, V>
103where
104 V: Clone + Into<f64>,
105 A::BinInterval: Display,
106{
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 let precision = f.precision().unwrap_or(2);
109
110 let sum = self
111 .values()
112 .map(|it| {
113 let x: f64 = it.clone().into();
114 x
115 })
116 .fold(0.0, |it, value| it + value);
117 write!(
118 f,
119 "VecHistogram{}D({} bins, sum={})",
120 self.axes().num_dim(),
121 self.axes().num_bins(),
122 sum
123 )?;
124 let values: Vec<_> = self
125 .iter()
126 .take(50)
127 .map(|item| {
128 (item.bin, {
129 let x: f64 = item.value.clone().into();
130 x
131 })
132 })
133 .collect();
134 let scale = values
135 .iter()
136 .max_by(|l, r| l.1.partial_cmp(&r.1).unwrap_or(Ordering::Less))
137 .map(|it| it.1)
138 .unwrap_or(f64::INFINITY);
139 values
140 .into_iter()
141 .map(|(bin, value)| (bin, 50.0 * (value / scale)))
142 .map(|(bin, value)| {
143 (
144 format!("{:.precision$}", bin, precision = precision),
145 "#".repeat(value as usize),
146 )
147 })
148 .map(|(bin, value)| write!(f, "\n{:>16} | {}", bin, value))
149 .filter_map(Result::ok)
150 .count();
151 Ok(())
152 }
153}
154
155macro_rules! impl_binary_op_with_immutable_borrow {
156 ($Trait:tt, $method:tt, $mathsymbol:tt, $testresult:tt) => {
157 impl<A: Axis + PartialEq + Clone, V> $Trait<&VecHistogram<A, V>> for &VecHistogram<A, V>
158where
159 for<'a> &'a V: $Trait<Output = V>,
160{
161 type Output = Result<VecHistogram<A, V>, crate::error::BinaryOperationError>;
162
163 #[doc=concat!("let combined_hist = (&hist1 ", stringify!($mathsymbol), " &hist2).expect(\"Axes are compatible\");")]
179 #[doc=concat!("assert_eq!(combined_hist.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
180 fn $method(self, rhs: &VecHistogram<A, V>) -> Self::Output {
183 if self.axes() != rhs.axes() {
184 return Err(crate::error::BinaryOperationError);
185 }
186 let values = self
187 .values
188 .iter()
189 .zip(rhs.values.iter())
190 .map(|(l, r)| l $mathsymbol r)
191 .collect();
192 Ok(VecHistogram {
193 axes: self.axes().clone(),
194 values,
195 })
196 }
197}
198 };
199}
200
201impl_binary_op_with_immutable_borrow! {Add, add, +, 3.0}
202impl_binary_op_with_immutable_borrow! {Sub, sub, -, 1.0}
203impl_binary_op_with_immutable_borrow! {Mul, mul, *, 2.0}
204impl_binary_op_with_immutable_borrow! {Div, div, /, 2.0}
205
206macro_rules! impl_binary_op_with_scalar {
207 ($Trait:tt, $method:tt, $mathsymbol:tt) => {
208 impl<A: Axis + PartialEq + Clone, V> $Trait<&V> for &VecHistogram<A, V>
209where
210 for<'a> &'a V: $Trait<Output = V>,
211{
212 type Output = VecHistogram<A, V>;
213
214 fn $method(self, rhs: &V) -> Self::Output {
215 let values = self
216 .values
217 .iter()
218 .map(|l| l $mathsymbol rhs)
219 .collect();
220 VecHistogram {
221 axes: self.axes().clone(),
222 values,
223 }
224 }
225}
226 };
227}
228
229impl_binary_op_with_scalar! {Add, add, +}
230impl_binary_op_with_scalar! {Sub, sub, -}
231impl_binary_op_with_scalar! {Mul, mul, *}
232impl_binary_op_with_scalar! {Div, div, /}
233
234macro_rules! impl_binary_op_with_owned {
235 ($Trait:tt, $method:tt, $ValueAssignTrait:tt, $mathsymbol:tt, $assignmathsymbol:tt, $testresult:tt) => {
236 impl<A: Axis + PartialEq, V> $Trait<&VecHistogram<A, V>> for VecHistogram<A, V>
237 where
238 for<'a> V: $ValueAssignTrait<&'a V>,
239 {
240 type Output = Result<VecHistogram<A, V>, crate::error::BinaryOperationError>;
241
242 #[doc=concat!("let combined_hist = (hist1 ", stringify!($mathsymbol), " &hist2).expect(\"Axes are compatible\");")]
260 #[doc=concat!("assert_eq!(combined_hist.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
261 fn $method(mut self, rhs: &VecHistogram<A, V>) -> Self::Output {
264 if self.axes() != rhs.axes() {
265 return Err(crate::error::BinaryOperationError);
266 }
267 self.values
268 .iter_mut()
269 .zip(rhs.values.iter())
270 .for_each(|(l, r)| *l $assignmathsymbol &r);
271 Ok(self)
272 }
273 }
274 };
275}
276
277impl_binary_op_with_owned! {Add, add, AddAssign, +, +=, 3.0}
278impl_binary_op_with_owned! {Sub, sub, SubAssign, -, -=, 1.0}
279impl_binary_op_with_owned! {Mul, mul, MulAssign, *, *=, 2.0}
280impl_binary_op_with_owned! {Div, div, DivAssign, /, /=, 2.0}
281
282macro_rules! impl_binary_op_assign {
283 ($Trait:tt, $method:tt, $ValueAssignTrait:tt, $mathsymbol:tt, $testresult:tt) => {
284 impl<A: Axis + PartialEq, V> $Trait<&VecHistogram<A, V>> for VecHistogram<A, V>
285 where
286 for<'a> V: $ValueAssignTrait<&'a V>,
287 {
288 #[doc=concat!("hist1 ", stringify!($mathsymbol), " &hist2;")]
307 #[doc=concat!("assert_eq!(hist1.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
308 fn $method(&mut self, rhs: &VecHistogram<A, V>) {
311 if self.axes() != rhs.axes() {
312 panic!("Cannot combine VecHistograms with incompatible axes.");
313 }
314 self.values
315 .iter_mut()
316 .zip(rhs.values.iter())
317 .for_each(|(l, r)| *l $mathsymbol &r);
318 }
319 }
320 };
321}
322
323impl_binary_op_assign! {AddAssign, add_assign, AddAssign, +=, 3.0}
324impl_binary_op_assign! {SubAssign, sub_assign, SubAssign, -=, 1.0}
325impl_binary_op_assign! {MulAssign, mul_assign, MulAssign, *=, 2.0}
326impl_binary_op_assign! {DivAssign, div_assign, DivAssign, /=, 2.0}
327
328#[cfg(feature = "rayon")]
329use rayon::prelude::*;
330
331impl<A, V> VecHistogram<A, V> {
354 #[cfg(feature = "rayon")]
358 pub fn par_values(&self) -> impl IndexedParallelIterator<Item = &V>
359 where
360 V: Sync,
361 {
362 self.values.par_iter()
363 }
364
365 #[cfg(feature = "rayon")]
369 pub fn par_values_mut(&mut self) -> impl IndexedParallelIterator<Item = &mut V>
370 where
371 V: Send,
372 {
373 self.values.par_iter_mut()
374 }
375
376 #[cfg(feature = "rayon")]
380 pub fn par_iter(
381 &self,
382 ) -> impl IndexedParallelIterator<Item = Item<<A as Axis>::BinInterval, &V>>
383 where
384 A: Axis + Sync,
385 V: Sync,
386 <A as Axis>::BinInterval: Send,
387 {
388 self.values
389 .par_iter()
390 .enumerate()
391 .map(move |(index, value)| Item {
392 index,
393 bin: self
394 .axes
395 .bin(index)
396 .expect("We only iterate over valid indices."),
397 value,
398 })
399 }
400
401 #[cfg(feature = "rayon")]
405 pub fn par_iter_mut(
406 &mut self,
407 ) -> impl IndexedParallelIterator<Item = Item<<A as Axis>::BinInterval, &mut V>>
408 where
409 A: Axis + Sync + Send,
410 V: Send + Sync,
411 <A as Axis>::BinInterval: Send,
412 {
413 let axes = &self.axes;
414 self.values.par_iter_mut().enumerate().map(move |it| Item {
415 index: it.0,
416 bin: axes.bin(it.0).expect("We only iterate over valid indices."),
417 value: it.1,
418 })
419 }
420}