#[doc(hidden)]
#[macro_export]
macro_rules! define_histogram_common {
($LEN:expr) => (
use $crate::Histogram as Trait;
const LEN: usize = $LEN;
impl ::core::fmt::Debug for Histogram {
fn fmt(&self, formatter: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
formatter.write_str("Histogram {{ range: ")?;
self.range[..].fmt(formatter)?;
formatter.write_str(", bins: ")?;
self.bin[..].fmt(formatter)?;
formatter.write_str(" }}")
}
}
impl Histogram {
#[inline]
pub fn with_const_width(start: f64, end: f64) -> Self {
let step = (end - start) / (LEN as f64);
let mut range = [0.; LEN + 1];
for (i, r) in range.iter_mut().enumerate() {
*r = start + step * (i as f64);
}
Self {
range,
bin: [0; LEN],
}
}
#[inline]
pub fn from_ranges<T>(ranges: T) -> Result<Self, ()>
where T: IntoIterator<Item = f64>
{
let mut range = [0.; LEN + 1];
let mut last_i = 0;
for (i, r) in ranges.into_iter().enumerate() {
if i > LEN {
break;
}
if r.is_nan() {
return Err(());
}
if i > 0 && range[i - 1] > r {
return Err(());
}
range[i] = r;
last_i = i;
}
if last_i != LEN {
return Err(());
}
Ok(Self {
range,
bin: [0; LEN],
})
}
#[inline]
pub fn find(&self, x: f64) -> Result<usize, ()> {
match self.range.binary_search_by(|p| p.partial_cmp(&x).unwrap()) {
Ok(i) if i < LEN => {
Ok(i)
},
Err(i) if i > 0 && i < LEN + 1 => {
Ok(i - 1)
},
_ => {
Err(())
},
}
}
#[inline]
pub fn add(&mut self, x: f64) -> Result<(), ()> {
if let Ok(i) = self.find(x) {
self.bin[i] += 1;
Ok(())
} else {
Err(())
}
}
#[inline]
pub fn ranges(&self) -> &[f64] {
&self.range[..]
}
#[inline]
pub fn iter(&self) -> IterHistogram<'_> {
self.into_iter()
}
#[inline]
pub fn reset(&mut self) {
self.bin = [0; LEN];
}
#[inline]
pub fn range_min(&self) -> f64 {
self.range[0]
}
#[inline]
pub fn range_max(&self) -> f64 {
self.range[LEN]
}
}
pub struct IterHistogram<'a> {
remaining_bin: &'a [u64],
remaining_range: &'a [f64],
}
impl<'a> ::core::iter::Iterator for IterHistogram<'a> {
type Item = ((f64, f64), u64);
fn next(&mut self) -> Option<((f64, f64), u64)> {
if let Some((&bin, rest)) = self.remaining_bin.split_first() {
let left = self.remaining_range[0];
let right = self.remaining_range[1];
self.remaining_bin = rest;
self.remaining_range = &self.remaining_range[1..];
return Some(((left, right), bin));
}
None
}
}
impl<'a> ::core::iter::IntoIterator for &'a Histogram {
type Item = ((f64, f64), u64);
type IntoIter = IterHistogram<'a>;
fn into_iter(self) -> IterHistogram<'a> {
IterHistogram {
remaining_bin: self.bins(),
remaining_range: self.ranges(),
}
}
}
impl $crate::Histogram for Histogram {
#[inline]
fn bins(&self) -> &[u64] {
&self.bin[..]
}
}
impl<'a> ::core::ops::AddAssign<&'a Self> for Histogram {
#[inline]
fn add_assign(&mut self, other: &Self) {
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()) {
*x += y;
}
}
}
impl ::core::ops::MulAssign<u64> for Histogram {
#[inline]
fn mul_assign(&mut self, other: u64) {
for x in &mut self.bin[..] {
*x *= other;
}
}
}
impl $crate::Merge for Histogram {
fn merge(&mut self, other: &Self) {
assert_eq!(self.bin.len(), other.bin.len());
for (a, b) in self.range.iter().zip(other.range.iter()) {
assert_eq!(a, b, "Both histograms must have the same ranges");
}
for (a, b) in self.bin.iter_mut().zip(other.bin.iter()) {
*a += *b;
}
}
}
);
}
#[cfg(feature = "serde1")]
#[doc(hidden)]
#[macro_export]
macro_rules! define_histogram_inner {
($name:ident, $LEN:expr) => (
mod $name {
$crate::define_histogram_common!($LEN);
use ::serde::{Serialize, Deserialize};
serde_big_array::big_array! {
BigArray; LEN, (LEN + 1),
}
#[derive(Clone, Serialize, Deserialize)]
pub struct Histogram {
#[serde(with = "BigArray")]
range: [f64; LEN + 1],
#[serde(with = "BigArray")]
bin: [u64; LEN],
}
}
);
}
#[cfg(not(feature = "serde1"))]
#[doc(hidden)]
#[macro_export]
macro_rules! define_histogram_inner {
($name:ident, $LEN:expr) => (
mod $name {
$crate::define_histogram_common!($LEN);
#[derive(Clone)]
pub struct Histogram {
range: [f64; LEN + 1],
bin: [u64; LEN],
}
}
);
}
#[macro_export]
macro_rules! define_histogram {
($name:ident, $LEN:expr) => ($crate::define_histogram_inner!($name, $LEN););
}