Fixes to Histogram

1. Now histograms with more than 31 bins are supported (before there
   were issues with missing implementations on arrays.)
2. The items defined by `define_histogram!` are in their own module, to
   avoid issues with Rust's lack of macro hygiene for items.
This commit is contained in:
Vinzent Steinberg 2018-07-24 18:18:05 +02:00
parent b010d0cae6
commit 56344750a7
3 changed files with 206 additions and 178 deletions

View File

@ -1,13 +1,20 @@
/// Define a histogram with a number of bins known at compile time. /// Define a histogram with a number of bins known at compile time.
/// ///
/// Because macros are not hygenic for items, everything is defined in a private
/// module with the given name. This includes the `Histogram` struct, the number
/// of bins `LEN` and the histogram iterator `HistogramIter`.
///
///
/// # Example
///
/// ``` /// ```
/// # extern crate core; /// # extern crate core;
/// # #[macro_use] extern crate average; /// # #[macro_use] extern crate average;
/// # fn main() { /// # fn main() {
/// use average::Histogram; /// use average::Histogram;
/// ///
/// define_histogram!(Histogram10, 10); /// define_histogram!(hist, 10);
/// let mut h = Histogram10::with_const_width(0., 100.); /// let mut h = hist::Histogram::with_const_width(0., 100.);
/// for i in 0..100 { /// for i in 0..100 {
/// h.add(i as f64).unwrap(); /// h.add(i as f64).unwrap();
/// } /// }
@ -17,19 +24,33 @@
#[macro_export] #[macro_export]
macro_rules! define_histogram { macro_rules! define_histogram {
($name:ident, $LEN:expr) => ( ($name:ident, $LEN:expr) => (
mod $name {
use $crate::Histogram as Trait;
/// The number of bins of the histogram. /// The number of bins of the histogram.
const LEN: usize = $LEN; const LEN: usize = $LEN;
/// A histogram with a number of bins known at compile time. /// A histogram with a number of bins known at compile time.
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct $name { pub struct Histogram {
/// The ranges defining the bins of the histogram. /// The ranges defining the bins of the histogram.
range: [f64; LEN + 1], range: [f64; LEN + 1],
/// The bins of the histogram. /// The bins of the histogram.
bin: [u64; LEN], bin: [u64; LEN],
} }
impl $name { impl ::core::fmt::Debug for Histogram {
fn fmt(&self, formatter: &mut ::core::fmt::Formatter)
-> ::core::fmt::Result {
write!(formatter, "Histogram {{ range: ")?;
self.range[..].fmt(formatter)?;
write!(formatter, ", bins: ")?;
self.bin[..].fmt(formatter)?;
write!(formatter, " }}")
}
}
impl Histogram {
/// Construct a histogram with constant bin width. /// Construct a histogram with constant bin width.
#[inline] #[inline]
pub fn with_const_width(start: f64, end: f64) -> Self { pub fn with_const_width(start: f64, end: f64) -> Self {
@ -170,7 +191,7 @@ macro_rules! define_histogram {
} }
} }
impl<'a> ::core::iter::IntoIterator for &'a $name { impl<'a> ::core::iter::IntoIterator for &'a Histogram {
type Item = ((f64, f64), u64); type Item = ((f64, f64), u64);
type IntoIter = IterHistogram<'a>; type IntoIter = IterHistogram<'a>;
fn into_iter(self) -> IterHistogram<'a> { fn into_iter(self) -> IterHistogram<'a> {
@ -181,24 +202,26 @@ macro_rules! define_histogram {
} }
} }
impl $crate::Histogram for $name { impl $crate::Histogram for Histogram {
#[inline] #[inline]
fn bins(&self) -> &[u64] { fn bins(&self) -> &[u64] {
&self.bin as &[u64] &self.bin as &[u64]
} }
} }
impl<'a> ::core::ops::AddAssign<&'a Self> for $name { impl<'a> ::core::ops::AddAssign<&'a Self> for Histogram {
#[inline] #[inline]
fn add_assign(&mut self, other: &Self) { fn add_assign(&mut self, other: &Self) {
assert_eq!(self.range, other.range); for (a, b) in self.range.iter().zip(other.range.iter()) {
assert_eq!(a, b, "Both histograms must have the same ranges");
}
for (x, y) in self.bin.iter_mut().zip(other.bin.iter()) { for (x, y) in self.bin.iter_mut().zip(other.bin.iter()) {
*x += y; *x += y;
} }
} }
} }
impl ::core::ops::MulAssign<u64> for $name { impl ::core::ops::MulAssign<u64> for Histogram {
#[inline] #[inline]
fn mul_assign(&mut self, other: u64) { fn mul_assign(&mut self, other: u64) {
for x in self.bin.iter_mut() { for x in self.bin.iter_mut() {
@ -207,7 +230,7 @@ macro_rules! define_histogram {
} }
} }
impl $crate::Merge for $name { impl $crate::Merge for Histogram {
fn merge(&mut self, other: &Self) { fn merge(&mut self, other: &Self) {
assert_eq!(self.bin.len(), other.bin.len()); assert_eq!(self.bin.len(), other.bin.len());
for (a, b) in self.range.iter().zip(other.range.iter()) { for (a, b) in self.range.iter().zip(other.range.iter()) {
@ -218,5 +241,6 @@ macro_rules! define_histogram {
} }
} }
} }
}
); );
} }

View File

@ -64,7 +64,7 @@
//! //!
//! The [`define_histogram`] macro can be used to define a histogram struct that //! The [`define_histogram`] macro can be used to define a histogram struct that
//! uses constant memory. See [`Histogram10`] (defined using //! uses constant memory. See [`Histogram10`] (defined using
//! `define_histogram!(Histogram10, 10)`) and the extension trait [`Histogram`] //! `define_histogram!(..., 10)`) and the extension trait [`Histogram`]
//! for the methods available to the generated struct. //! for the methods available to the generated struct.
//! //!
//! //!
@ -110,5 +110,6 @@ pub use minmax::{Min, Max};
pub use quantile::Quantile; pub use quantile::Quantile;
pub use traits::{Estimate, Merge, Histogram}; pub use traits::{Estimate, Merge, Histogram};
define_histogram!(Histogram10, 10); define_histogram!(hist, 10);
pub use hist::Histogram as Histogram10;
define_moments!(Moments4, 4); define_moments!(Moments4, 4);

View File

@ -9,7 +9,10 @@ use rand::FromEntropy;
use average::{Histogram, Merge}; use average::{Histogram, Merge};
define_histogram!(Histogram10, 10); define_histogram!(hist10, 10);
define_histogram!(hist100, 100);
use hist10::Histogram as Histogram10;
#[test] #[test]
fn with_const_width() { fn with_const_width() {