|
|
|
@ -26,73 +26,63 @@ impl Bucket { |
|
|
|
/// A struct representing the options to build an histogram.
|
|
|
|
/// A struct representing the options to build an histogram.
|
|
|
|
pub struct Histogram { |
|
|
|
pub struct Histogram { |
|
|
|
vec: Vec<Bucket>, |
|
|
|
vec: Vec<Bucket>, |
|
|
|
max: f64, |
|
|
|
|
|
|
|
step: f64, |
|
|
|
step: f64, |
|
|
|
|
|
|
|
// Maximum of all bucket counts
|
|
|
|
top: usize, |
|
|
|
top: usize, |
|
|
|
last: usize, |
|
|
|
last: usize, |
|
|
|
stats: Stats, |
|
|
|
stats: Stats, |
|
|
|
|
|
|
|
log_scale: bool, |
|
|
|
precision: Option<usize>, // If None, then human friendly display will be used
|
|
|
|
precision: Option<usize>, // If None, then human friendly display will be used
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// A struct holding data to plot a Histogram of numerical data.
|
|
|
|
/// A struct holding data to plot a Histogram of numerical data.
|
|
|
|
|
|
|
|
#[derive(Default)] |
|
|
|
pub struct HistogramOptions { |
|
|
|
pub struct HistogramOptions { |
|
|
|
/// Maximum number of buckets to use
|
|
|
|
/// `intervals` is the number of histogram buckets to display (capped to the
|
|
|
|
|
|
|
|
/// length of input data).
|
|
|
|
pub intervals: usize, |
|
|
|
pub intervals: usize, |
|
|
|
/// If true, logarithmic scale will be used for buckets
|
|
|
|
/// If true, logarithmic scale will be used for buckets
|
|
|
|
pub log_scale: bool, |
|
|
|
pub log_scale: bool, |
|
|
|
/// If None, then human friendly display will be used
|
|
|
|
/// `precision` is an Option with the number of decimals to display. If
|
|
|
|
|
|
|
|
/// "None" is used, human units will be used, with an heuristic based on the
|
|
|
|
|
|
|
|
/// input data for deciding the units and the decimal places.
|
|
|
|
pub precision: Option<usize>, |
|
|
|
pub precision: Option<usize>, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl Default for HistogramOptions { |
|
|
|
|
|
|
|
fn default() -> Self { |
|
|
|
|
|
|
|
Self { |
|
|
|
|
|
|
|
intervals: 10, |
|
|
|
|
|
|
|
log_scale: false, |
|
|
|
|
|
|
|
precision: None, |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
impl Histogram { |
|
|
|
impl Histogram { |
|
|
|
/// Creates a Histogram from a vector of numerical data.
|
|
|
|
/// Creates a Histogram from a vector of numerical data.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// `intervals` is the number of histogram buckets to display (capped to the
|
|
|
|
/// `options` is a `HistogramOptions` struct with the preferences to create
|
|
|
|
/// length of input data).
|
|
|
|
/// histogram.
|
|
|
|
///
|
|
|
|
|
|
|
|
/// `precision` is an Option with the number of decimals to display. If
|
|
|
|
|
|
|
|
/// "None" is used, human units will be used, with an heuristic based on the
|
|
|
|
|
|
|
|
/// input data for deciding the units and the decimal places.
|
|
|
|
|
|
|
|
pub fn new(vec: &[f64], mut options: HistogramOptions) -> Self { |
|
|
|
pub fn new(vec: &[f64], mut options: HistogramOptions) -> Self { |
|
|
|
options.intervals = options.intervals.min(vec.len()); |
|
|
|
let mut stats = Stats::new(vec, options.precision); |
|
|
|
let stats = Stats::new(vec, options.precision); |
|
|
|
if options.log_scale { |
|
|
|
let size = options.intervals.min(vec.len()); |
|
|
|
stats.min = 0.0; // We will silently discard negative values
|
|
|
|
let step = (stats.max - stats.min) / size as f64; |
|
|
|
} |
|
|
|
let mut histogram = Self::new_with_stats(step, stats, &options); |
|
|
|
options.intervals = options.intervals.clamp(1, vec.len()); |
|
|
|
|
|
|
|
let mut histogram = Self::new_with_stats(stats, &options); |
|
|
|
histogram.load(vec); |
|
|
|
histogram.load(vec); |
|
|
|
histogram |
|
|
|
histogram |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Creates a Histogram with no input data.
|
|
|
|
/// Creates a Histogram with no input data.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
///
|
|
|
|
|
|
|
|
/// Parameters are similar to those on the `new` method, but a parameter
|
|
|
|
/// Parameters are similar to those on the `new` method, but a parameter
|
|
|
|
/// named `stats` is needed to decide how future data (to be injected with
|
|
|
|
/// named `stats` is needed to decide how future data (to be injected with
|
|
|
|
/// the load method) will be accommodated.
|
|
|
|
/// the load method) will be accommodated.
|
|
|
|
pub fn new_with_stats(step: f64, stats: Stats, options: &HistogramOptions) -> Self { |
|
|
|
pub fn new_with_stats(stats: Stats, options: &HistogramOptions) -> Self { |
|
|
|
let mut vec = Vec::<Bucket>::with_capacity(options.intervals); |
|
|
|
let step = if options.log_scale { |
|
|
|
let mut lower = stats.min; |
|
|
|
f64::NAN |
|
|
|
for _ in 0..options.intervals { |
|
|
|
} else { |
|
|
|
vec.push(Bucket::new(lower..lower + step)); |
|
|
|
(stats.max - stats.min) / options.intervals as f64 |
|
|
|
lower += step; |
|
|
|
}; |
|
|
|
} |
|
|
|
|
|
|
|
Self { |
|
|
|
Self { |
|
|
|
vec, |
|
|
|
vec: Self::build_buckets(stats.min..stats.max, options), |
|
|
|
max: step.mul_add(options.intervals as f64, stats.min), |
|
|
|
|
|
|
|
step, |
|
|
|
step, |
|
|
|
top: 0, |
|
|
|
top: 0, |
|
|
|
last: options.intervals - 1, |
|
|
|
last: options.intervals - 1, |
|
|
|
stats, |
|
|
|
stats, |
|
|
|
|
|
|
|
log_scale: options.log_scale, |
|
|
|
precision: options.precision, |
|
|
|
precision: options.precision, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -113,12 +103,43 @@ impl Histogram { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn find_slot(&self, n: f64) -> Option<usize> { |
|
|
|
fn find_slot(&self, n: f64) -> Option<usize> { |
|
|
|
if n < self.stats.min || n > self.max { |
|
|
|
if n < self.stats.min || n > self.stats.max { |
|
|
|
None |
|
|
|
return None; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if self.log_scale { |
|
|
|
|
|
|
|
let mut bucket = None; |
|
|
|
|
|
|
|
for i in 0..self.vec.len() { |
|
|
|
|
|
|
|
if self.vec[i].range.end >= n { |
|
|
|
|
|
|
|
bucket = Some(i); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bucket |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
Some((((n - self.stats.min) / self.step) as usize).min(self.last)) |
|
|
|
Some((((n - self.stats.min) / self.step) as usize).min(self.last)) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn build_buckets(range: Range<f64>, options: &HistogramOptions) -> Vec<Bucket> { |
|
|
|
|
|
|
|
let mut vec = Vec::<Bucket>::with_capacity(options.intervals); |
|
|
|
|
|
|
|
if options.log_scale { |
|
|
|
|
|
|
|
let first_bucket_size = range.end / (2_f64.powi(options.intervals as i32) - 1.0); |
|
|
|
|
|
|
|
let mut lower = 0.0; |
|
|
|
|
|
|
|
for i in 0..options.intervals { |
|
|
|
|
|
|
|
let upper = lower + 2_f64.powi(i as i32) * first_bucket_size; |
|
|
|
|
|
|
|
vec.push(Bucket::new(lower..upper)); |
|
|
|
|
|
|
|
lower = upper; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
let step = (range.end - range.start) / options.intervals as f64; |
|
|
|
|
|
|
|
let mut lower = range.start; |
|
|
|
|
|
|
|
for _ in 0..options.intervals { |
|
|
|
|
|
|
|
vec.push(Bucket::new(lower..lower + step)); |
|
|
|
|
|
|
|
lower += step; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
vec |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl fmt::Display for Histogram { |
|
|
|
impl fmt::Display for Histogram { |
|
|
|
@ -180,7 +201,7 @@ impl HistWriter { |
|
|
|
self.formatter |
|
|
|
self.formatter |
|
|
|
.format(hist.stats.min) |
|
|
|
.format(hist.stats.min) |
|
|
|
.len() |
|
|
|
.len() |
|
|
|
.max(self.formatter.format(hist.max).len()) |
|
|
|
.max(self.formatter.format(hist.stats.max).len()) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn get_max_bar_len(&self, fixed_width: usize) -> usize { |
|
|
|
fn get_max_bar_len(&self, fixed_width: usize) -> usize { |
|
|
|
@ -196,6 +217,7 @@ impl HistWriter { |
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
mod tests { |
|
|
|
mod tests { |
|
|
|
use super::*; |
|
|
|
use super::*; |
|
|
|
|
|
|
|
use float_eq::assert_float_eq; |
|
|
|
use yansi::Paint; |
|
|
|
use yansi::Paint; |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
@ -205,18 +227,18 @@ mod tests { |
|
|
|
intervals: 8, |
|
|
|
intervals: 8, |
|
|
|
..Default::default() |
|
|
|
..Default::default() |
|
|
|
}; |
|
|
|
}; |
|
|
|
let mut hist = Histogram::new_with_stats(2.5, stats, &options); |
|
|
|
let mut hist = Histogram::new_with_stats(stats, &options); |
|
|
|
hist.load(&[ |
|
|
|
hist.load(&[ |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
]); |
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
assert_eq!(hist.top, 8); |
|
|
|
assert_eq!(hist.top, 5); |
|
|
|
let bucket = &hist.vec[0]; |
|
|
|
let bucket = &hist.vec[0]; |
|
|
|
assert_eq!(bucket.range, -2.0..0.5); |
|
|
|
assert_eq!(bucket.range, -2.0..0.0); |
|
|
|
assert_eq!(bucket.count, 3); |
|
|
|
assert_eq!(bucket.count, 3); |
|
|
|
let bucket = &hist.vec[1]; |
|
|
|
let bucket = &hist.vec[1]; |
|
|
|
assert_eq!(bucket.count, 8); |
|
|
|
assert_eq!(bucket.count, 5); |
|
|
|
assert_eq!(bucket.range, 0.5..3.0); |
|
|
|
assert_eq!(bucket.range, 0.0..2.0); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
@ -225,7 +247,7 @@ mod tests { |
|
|
|
intervals: 6, |
|
|
|
intervals: 6, |
|
|
|
..Default::default() |
|
|
|
..Default::default() |
|
|
|
}; |
|
|
|
}; |
|
|
|
let mut hist = Histogram::new_with_stats(1.0, Stats::new(&[-2.0, 4.0], None), &options); |
|
|
|
let mut hist = Histogram::new_with_stats(Stats::new(&[-2.0, 4.0], None), &options); |
|
|
|
hist.load(&[-1.0, 2.0, -1.0, 2.0, 10.0, 10.0, 10.0, -10.0]); |
|
|
|
hist.load(&[-1.0, 2.0, -1.0, 2.0, 10.0, 10.0, 10.0, -10.0]); |
|
|
|
assert_eq!(hist.top, 2); |
|
|
|
assert_eq!(hist.top, 2); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -236,17 +258,19 @@ mod tests { |
|
|
|
let options = HistogramOptions { |
|
|
|
let options = HistogramOptions { |
|
|
|
intervals: 8, |
|
|
|
intervals: 8, |
|
|
|
precision: Some(3), |
|
|
|
precision: Some(3), |
|
|
|
log_scale: false, |
|
|
|
..Default::default() |
|
|
|
}; |
|
|
|
}; |
|
|
|
let mut hist = Histogram::new_with_stats(2.5, stats, &options); |
|
|
|
let mut hist = Histogram::new_with_stats(stats, &options); |
|
|
|
hist.load(&[ |
|
|
|
hist.load(&[ |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
]); |
|
|
|
]); |
|
|
|
Paint::disable(); |
|
|
|
Paint::disable(); |
|
|
|
let display = format!("{hist}"); |
|
|
|
let display = format!("{hist}"); |
|
|
|
assert!(display.contains("[-2.000 .. 0.500] [3] ∎∎∎\n")); |
|
|
|
assert!(display.contains("[-2.000 .. 0.000] [3] ∎∎∎\n")); |
|
|
|
assert!(display.contains("[ 0.500 .. 3.000] [8] ∎∎∎∎∎∎∎∎\n")); |
|
|
|
assert!(display.contains("[ 0.000 .. 2.000] [5] ∎∎∎∎∎\n")); |
|
|
|
assert!(display.contains("[10.500 .. 13.000] [2] ∎∎\n")); |
|
|
|
assert!(display.contains("[ 2.000 .. 4.000] [3] ∎∎∎\n")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 6.000 .. 8.000] [0] \n")); |
|
|
|
|
|
|
|
assert!(display.contains("[10.000 .. 12.000] [2] ∎∎\n")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
@ -254,15 +278,15 @@ mod tests { |
|
|
|
let options = HistogramOptions { |
|
|
|
let options = HistogramOptions { |
|
|
|
intervals: 8, |
|
|
|
intervals: 8, |
|
|
|
precision: Some(3), |
|
|
|
precision: Some(3), |
|
|
|
log_scale: false, |
|
|
|
..Default::default() |
|
|
|
}; |
|
|
|
}; |
|
|
|
let mut hist = Histogram::new_with_stats(2.5, Stats::new(&[-2.0, 14.0], None), &options); |
|
|
|
let mut hist = Histogram::new_with_stats(Stats::new(&[-2.0, 14.0], None), &options); |
|
|
|
hist.load(&[ |
|
|
|
hist.load(&[ |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
-1.0, -1.1, 2.0, 2.0, 2.1, -0.9, 11.0, 11.2, 1.9, 1.99, 1.98, 1.97, 1.96, |
|
|
|
]); |
|
|
|
]); |
|
|
|
Paint::disable(); |
|
|
|
Paint::disable(); |
|
|
|
let display = format!("{hist:2}"); |
|
|
|
let display = format!("{hist:2}"); |
|
|
|
assert!(display.contains("[-2.000 .. 0.500] [3] ∎∎∎\n")); |
|
|
|
assert!(display.contains("[-2.000 .. 0.000] [3] ∎∎∎\n")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
#[test] |
|
|
|
@ -277,7 +301,13 @@ mod tests { |
|
|
|
500000.0, |
|
|
|
500000.0, |
|
|
|
500000.0, |
|
|
|
500000.0, |
|
|
|
]; |
|
|
|
]; |
|
|
|
let hist = Histogram::new(vector, HistogramOptions::default()); |
|
|
|
let hist = Histogram::new( |
|
|
|
|
|
|
|
vector, |
|
|
|
|
|
|
|
HistogramOptions { |
|
|
|
|
|
|
|
intervals: 10, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
Paint::disable(); |
|
|
|
Paint::disable(); |
|
|
|
let display = format!("{hist}"); |
|
|
|
let display = format!("{hist}"); |
|
|
|
assert!(display.contains("[-12.0 M .. -10.4 M] [4] ∎∎∎∎\n")); |
|
|
|
assert!(display.contains("[-12.0 M .. -10.4 M] [4] ∎∎∎∎\n")); |
|
|
|
@ -286,4 +316,121 @@ mod tests { |
|
|
|
assert!(display.contains("Samples = 8; Min = -12.0 M; Max = 0.5 M")); |
|
|
|
assert!(display.contains("Samples = 8; Min = -12.0 M; Max = 0.5 M")); |
|
|
|
assert!(display.contains("Average = -6.1 M;")); |
|
|
|
assert!(display.contains("Average = -6.1 M;")); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn display_test_log_scale() { |
|
|
|
|
|
|
|
let hist = Histogram::new( |
|
|
|
|
|
|
|
&[0.4, 0.4, 0.4, 0.4, 255.0, 0.2, 1.2, 128.0, 126.0, -7.0], |
|
|
|
|
|
|
|
HistogramOptions { |
|
|
|
|
|
|
|
intervals: 8, |
|
|
|
|
|
|
|
log_scale: true, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
Paint::disable(); |
|
|
|
|
|
|
|
let display = format!("{hist}"); |
|
|
|
|
|
|
|
assert!(display.contains("[ 0.00 .. 1.00] [5] ∎∎∎∎∎\n")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 1.00 .. 3.00] [1] ∎\n")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 3.00 .. 7.00] [0]")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 7.00 .. 15.00] [0]")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 15.00 .. 31.00] [0]")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 31.00 .. 63.00] [0]")); |
|
|
|
|
|
|
|
assert!(display.contains("[ 63.00 .. 127.00] [1] ∎\n")); |
|
|
|
|
|
|
|
assert!(display.contains("[127.00 .. 255.00] [2] ∎∎\n")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn build_buckets_log_scale() { |
|
|
|
|
|
|
|
let options = HistogramOptions { |
|
|
|
|
|
|
|
intervals: 8, |
|
|
|
|
|
|
|
log_scale: true, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let buckets = Histogram::build_buckets(0.0..2.0_f64.powi(8) - 1.0, &options); |
|
|
|
|
|
|
|
assert!(buckets.len() == 8); |
|
|
|
|
|
|
|
assert!(buckets[0].range == (0.0..1.0)); |
|
|
|
|
|
|
|
assert!(buckets[1].range == (1.0..3.0)); |
|
|
|
|
|
|
|
assert!(buckets[2].range == (3.0..7.0)); |
|
|
|
|
|
|
|
assert!(buckets[3].range == (7.0..15.0)); |
|
|
|
|
|
|
|
assert!(buckets[4].range == (15.0..31.0)); |
|
|
|
|
|
|
|
assert!(buckets[5].range == (31.0..63.0)); |
|
|
|
|
|
|
|
assert!(buckets[6].range == (63.0..127.0)); |
|
|
|
|
|
|
|
assert!(buckets[7].range == (127.0..255.0)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn build_buckets_log_scale_with_math() { |
|
|
|
|
|
|
|
let options = HistogramOptions { |
|
|
|
|
|
|
|
intervals: 10, |
|
|
|
|
|
|
|
log_scale: true, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let buckets = Histogram::build_buckets(0.0..10000.0, &options); |
|
|
|
|
|
|
|
assert!(buckets.len() == 10); |
|
|
|
|
|
|
|
for i in 0..9 { |
|
|
|
|
|
|
|
assert_float_eq!( |
|
|
|
|
|
|
|
2.0 * (buckets[i].range.end - buckets[i].range.start), |
|
|
|
|
|
|
|
buckets[i + 1].range.end - buckets[i + 1].range.start, |
|
|
|
|
|
|
|
rmax <= 2.0 * f64::EPSILON |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
assert_float_eq!( |
|
|
|
|
|
|
|
buckets[9].range.end - buckets[0].range.start, |
|
|
|
|
|
|
|
10000.0, |
|
|
|
|
|
|
|
rmax <= 2.0 * f64::EPSILON |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn build_buckets_no_log_scale() { |
|
|
|
|
|
|
|
let options = HistogramOptions { |
|
|
|
|
|
|
|
intervals: 7, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let buckets = Histogram::build_buckets(0.0..700.0, &options); |
|
|
|
|
|
|
|
assert!(buckets.len() == 7); |
|
|
|
|
|
|
|
for i in 0..6 { |
|
|
|
|
|
|
|
let min = (i * 100) as f64; |
|
|
|
|
|
|
|
let max = ((i + 1) * 100) as f64; |
|
|
|
|
|
|
|
assert!(buckets[i].range == (min..max)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn find_slot_linear() { |
|
|
|
|
|
|
|
let options = HistogramOptions { |
|
|
|
|
|
|
|
intervals: 8, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
let hist = Histogram::new_with_stats(Stats::new(&[-12.0, 4.0], None), &options); |
|
|
|
|
|
|
|
assert!(hist.find_slot(-13.0) == None); |
|
|
|
|
|
|
|
assert!(hist.find_slot(13.0) == None); |
|
|
|
|
|
|
|
assert!(hist.find_slot(-12.0) == Some(0)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(-11.0) == Some(0)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(-9.0) == Some(1)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(4.0) == Some(7)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(1.1) == Some(6)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
|
|
|
fn find_slot_logarithmic() { |
|
|
|
|
|
|
|
let hist = Histogram::new( |
|
|
|
|
|
|
|
// More than 8 values to avoid interval truncation
|
|
|
|
|
|
|
|
&[255.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -2000.0], |
|
|
|
|
|
|
|
HistogramOptions { |
|
|
|
|
|
|
|
intervals: 8, |
|
|
|
|
|
|
|
log_scale: true, |
|
|
|
|
|
|
|
..Default::default() |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
assert!(hist.find_slot(-1.0) == None); |
|
|
|
|
|
|
|
assert!(hist.find_slot(0.0) == Some(0)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(0.5) == Some(0)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(1.5) == Some(1)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(8.75) == Some(3)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(33.1) == Some(5)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(127.1) == Some(7)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(247.1) == Some(7)); |
|
|
|
|
|
|
|
assert!(hist.find_slot(1000.0) == None); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|