use crate::num::arithmetic::traits::{SaturatingAbs, UnsignedAbs};
use crate::num::basic::integers::PrimitiveInt;
use crate::num::basic::signeds::PrimitiveSigned;
use crate::num::basic::traits::Zero;
use crate::num::basic::unsigneds::PrimitiveUnsigned;
use crate::num::conversion::traits::WrappingFrom;
use crate::num::float::NiceFloat;
use crate::test_util::stats::common_values_map::common_values_map;
use crate::test_util::stats::median;
use crate::test_util::stats::median::{
deleted_uniform_primitive_int_median, double_geometric_median, double_nonzero_geometric_median,
truncated_geometric_median, uniform_bool_median, uniform_primitive_int_median,
};
use itertools::Itertools;
use std::fmt::{self, Debug, Formatter};
pub trait CheckedToF64 {
fn checked_to_f64(&self) -> f64;
}
macro_rules! impl_checked_to_f64_for_primitive_ints {
($t: ident) => {
impl CheckedToF64 for $t {
#[allow(clippy::cast_lossless)]
#[inline]
fn checked_to_f64(&self) -> f64 {
*self as f64
}
}
};
}
apply_to_primitive_ints!(impl_checked_to_f64_for_primitive_ints);
impl CheckedToF64 for bool {
#[inline]
fn checked_to_f64(&self) -> f64 {
if *self { 1.0 } else { 0.0 }
}
}
impl CheckedToF64 for f32 {
#[inline]
fn checked_to_f64(&self) -> f64 {
f64::from(*self)
}
}
impl CheckedToF64 for f64 {
#[inline]
fn checked_to_f64(&self) -> f64 {
*self
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct MomentStats {
pub mean: NiceFloat<f64>,
pub standard_deviation: NiceFloat<f64>,
pub skewness: NiceFloat<f64>,
pub excess_kurtosis: NiceFloat<f64>,
}
impl Debug for MomentStats {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_fmt(format_args!(
"MomentStats {{ mean: NiceFloat({}), standard_deviation: NiceFloat({}), \
skewness: NiceFloat({}), excess_kurtosis: NiceFloat({}) }}",
self.mean, self.standard_deviation, self.skewness, self.excess_kurtosis
))
}
}
#[allow(clippy::suspicious_operation_groupings)]
pub fn moment_stats<I: Iterator>(xs: I) -> MomentStats
where
I::Item: CheckedToF64,
{
let mut n: usize = 1;
let mut m = 0.0;
let mut m_2 = 0.0;
let mut m_3 = 0.0;
let mut m_4 = 0.0;
for x in xs {
let d = x.checked_to_f64() - m;
let nf = n.checked_to_f64();
let d_n = d / nf;
let d_n_2 = d_n * d_n;
let d_n_3 = d_n_2 * d_n;
let m_4_1 = (nf - 1.0) * (nf * nf - 3.0 * nf + 3.0) * d_n_3 * d;
let m_4_2 = m_2 * d_n_2 * 6.0;
let m_4_3 = m_3 * d_n * 4.0;
m_4 += m_4_1 + m_4_2 - m_4_3;
let m_3_1 = (nf - 1.0) * (nf - 2.0) * d_n_2 * d;
let m_3_2 = m_2 * d_n * 3.0;
m_3 += m_3_1 - m_3_2;
m_2 += (nf - 1.0) * d_n * d;
m += d_n;
n += 1;
}
n -= 1;
let nf = n.checked_to_f64();
let mean = m;
let standard_deviation = (m_2 / (nf - 1.0)).sqrt();
let sqrt_m_2 = m_2.sqrt();
let skewness = m_3 * nf.sqrt() / (sqrt_m_2 * sqrt_m_2 * sqrt_m_2);
let excess_kurtosis = m_4 * nf / (m_2 * m_2) - 3.0;
MomentStats {
mean: NiceFloat(mean),
standard_deviation: NiceFloat(standard_deviation),
skewness: NiceFloat(skewness),
excess_kurtosis: NiceFloat(excess_kurtosis),
}
}
pub const NAN_MOMENT_STATS: MomentStats = MomentStats {
mean: NiceFloat(f64::NAN),
standard_deviation: NiceFloat(f64::NAN),
skewness: NiceFloat(f64::NAN),
excess_kurtosis: NiceFloat(f64::NAN),
};
pub fn mean<I: Iterator>(xs: I) -> NiceFloat<f64>
where
I::Item: CheckedToF64,
{
let mut n: usize = 1;
let mut m = 0.0;
for x in xs {
let d_n = (x.checked_to_f64() - m) / n.checked_to_f64();
m += d_n;
n += 1;
}
NiceFloat(m)
}
fn moment_stats_from_raw_moments(
mean: f64,
p_2_mean: f64,
p_3_mean: f64,
p_4_mean: f64,
) -> MomentStats {
let mean_2 = mean * mean;
let mean_3 = mean_2 * mean;
let mean_4 = mean_2 * mean_2;
let variance = p_2_mean - mean_2;
let standard_deviation = variance.sqrt();
let mu_3 = p_3_mean - 3.0 * mean * p_2_mean + 2.0 * mean_3;
let mu_4 = p_4_mean - 4.0 * mean * p_3_mean + 6.0 * mean_2 * p_2_mean - 3.0 * mean_4;
MomentStats {
mean: NiceFloat(mean),
standard_deviation: NiceFloat(standard_deviation),
skewness: NiceFloat(mu_3 / (variance * standard_deviation)),
excess_kurtosis: NiceFloat(mu_4 / (variance * variance) - 3.0),
}
}
#[inline]
fn pop_uniform_mean<T: CheckedToF64>(a: &T, b: &T) -> f64 {
(a.checked_to_f64() + b.checked_to_f64()) / 2.0
}
fn pop_uniform_standard_deviation<T: CheckedToF64>(a: &T, b: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
let n = b - a + 1.0;
((n * n - 1.0) / 12.0).sqrt()
}
fn pop_uniform_skewness<T: CheckedToF64>(a: &T, b: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
if a == b { f64::NAN } else { 0.0 }
}
fn pop_uniform_excess_kurtosis<T: CheckedToF64>(a: &T, b: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
if a == b {
f64::NAN
} else {
let n = b - a + 1.0;
let n_squared = n * n;
6.0 / 5.0 * (-1.0 - 2.0 / (-1.0 + n_squared))
}
}
pub fn pop_disc_uniform_dist_moment_stats<T: CheckedToF64>(a: &T, b: &T) -> MomentStats {
MomentStats {
mean: NiceFloat(pop_uniform_mean(a, b)),
standard_deviation: NiceFloat(pop_uniform_standard_deviation(a, b)),
skewness: NiceFloat(pop_uniform_skewness(a, b)),
excess_kurtosis: NiceFloat(pop_uniform_excess_kurtosis(a, b)),
}
}
pub fn uniform_bool_assertions<I: Clone + Iterator<Item = bool>>(
xs: I,
a: bool,
b: bool,
expected_values: &[bool],
expected_common_values: &[(bool, usize)],
expected_pop_median: (bool, Option<bool>),
expected_sample_median: (bool, Option<bool>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) {
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
uniform_bool_median(a, b),
actual_sample_median,
pop_disc_uniform_dist_moment_stats(&a, &b),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
pub fn uniform_primitive_int_assertions<I: Clone + Iterator>(
xs: I,
a: I::Item,
b: I::Item,
expected_values: &[I::Item],
expected_common_values: &[(I::Item, usize)],
expected_pop_median: (I::Item, Option<I::Item>),
expected_sample_median: (I::Item, Option<I::Item>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) where
I::Item: CheckedToF64 + PrimitiveInt,
{
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
uniform_primitive_int_median(a, b),
actual_sample_median,
pop_disc_uniform_dist_moment_stats(&a, &b),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
fn pop_deleted_disc_uniform_dist_mean<T: CheckedToF64>(a: &T, b: &T, c: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
let c = c.checked_to_f64();
(a - a * a + b + b * b - 2.0 * c) / (2.0 * (b - a))
}
fn pop_deleted_disc_uniform_dist_2_mean<T: CheckedToF64>(a: &T, b: &T, c: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
let c = c.checked_to_f64();
let a_2 = a * a;
let a_3 = a_2 * a;
(a - 3.0 * a_2 + 2.0 * a_3 - b * (1.0 + b) * (1.0 + 2.0 * b) + 6.0 * c * c) / (6.0 * (a - b))
}
fn pop_deleted_disc_uniform_dist_3_mean<T: CheckedToF64>(a: &T, b: &T, c: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
let c = c.checked_to_f64();
let a_2 = a * a;
let b_2 = b * b;
let a_m_1 = a - 1.0;
let b_p_1 = b + 1.0;
(a_m_1 * a_m_1 * a_2 - b_2 * b_p_1 * b_p_1 + 4.0 * c * c * c) / (4.0 * (a - b))
}
fn pop_deleted_disc_uniform_dist_4_mean<T: CheckedToF64>(a: &T, b: &T, c: &T) -> f64 {
let a = a.checked_to_f64();
let b = b.checked_to_f64();
let c = c.checked_to_f64();
let a_2 = a * a;
let a_3 = a_2 * a;
let a_4 = a_2 * a_2;
let a_5 = a_3 * a_2;
let b_2 = b * b;
let b_3 = b_2 * b;
let b_4 = b_2 * b_2;
let b_5 = b_3 * b_2;
let c_2 = c * c;
(-a + 10.0 * a_3 - 15.0 * a_4 + 6.0 * a_5 + b - 10.0 * b_3 - 15.0 * b_4 - 6.0 * b_5
+ 30.0 * c_2 * c_2)
/ (30.0 * (a - b))
}
fn deleted_uniform_primitive_int_moment_stats<T: CheckedToF64 + Ord>(
a: &T,
b: &T,
c: &T,
) -> MomentStats {
assert!(a <= c);
assert!(c <= b);
moment_stats_from_raw_moments(
pop_deleted_disc_uniform_dist_mean(a, b, c),
pop_deleted_disc_uniform_dist_2_mean(a, b, c),
pop_deleted_disc_uniform_dist_3_mean(a, b, c),
pop_deleted_disc_uniform_dist_4_mean(a, b, c),
)
}
pub fn deleted_uniform_primitive_int_assertions<I: Clone + Iterator>(
xs: I,
a: I::Item,
b: I::Item,
c: I::Item,
expected_values: &[I::Item],
expected_common_values: &[(I::Item, usize)],
expected_pop_median: (I::Item, Option<I::Item>),
expected_sample_median: (I::Item, Option<I::Item>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) where
I::Item: CheckedToF64 + PrimitiveInt,
{
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
deleted_uniform_primitive_int_median(a, b, c),
actual_sample_median,
deleted_uniform_primitive_int_moment_stats(&a, &b, &c),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
fn pop_truncated_geometric_dist_mean(max: f64, unadjusted_mean: f64) -> f64 {
let m = max;
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
(q - q.powf(m) * q * (1.0 + m * p)) / (p - q.powf(1.0 + m) * p)
}
fn pop_truncated_geometric_dist_2_mean(max: f64, unadjusted_mean: f64) -> f64 {
let m = max;
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let p_2 = p * p;
(2.0 - 3.0 * p + p_2 - q.powf(m) * q * (2.0 + p * (-1.0 + m * (2.0 + m * p))))
/ ((1.0 - q.powf(1.0 + m)) * p_2)
}
fn pop_truncated_geometric_dist_3_mean(max: f64, unadjusted_mean: f64) -> f64 {
let m = max;
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let p_2 = p * p;
let p_3 = p_2 * p;
(6.0 - 12.0 * p + 7.0 * p_2
- p_3
- q.powf(m) * q * (6.0 + p * (-6.0 + p + m * (6.0 + p * (-3.0 + m * (3.0 + m * p))))))
/ ((1.0 - q.powf(1.0 + m)) * p_3)
}
fn pop_truncated_geometric_dist_4_mean(max: f64, unadjusted_mean: f64) -> f64 {
let m = max;
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let m_2 = m * m;
let m_4 = m_2 * m_2;
let p_2 = p * p;
let p_3 = p_2 * p;
let p_4 = p_2 * p_2;
(1.0 / ((1.0 - q.powf(1.0 + m)) * p_4))
* (24.0 - 60.0 * p + 50.0 * p_2 - 15.0 * p_3 + p_4
- q.powf(m)
* q
* (24.0
+ p * (-36.0
+ 24.0 * m
+ 2.0 * (7.0 + 6.0 * (-2.0 + m) * m) * p
+ (-1.0 + 2.0 * m * (2.0 + m * (-3.0 + 2.0 * m))) * p_2
+ m_4 * p_3)))
}
fn pop_truncated_geometric_dist_moment_stats(
min: f64,
max: f64,
unadjusted_mean: f64,
) -> MomentStats {
assert!(min <= max);
assert!(min <= unadjusted_mean); let unadjusted_mean = unadjusted_mean - min;
let max = max - min;
let mut stats = moment_stats_from_raw_moments(
pop_truncated_geometric_dist_mean(max, unadjusted_mean),
pop_truncated_geometric_dist_2_mean(max, unadjusted_mean),
pop_truncated_geometric_dist_3_mean(max, unadjusted_mean),
pop_truncated_geometric_dist_4_mean(max, unadjusted_mean),
);
stats.mean = NiceFloat(stats.mean.0 + min);
stats
}
pub fn truncated_geometric_dist_assertions<I: Clone + Iterator>(
xs: I,
min: I::Item,
max: I::Item,
um_numerator: u64,
um_denominator: u64,
expected_values: &[I::Item],
expected_common_values: &[(I::Item, usize)],
expected_pop_median: (I::Item, Option<I::Item>),
expected_sample_median: (I::Item, Option<I::Item>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) where
I::Item: CheckedToF64 + PrimitiveInt,
{
let min_64 = min.checked_to_f64();
let max_64 = max.checked_to_f64();
let unadjusted_mean = um_numerator as f64 / um_denominator as f64;
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
truncated_geometric_median(min, max, unadjusted_mean),
actual_sample_median,
pop_truncated_geometric_dist_moment_stats(min_64, max_64, unadjusted_mean),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
pub fn negative_truncated_geometric_dist_assertions<
I: Clone + Iterator<Item = S>,
S: CheckedToF64 + PrimitiveSigned + UnsignedAbs<Output = U> + WrappingFrom<U>,
U: CheckedToF64 + PrimitiveUnsigned,
>(
xs: I,
abs_min: S,
abs_max: S,
abs_um_numerator: u64,
abs_um_denominator: u64,
expected_values: &[S],
expected_common_values: &[(S, usize)],
expected_pop_median: (S, Option<S>),
expected_sample_median: (S, Option<S>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) {
let abs_min_64 = -abs_min.checked_to_f64();
let abs_max_64 = -abs_max.checked_to_f64();
let abs_unadjusted_mean = abs_um_numerator as f64 / abs_um_denominator as f64;
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
let mut pop_sample_moment_stats =
pop_truncated_geometric_dist_moment_stats(abs_min_64, abs_max_64, abs_unadjusted_mean);
pop_sample_moment_stats.mean = NiceFloat(-pop_sample_moment_stats.mean.0);
pop_sample_moment_stats.skewness = NiceFloat(-pop_sample_moment_stats.skewness.0);
let (x, y) = truncated_geometric_median(
abs_min.unsigned_abs(),
abs_max.unsigned_abs(),
abs_unadjusted_mean,
);
let (x, y) = y.map_or((S::wrapping_from(x.wrapping_neg()), None), |y| {
(
S::wrapping_from(y.wrapping_neg()),
Some(S::wrapping_from(x.wrapping_neg())),
)
});
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
(x, y),
actual_sample_median,
pop_sample_moment_stats,
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
fn pop_double_nonzero_truncated_geometric_dist_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
(qpa * (1.0 + a * p) - qpb * (1.0 + b * p)) / ((-2.0 + qpa + qpb) * p)
}
fn pop_double_nonzero_truncated_geometric_dist_2_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
(2.0 + (p * (2.0 - qpa + a * qpa * (2.0 + a * p) + qpb * (-1.0 + b * (2.0 + b * p))))
/ (-2.0 + qpa + qpb))
/ (p * p)
}
fn pop_double_nonzero_truncated_geometric_dist_3_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
(qpa * (6.0 + p * (-6.0 + p + a * (6.0 + p * (-3.0 + a * (3.0 + a * p)))))
- qpb * (6.0 + p * (-6.0 + p + b * (6.0 + p * (-3.0 + b * (3.0 + b * p))))))
/ ((-2.0 + qpa + qpb) * p * p * p)
}
fn pop_double_nonzero_truncated_geometric_dist_4_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let a_2 = a * a;
let a_3 = a_2 * a;
let a_4 = a_2 * a_2;
let b_2 = b * b;
let b_4 = b_2 * b_2;
let p_2 = p * p;
let p_3 = p_2 * p;
let p_4 = p_2 * p_2;
let qpa = q.powf(a);
let qpb = q.powf(b);
1.0 / p_4
* (24.0
+ 1.0 / (-2.0 + qpa + qpb)
* p
* (-6.0 * a_2 * qpa * (-2.0 + p) * p + 4.0 * a_3 * qpa * p_2 + a_4 * qpa * p_3
- (-2.0 + qpa) * (36.0 + (-14.0 + p) * p)
+ 4.0 * a * qpa * (6.0 + (-6.0 + p) * p)
+ qpb
* (-36.0
+ 24.0 * b
+ 2.0 * (7.0 + 6.0 * (-2.0 + b) * b) * p
+ (-1.0 + 2.0 * b * (2.0 + b * (-3.0 + 2.0 * b))) * p_2
+ b_4 * p_3)))
}
fn pop_double_nonzero_truncated_geometric_dist_moment_stats(
min: f64,
max: f64,
unadjusted_mean: f64,
) -> MomentStats {
assert!(min < 0.0);
assert!(max > 0.0);
assert!(unadjusted_mean > 1.0); moment_stats_from_raw_moments(
pop_double_nonzero_truncated_geometric_dist_mean(-min, max, unadjusted_mean - 1.0),
pop_double_nonzero_truncated_geometric_dist_2_mean(-min, max, unadjusted_mean - 1.0),
pop_double_nonzero_truncated_geometric_dist_3_mean(-min, max, unadjusted_mean - 1.0),
pop_double_nonzero_truncated_geometric_dist_4_mean(-min, max, unadjusted_mean - 1.0),
)
}
pub fn double_nonzero_truncated_geometric_dist_assertions<I: Clone + Iterator>(
xs: I,
min: I::Item,
max: I::Item,
um_numerator: u64,
um_denominator: u64,
expected_values: &[I::Item],
expected_common_values: &[(I::Item, usize)],
expected_abs_mean: NiceFloat<f64>,
expected_pop_median: (I::Item, Option<I::Item>),
expected_sample_median: (I::Item, Option<I::Item>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) where
I::Item: CheckedToF64 + PrimitiveSigned,
{
let min_64 = min.checked_to_f64();
let max_64 = max.checked_to_f64();
let unadjusted_mean = um_numerator as f64 / um_denominator as f64;
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_abs_mean = mean(xs.clone().map(SaturatingAbs::saturating_abs).take(1000000));
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
actual_abs_mean,
double_nonzero_geometric_median(min, max, unadjusted_mean),
actual_sample_median,
pop_double_nonzero_truncated_geometric_dist_moment_stats(
min_64,
max_64,
unadjusted_mean,
),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_abs_mean,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}
fn pop_double_truncated_geometric_dist_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
q * (qpa * (1.0 + a * p) - q.powf(b) * (1.0 + b * p))
/ (p * (-2.0 + qpa + q.powf(1.0 + b) + p - qpa * p))
}
#[allow(clippy::suspicious_operation_groupings)]
fn pop_double_truncated_geometric_dist_2_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
q * (2.0 * (-2.0 + qpa + qpb)
+ p * (2.0 - qpa + a * qpa * (2.0 + a * p) + qpb * (-1.0 + b * (2.0 + b * p))))
/ (p * p * (-2.0 + qpa + q.powf(1.0 + b) + p - qpa * p))
}
fn pop_double_truncated_geometric_dist_3_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
q * (qpa * (6.0 + p * (-6.0 + p + a * (6.0 + p * (-3.0 + a * (3.0 + a * p)))))
- qpb * (6.0 + p * (-6.0 + p + b * (6.0 + p * (-3.0 + b * (3.0 + b * p))))))
/ (p * p * p * (-2.0 + qpa + q.powf(1.0 + b) + p - qpa * p))
}
fn pop_double_truncated_geometric_dist_4_mean(b: f64, a: f64, unadjusted_mean: f64) -> f64 {
let p = 1.0 / (unadjusted_mean + 1.0);
let q = 1.0 - p;
let qpa = q.powf(a);
let qpb = q.powf(b);
let a_2 = a * a;
let a_3 = a_2 * a;
let a_4 = a_2 * a_2;
let b_2 = b * b;
let b_4 = b_2 * b_2;
let p_2 = p * p;
let p_3 = p_2 * p;
let p_4 = p_2 * p_2;
q * (24.0 * (-2.0 + qpa + qpb)
+ p * (-6.0 * a_2 * qpa * (-2.0 + p) * p + 4.0 * a_3 * qpa * p_2 + a_4 * qpa * p_3
- (-2.0 + qpa) * (36.0 + (-14.0 + p) * p)
+ 4.0 * a * qpa * (6.0 + (-6.0 + p) * p)
+ qpb
* (-36.0
+ 24.0 * b
+ 2.0 * (7.0 + 6.0 * (-2.0 + b) * b) * p
+ (-1.0 + 2.0 * b * (2.0 + b * (-3.0 + 2.0 * b))) * p_2
+ b_4 * p_3)))
/ (p_4 * (-2.0 + qpa + q.powf(1.0 + b) + p - qpa * p))
}
fn pop_double_truncated_geometric_dist_moment_stats(
min: f64,
max: f64,
unadjusted_mean: f64,
) -> MomentStats {
assert!(min < 0.0);
assert!(max > 0.0);
assert!(unadjusted_mean > 0.0); moment_stats_from_raw_moments(
pop_double_truncated_geometric_dist_mean(-min, max, unadjusted_mean),
pop_double_truncated_geometric_dist_2_mean(-min, max, unadjusted_mean),
pop_double_truncated_geometric_dist_3_mean(-min, max, unadjusted_mean),
pop_double_truncated_geometric_dist_4_mean(-min, max, unadjusted_mean),
)
}
pub fn double_truncated_geometric_dist_assertions<I: Clone + Iterator>(
xs: I,
min: I::Item,
max: I::Item,
um_numerator: u64,
um_denominator: u64,
expected_values: &[I::Item],
expected_common_values: &[(I::Item, usize)],
expected_natural_mean: NiceFloat<f64>,
expected_pop_median: (I::Item, Option<I::Item>),
expected_sample_median: (I::Item, Option<I::Item>),
expected_pop_moment_stats: MomentStats,
expected_sample_moment_stats: MomentStats,
) where
I::Item: CheckedToF64 + PrimitiveSigned,
{
let min_64 = min.checked_to_f64();
let max_64 = max.checked_to_f64();
let unadjusted_mean = um_numerator as f64 / um_denominator as f64;
let actual_values = xs.clone().take(20).collect_vec();
let actual_common_values = common_values_map(1000000, 10, xs.clone());
let actual_natural_mean = mean(xs.clone().filter(|&x| x >= I::Item::ZERO).take(1000000));
let actual_sample_median = median(xs.clone().take(1000000));
let actual_sample_moment_stats = moment_stats(xs.take(1000000));
assert_eq!(
(
actual_values.as_slice(),
actual_common_values.as_slice(),
actual_natural_mean,
double_geometric_median(min, max, unadjusted_mean),
actual_sample_median,
pop_double_truncated_geometric_dist_moment_stats(min_64, max_64, unadjusted_mean),
actual_sample_moment_stats
),
(
expected_values,
expected_common_values,
expected_natural_mean,
expected_pop_median,
expected_sample_median,
expected_pop_moment_stats,
expected_sample_moment_stats
)
);
}