// SPDX-License-Identifier: GPL-2.0
#include <kunit/test.h>
#include "mean_and_variance.h"
#define MAX_SQR (SQRT_U64_MAX*SQRT_U64_MAX)
static void mean_and_variance_basic_test(struct kunit *test)
{
struct mean_and_variance s = {};
mean_and_variance_update(&s, 2 );
mean_and_variance_update(&s, 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(s), 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_get_variance(s), 0 );
KUNIT_EXPECT_EQ(test, s.n, 2 );
mean_and_variance_update(&s, 4 );
mean_and_variance_update(&s, 4 );
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(s), 3 );
KUNIT_EXPECT_EQ(test, mean_and_variance_get_variance(s), 1 );
KUNIT_EXPECT_EQ(test, s.n, 4 );
}
/*
* Test values computed using a spreadsheet from the psuedocode at the bottom :
* https : //fanf2.user.srcf.net/hermes/doc/antiforgery/stats.pdf
*/
static void mean_and_variance_weighted_test(struct kunit *test)
{
struct mean_and_variance_weighted s = { };
mean_and_variance_weighted_update(&s, 10 , false , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), 10 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 0 );
mean_and_variance_weighted_update(&s, 20 , true , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), 12 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 18 );
mean_and_variance_weighted_update(&s, 30 , true , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), 16 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 72 );
s = (struct mean_and_variance_weighted) { };
mean_and_variance_weighted_update(&s, -10 , false , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), -10 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 0 );
mean_and_variance_weighted_update(&s, -20 , true , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), -12 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 18 );
mean_and_variance_weighted_update(&s, -30 , true , 2 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 2 ), -16 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 2 ), 72 );
}
static void mean_and_variance_weighted_advanced_test(struct kunit *test)
{
struct mean_and_variance_weighted s = { };
bool initted = false ;
s64 i;
for (i = 10 ; i <= 100 ; i += 10 ) {
mean_and_variance_weighted_update(&s, i, initted, 8 );
initted = true ;
}
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 8 ), 11 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 8 ), 107 );
s = (struct mean_and_variance_weighted) { };
initted = false ;
for (i = -10 ; i >= -100 ; i -= 10 ) {
mean_and_variance_weighted_update(&s, i, initted, 8 );
initted = true ;
}
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(s, 8 ), -11 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_variance(s, 8 ), 107 );
}
static void do_mean_and_variance_test(struct kunit *test,
s64 initial_value,
s64 initial_n,
s64 n,
unsigned weight,
s64 *data,
s64 *mean,
s64 *stddev,
s64 *weighted_mean,
s64 *weighted_stddev)
{
struct mean_and_variance mv = {};
struct mean_and_variance_weighted vw = { };
for (unsigned i = 0 ; i < initial_n; i++) {
mean_and_variance_update(&mv, initial_value);
mean_and_variance_weighted_update(&vw, initial_value, false , weight);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(mv), initial_value);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_stddev(mv), 0 );
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(vw, weight), initial_value);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_stddev(vw, weight),0 );
}
for (unsigned i = 0 ; i < n; i++) {
mean_and_variance_update(&mv, data[i]);
mean_and_variance_weighted_update(&vw, data[i], true , weight);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_mean(mv), mean[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_get_stddev(mv), stddev[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_mean(vw, weight), weighted_mean[i]);
KUNIT_EXPECT_EQ(test, mean_and_variance_weighted_get_stddev(vw, weight),weighted_stddev[i]);
}
KUNIT_EXPECT_EQ(test, mv.n, initial_n + n);
}
/* Test behaviour with a single outlier, then back to steady state: */
static void mean_and_variance_test_1(struct kunit *test)
{
s64 d[] = { 100 , 10 , 10 , 10 , 10 , 10 , 10 };
s64 mean[] = { 22 , 21 , 20 , 19 , 18 , 17 , 16 };
s64 stddev[] = { 32 , 29 , 28 , 27 , 26 , 25 , 24 };
s64 weighted_mean[] = { 32 , 27 , 22 , 19 , 17 , 15 , 14 };
s64 weighted_stddev[] = { 38 , 35 , 31 , 27 , 24 , 21 , 18 };
do_mean_and_variance_test(test, 10 , 6 , ARRAY_SIZE(d), 2 ,
d, mean, stddev, weighted_mean, weighted_stddev);
}
/* Test behaviour where we switch from one steady state to another: */
static void mean_and_variance_test_2(struct kunit *test)
{
s64 d[] = { 100 , 100 , 100 , 100 , 100 };
s64 mean[] = { 22 , 32 , 40 , 46 , 50 };
s64 stddev[] = { 32 , 39 , 42 , 44 , 45 };
s64 weighted_mean[] = { 32 , 49 , 61 , 71 , 78 };
s64 weighted_stddev[] = { 38 , 44 , 44 , 41 , 38 };
do_mean_and_variance_test(test, 10 , 6 , ARRAY_SIZE(d), 2 ,
d, mean, stddev, weighted_mean, weighted_stddev);
}
static void mean_and_variance_fast_divpow2(struct kunit *test)
{
s64 i;
u8 d;
for (i = 0 ; i < 100 ; i++) {
d = 0 ;
KUNIT_EXPECT_EQ(test, fast_divpow2(i, d), div_u64(i, 1 LLU << d));
KUNIT_EXPECT_EQ(test, abs(fast_divpow2(-i, d)), div_u64(i, 1 LLU << d));
for (d = 1 ; d < 32 ; d++) {
KUNIT_EXPECT_EQ_MSG(test, abs(fast_divpow2(i, d)),
div_u64(i, 1 << d), "%lld %u" , i, d);
KUNIT_EXPECT_EQ_MSG(test, abs(fast_divpow2(-i, d)),
div_u64(i, 1 << d), "%lld %u" , -i, d);
}
}
}
static void mean_and_variance_u128_basic_test(struct kunit *test)
{
u128_u a = u64s_to_u128(0 , U64_MAX);
u128_u a1 = u64s_to_u128(0 , 1 );
u128_u b = u64s_to_u128(1 , 0 );
u128_u c = u64s_to_u128(0 , 1 LLU << 63 );
u128_u c2 = u64s_to_u128(U64_MAX, U64_MAX);
KUNIT_EXPECT_EQ(test, u128_hi(u128_add(a, a1)), 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_add(a, a1)), 0 );
KUNIT_EXPECT_EQ(test, u128_hi(u128_add(a1, a)), 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_add(a1, a)), 0 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_sub(b, a1)), U64_MAX);
KUNIT_EXPECT_EQ(test, u128_hi(u128_sub(b, a1)), 0 );
KUNIT_EXPECT_EQ(test, u128_hi(u128_shl(c, 1 )), 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_shl(c, 1 )), 0 );
KUNIT_EXPECT_EQ(test, u128_hi(u128_square(U64_MAX)), U64_MAX - 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_square(U64_MAX)), 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_div(b, 2 )), 1 LLU << 63 );
KUNIT_EXPECT_EQ(test, u128_hi(u128_div(c2, 2 )), U64_MAX >> 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_div(c2, 2 )), U64_MAX);
KUNIT_EXPECT_EQ(test, u128_hi(u128_div(u128_shl(u64_to_u128(U64_MAX), 32 ), 2 )), U32_MAX >> 1 );
KUNIT_EXPECT_EQ(test, u128_lo(u128_div(u128_shl(u64_to_u128(U64_MAX), 32 ), 2 )), U64_MAX << 31 );
}
static struct kunit_case mean_and_variance_test_cases[] = {
KUNIT_CASE(mean_and_variance_fast_divpow2),
KUNIT_CASE(mean_and_variance_u128_basic_test),
KUNIT_CASE(mean_and_variance_basic_test),
KUNIT_CASE(mean_and_variance_weighted_test),
KUNIT_CASE(mean_and_variance_weighted_advanced_test),
KUNIT_CASE(mean_and_variance_test_1),
KUNIT_CASE(mean_and_variance_test_2),
{}
};
static struct kunit_suite mean_and_variance_test_suite = {
.name = "mean and variance tests" ,
.test_cases = mean_and_variance_test_cases
};
kunit_test_suite(mean_and_variance_test_suite);
MODULE_AUTHOR("Daniel B. Hill" );
MODULE_DESCRIPTION("bcachefs filesystem mean and variance unit tests" );
MODULE_LICENSE("GPL" );
Messung V0.5 in Prozent C=98 H=79 G=88
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland