1use std::{
2 collections::HashMap,
3 collections::HashSet,
4 fmt::Display,
5 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
6};
7
8use super::histogram::{Histogram, Iter, IterMut, ValuesMut};
9use crate::{axis::Axis, Item};
10
11#[derive(Default, Clone, PartialEq, Eq, Debug)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct HashHistogram<A, V> {
21 axes: A,
22 values: HashMap<usize, V>,
23}
24
25impl<A: Axis, V> HashHistogram<A, V> {
26 pub fn new(axes: A) -> Self {
29 Self {
30 axes,
31 values: HashMap::new(),
32 }
33 }
34}
35
36impl<A: Axis, V: Default> Histogram<A, V> for HashHistogram<A, V> {
37 #[inline]
38 fn axes(&self) -> &A {
39 &self.axes
40 }
41
42 fn value_at_index(&self, index: usize) -> Option<&V> {
43 self.values.get(&index)
44 }
45
46 fn values(&self) -> super::histogram::Values<'_, V> {
47 Box::new(self.values.values())
48 }
49
50 fn iter(&self) -> Iter<'_, A, V> {
51 Box::new(self.values.iter().map(move |(index, value)| {
52 Item {
53 index: *index,
54 bin: self
55 .axes
56 .bin(*index)
57 .expect("iter() indices are always valid bins"),
58 value,
59 }
60 }))
61 }
62
63 fn value_at_index_mut(&mut self, index: usize) -> Option<&mut V> {
64 self.values.get_mut(&index)
65 }
66
67 fn values_mut(&mut self) -> ValuesMut<'_, V> {
68 Box::new(self.values.values_mut())
69 }
70
71 fn iter_mut(&mut self) -> IterMut<'_, A, V> {
72 let axes = &self.axes;
73 Box::new(self.values.iter_mut().map(move |(index, value)| {
74 Item {
75 index: *index,
76 bin: axes
77 .bin(*index)
78 .expect("iter_mut() indices are always valid bins"),
79 value,
80 }
81 }))
82 }
83
84 #[inline]
85 fn fill(&mut self, coordinate: &A::Coordinate)
86 where
87 V: crate::Fill,
88 {
89 if let Some(index) = self.axes.index(coordinate) {
90 self.values.entry(index).or_default().fill();
91 }
92 }
93
94 #[inline]
95 fn fill_with<D>(&mut self, coordinate: &A::Coordinate, data: D)
96 where
97 V: crate::FillWith<D>,
98 {
99 if let Some(index) = self.axes.index(coordinate) {
100 self.values.entry(index).or_default().fill_with(data);
101 }
102 }
103
104 #[inline]
105 fn fill_with_weighted<D, W>(&mut self, coordinate: &A::Coordinate, data: D, weight: W)
106 where
107 V: crate::FillWithWeighted<D, W>,
108 {
109 if let Some(index) = self.axes.index(coordinate) {
110 self.values
111 .entry(index)
112 .or_default()
113 .fill_with_weighted(data, weight);
114 }
115 }
116}
117
118impl<'a, A: Axis, V> IntoIterator for &'a HashHistogram<A, V>
119where
120 HashHistogram<A, V>: Histogram<A, V>,
121{
122 type Item = Item<A::BinInterval, &'a V>;
123
124 type IntoIter = Iter<'a, A, V>;
125
126 fn into_iter(self) -> Self::IntoIter {
127 self.iter()
128 }
129}
130
131impl<'a, A: Axis, V: 'a> IntoIterator for &'a mut HashHistogram<A, V>
132where
133 HashHistogram<A, V>: Histogram<A, V>,
134{
135 type Item = Item<A::BinInterval, &'a mut V>;
136
137 type IntoIter = IterMut<'a, A, V>;
138
139 fn into_iter(self) -> Self::IntoIter {
140 self.iter_mut()
141 }
142}
143
144impl<A: Axis, V> Display for HashHistogram<A, V>
145where
146 HashHistogram<A, V>: Histogram<A, V>,
147 V: Clone + Into<f64>,
148 A::BinInterval: Display,
149{
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 let sum = self
152 .values()
153 .map(|it| {
154 let x: f64 = it.clone().into();
155 x
156 })
157 .fold(0.0, |it, value| it + value);
158 write!(
159 f,
160 "HashHistogram{}D({} bins, sum={})",
161 self.axes().num_dim(),
162 self.axes().num_bins(),
163 sum
164 )?;
165 Ok(())
166 }
167}
168
169macro_rules! impl_binary_op_with_immutable_borrow {
170 ($Trait:tt, $method:tt, $mathsymbol:tt, $testresult:tt) => {
171 impl<A: Axis + PartialEq + Clone, V> $Trait<&HashHistogram<A, V>> for &HashHistogram<A, V>
172 where
173 HashHistogram<A, V>: Histogram<A, V>,
174 V: Clone + Default,
175 for<'a> &'a V: $Trait<Output = V>,
176 {
177 type Output = Result<HashHistogram<A, V>, crate::error::BinaryOperationError>;
178
179 #[doc=concat!("let combined_hist = (&hist1 ", stringify!($mathsymbol), " &hist2).expect(\"Axes are compatible\");")]
195 #[doc=concat!("assert_eq!(combined_hist.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
196 fn $method(self, rhs: &HashHistogram<A, V>) -> Self::Output {
199 if self.axes() != rhs.axes() {
200 return Err(crate::error::BinaryOperationError);
201 }
202 let indices: HashSet<usize> = self.values.keys().chain(rhs.values.keys()).copied().collect();
203 let values: HashMap<usize, V> = indices.into_iter().map(|index| {
204 let left = self.values.get(&index);
205 let right = rhs.values.get(&index);
206 let newvalue = match (left, right) {
207 (Some(left), Some(right)) => { left $mathsymbol right },
208 (None, Some(right)) => { &V::default() $mathsymbol right },
209 (Some(left), None) => { left $mathsymbol &V::default() },
210 (None, None) => { unreachable!() },
211 };
212 (index, newvalue)
213 }).collect();
214 Ok(HashHistogram {
215 axes: self.axes().clone(),
216 values,
217 })
218 }
219 }
220 };
221}
222
223impl_binary_op_with_immutable_borrow! {Add, add, +, 3.0}
224impl_binary_op_with_immutable_borrow! {Sub, sub, -, 1.0}
225impl_binary_op_with_immutable_borrow! {Mul, mul, *, 2.0}
226impl_binary_op_with_immutable_borrow! {Div, div, /, 2.0}
227
228macro_rules! impl_binary_op_with_owned {
229 ($Trait:tt, $method:tt, $ValueAssignTrait:tt, $mathsymbol:tt, $assignmathsymbol:tt, $testresult:tt) => {
230 impl<A: Axis + PartialEq + Clone, V> $Trait<&HashHistogram<A, V>> for HashHistogram<A, V>
231 where
232 HashHistogram<A, V>: Histogram<A, V>,
233 V: Clone + Default,
234 for<'a> V: $ValueAssignTrait<&'a V>,
235 {
236 type Output = Result<HashHistogram<A, V>, crate::error::BinaryOperationError>;
237
238 #[doc=concat!("let combined_hist = (hist1 ", stringify!($mathsymbol), " &hist2).expect(\"Axes are compatible\");")]
256 #[doc=concat!("assert_eq!(combined_hist.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
257 fn $method(mut self, rhs: &HashHistogram<A, V>) -> Self::Output {
260 if self.axes() != rhs.axes() {
261 return Err(crate::error::BinaryOperationError);
262 }
263 for (index, rhs_value) in rhs.values.iter() {
264 let lhs_value = self.values.entry(*index).or_default();
265 *lhs_value $assignmathsymbol rhs_value
266 }
267 Ok(self)
268 }
269 }
270 };
271}
272
273impl_binary_op_with_owned! {Add, add, AddAssign, +, +=, 3.0}
274impl_binary_op_with_owned! {Sub, sub, SubAssign, -, -=, 1.0}
275impl_binary_op_with_owned! {Mul, mul, MulAssign, *, *=, 2.0}
276impl_binary_op_with_owned! {Div, div, DivAssign, /, /=, 2.0}
277
278macro_rules! impl_binary_op_assign {
279 ($Trait:tt, $method:tt, $ValueAssignTrait:tt, $mathsymbol:tt, $testresult:tt) => {
280 impl<A: Axis + PartialEq, V> $Trait<&HashHistogram<A, V>> for HashHistogram<A, V>
281 where
282 HashHistogram<A, V>: Histogram<A, V>,
283 V: Default,
284 for<'a> V: $ValueAssignTrait<&'a V>,
285 {
286
287 #[doc=concat!("hist1 ", stringify!($mathsymbol), " &hist2;")]
306 #[doc=concat!("assert_eq!(hist1.value(&0.0).unwrap(), &", stringify!($testresult), ");")]
307 fn $method(&mut self, rhs: &HashHistogram<A, V>) {
310 if self.axes() != rhs.axes() {
311 panic!("Cannot combine HashHistograms with incompatible axes.");
312 }
313 for (index, rhs_value) in rhs.values.iter() {
314 let lhs_value = self.values.entry(*index).or_default();
315 *lhs_value $mathsymbol rhs_value
316 }
317 }
318 }
319 };
320}
321
322impl_binary_op_assign! {AddAssign, add_assign, AddAssign, +=, 3.0}
323impl_binary_op_assign! {SubAssign, sub_assign, SubAssign, -=, 1.0}
324impl_binary_op_assign! {MulAssign, mul_assign, MulAssign, *=, 2.0}
325impl_binary_op_assign! {DivAssign, div_assign, DivAssign, /=, 2.0}
326
327#[cfg(feature = "rayon")]
328use rayon::prelude::*;
329
330impl<A, V> HashHistogram<A, V> {
334 #[cfg(feature = "rayon")]
339 pub fn par_values(&self) -> impl ParallelIterator<Item = &V>
340 where
341 V: Sync,
342 {
343 self.values.par_iter().map(|it| it.1)
344 }
345
346 #[cfg(feature = "rayon")]
351 pub fn par_values_mut(&mut self) -> impl ParallelIterator<Item = &mut V>
352 where
353 V: Send,
354 {
355 self.values.par_iter_mut().map(|it| it.1)
356 }
357
358 #[cfg(feature = "rayon")]
363 pub fn par_iter(&self) -> impl ParallelIterator<Item = Item<<A as Axis>::BinInterval, &V>>
364 where
365 A: Axis + Sync,
366 V: Sync,
367 <A as Axis>::BinInterval: Send,
368 {
369 self.values.par_iter().map(move |(index, value)| Item {
370 index: *index,
371 bin: self
372 .axes
373 .bin(*index)
374 .expect("We only iterate over valid indices."),
375 value,
376 })
377 }
378
379 #[cfg(feature = "rayon")]
384 pub fn par_iter_mut(
385 &mut self,
386 ) -> impl ParallelIterator<Item = Item<<A as Axis>::BinInterval, &mut V>>
387 where
388 A: Axis + Sync + Send,
389 V: Send + Sync,
390 <A as Axis>::BinInterval: Send,
391 {
392 let axes = &self.axes;
393 self.values.par_iter_mut().map(move |it| Item {
394 index: *it.0,
395 bin: axes
396 .bin(*it.0)
397 .expect("We only iterate over valid indices."),
398 value: it.1,
399 })
400 }
401}