1*a0275efaSAdi Nata // SPDX-License-Identifier: GPL-2.0-only 2*a0275efaSAdi Nata 3*a0275efaSAdi Nata #include <kunit/test.h> 4*a0275efaSAdi Nata #include <linux/polynomial.h> 5*a0275efaSAdi Nata 6*a0275efaSAdi Nata struct polynomial_test_param { 7*a0275efaSAdi Nata const struct polynomial *poly; 8*a0275efaSAdi Nata long data; 9*a0275efaSAdi Nata long expected; 10*a0275efaSAdi Nata const char *name; 11*a0275efaSAdi Nata }; 12*a0275efaSAdi Nata 13*a0275efaSAdi Nata /* f(x) = 5 */ 14*a0275efaSAdi Nata static const struct polynomial poly_constant = { 15*a0275efaSAdi Nata .total_divider = 1, 16*a0275efaSAdi Nata .terms = { 17*a0275efaSAdi Nata {0, 5, 1, 1}, 18*a0275efaSAdi Nata } 19*a0275efaSAdi Nata }; 20*a0275efaSAdi Nata 21*a0275efaSAdi Nata /* f(x) = 2x^2 + 3x + 5 */ 22*a0275efaSAdi Nata static const struct polynomial poly_simple = { 23*a0275efaSAdi Nata .total_divider = 1, 24*a0275efaSAdi Nata .terms = { 25*a0275efaSAdi Nata {2, 2, 1, 1}, 26*a0275efaSAdi Nata {1, 3, 1, 1}, 27*a0275efaSAdi Nata {0, 5, 1, 1}, 28*a0275efaSAdi Nata } 29*a0275efaSAdi Nata }; 30*a0275efaSAdi Nata 31*a0275efaSAdi Nata /* f(x) = -5x + 100 */ 32*a0275efaSAdi Nata static const struct polynomial poly_negative_coef = { 33*a0275efaSAdi Nata .total_divider = 1, 34*a0275efaSAdi Nata .terms = { 35*a0275efaSAdi Nata {1, -5, 1, 1}, 36*a0275efaSAdi Nata {0, 100, 1, 1}, 37*a0275efaSAdi Nata } 38*a0275efaSAdi Nata }; 39*a0275efaSAdi Nata 40*a0275efaSAdi Nata /* f(x) = (150x + 50) / 10 */ 41*a0275efaSAdi Nata static const struct polynomial poly_total_divider = { 42*a0275efaSAdi Nata .total_divider = 10, 43*a0275efaSAdi Nata .terms = { 44*a0275efaSAdi Nata {1, 150, 1, 1}, 45*a0275efaSAdi Nata {0, 50, 1, 1}, 46*a0275efaSAdi Nata } 47*a0275efaSAdi Nata }; 48*a0275efaSAdi Nata 49*a0275efaSAdi Nata /* 50*a0275efaSAdi Nata * f(x) = x / 2 51*a0275efaSAdi Nata * divider=2 applied once per multiply: mult_frac(coef, data, 2) = coef*data/2 52*a0275efaSAdi Nata */ 53*a0275efaSAdi Nata static const struct polynomial poly_step_divider = { 54*a0275efaSAdi Nata .total_divider = 1, 55*a0275efaSAdi Nata .terms = { 56*a0275efaSAdi Nata {1, 1, 2, 1}, 57*a0275efaSAdi Nata {0, 0, 1, 1}, 58*a0275efaSAdi Nata } 59*a0275efaSAdi Nata }; 60*a0275efaSAdi Nata 61*a0275efaSAdi Nata /* 62*a0275efaSAdi Nata * f(x) = (100/500) * x^2 = 0.2 * x^2 63*a0275efaSAdi Nata * Encoded as coef=100, divider=10, divider_leftover=5: 64*a0275efaSAdi Nata * denom = 10^2 * 5 = 500 65*a0275efaSAdi Nata */ 66*a0275efaSAdi Nata static const struct polynomial poly_leftover = { 67*a0275efaSAdi Nata .total_divider = 1, 68*a0275efaSAdi Nata .terms = { 69*a0275efaSAdi Nata {2, 100, 10, 5}, 70*a0275efaSAdi Nata {0, 0, 1, 1}, 71*a0275efaSAdi Nata } 72*a0275efaSAdi Nata }; 73*a0275efaSAdi Nata 74*a0275efaSAdi Nata /* 75*a0275efaSAdi Nata * f(x) = 2x^3 (single high-degree term, no constant) 76*a0275efaSAdi Nata * Used to exercise the power loop alone. 77*a0275efaSAdi Nata */ 78*a0275efaSAdi Nata static const struct polynomial poly_cubic = { 79*a0275efaSAdi Nata .total_divider = 1, 80*a0275efaSAdi Nata .terms = { 81*a0275efaSAdi Nata {3, 2, 1, 1}, 82*a0275efaSAdi Nata {0, 0, 1, 1}, 83*a0275efaSAdi Nata } 84*a0275efaSAdi Nata }; 85*a0275efaSAdi Nata 86*a0275efaSAdi Nata /* 87*a0275efaSAdi Nata * f(x) = 4x + 1 with a zero-coefficient quadratic term. 88*a0275efaSAdi Nata * The deg-2 term contributes nothing regardless of input. 89*a0275efaSAdi Nata */ 90*a0275efaSAdi Nata static const struct polynomial poly_zero_coef = { 91*a0275efaSAdi Nata .total_divider = 1, 92*a0275efaSAdi Nata .terms = { 93*a0275efaSAdi Nata {2, 0, 1, 1}, 94*a0275efaSAdi Nata {1, 4, 1, 1}, 95*a0275efaSAdi Nata {0, 1, 1, 1}, 96*a0275efaSAdi Nata } 97*a0275efaSAdi Nata }; 98*a0275efaSAdi Nata 99*a0275efaSAdi Nata /* 100*a0275efaSAdi Nata * f(x) = 9 with total_divider = 0. 101*a0275efaSAdi Nata * The implementation treats 0 as 1 via `total_divider ?: 1`, so the 102*a0275efaSAdi Nata * result must equal the constant term unchanged. 103*a0275efaSAdi Nata */ 104*a0275efaSAdi Nata static const struct polynomial poly_zero_total_divider = { 105*a0275efaSAdi Nata .total_divider = 0, 106*a0275efaSAdi Nata .terms = { 107*a0275efaSAdi Nata {0, 9, 1, 1}, 108*a0275efaSAdi Nata } 109*a0275efaSAdi Nata }; 110*a0275efaSAdi Nata 111*a0275efaSAdi Nata 112*a0275efaSAdi Nata static const struct polynomial_test_param test_params[] = { 113*a0275efaSAdi Nata { 114*a0275efaSAdi Nata .poly = &poly_constant, 115*a0275efaSAdi Nata .data = 0, 116*a0275efaSAdi Nata .expected = 5, 117*a0275efaSAdi Nata .name = "Constant polynomial at x=0", 118*a0275efaSAdi Nata }, 119*a0275efaSAdi Nata { 120*a0275efaSAdi Nata .poly = &poly_constant, 121*a0275efaSAdi Nata .data = 42, 122*a0275efaSAdi Nata .expected = 5, 123*a0275efaSAdi Nata .name = "Constant polynomial is independent of input", 124*a0275efaSAdi Nata }, 125*a0275efaSAdi Nata { 126*a0275efaSAdi Nata .poly = &poly_simple, 127*a0275efaSAdi Nata .data = 0, 128*a0275efaSAdi Nata .expected = 5, /* zero input collapses all power terms */ 129*a0275efaSAdi Nata .name = "Zero input yields constant term only", 130*a0275efaSAdi Nata }, 131*a0275efaSAdi Nata { 132*a0275efaSAdi Nata .poly = &poly_simple, 133*a0275efaSAdi Nata .data = 10, 134*a0275efaSAdi Nata .expected = 235, /* 2*100 + 3*10 + 5 */ 135*a0275efaSAdi Nata .name = "Simple quadratic at x=10", 136*a0275efaSAdi Nata }, 137*a0275efaSAdi Nata { 138*a0275efaSAdi Nata .poly = &poly_negative_coef, 139*a0275efaSAdi Nata .data = 10, 140*a0275efaSAdi Nata .expected = 50, /* -5*10 + 100 */ 141*a0275efaSAdi Nata .name = "Negative coefficient at x=10", 142*a0275efaSAdi Nata }, 143*a0275efaSAdi Nata { 144*a0275efaSAdi Nata .poly = &poly_negative_coef, 145*a0275efaSAdi Nata .data = 20, 146*a0275efaSAdi Nata .expected = 0, /* -5*20 + 100 = 0 */ 147*a0275efaSAdi Nata .name = "Negative coefficient result is zero", 148*a0275efaSAdi Nata }, 149*a0275efaSAdi Nata { 150*a0275efaSAdi Nata .poly = &poly_total_divider, 151*a0275efaSAdi Nata .data = 3, 152*a0275efaSAdi Nata .expected = 50, /* (150*3 + 50) / 10 = 500/10 */ 153*a0275efaSAdi Nata .name = "total_divider scales the final sum", 154*a0275efaSAdi Nata }, 155*a0275efaSAdi Nata { 156*a0275efaSAdi Nata .poly = &poly_step_divider, 157*a0275efaSAdi Nata .data = 100, 158*a0275efaSAdi Nata .expected = 50, /* 1*100/2 */ 159*a0275efaSAdi Nata .name = "Per-step divider halves input", 160*a0275efaSAdi Nata }, 161*a0275efaSAdi Nata { 162*a0275efaSAdi Nata .poly = &poly_leftover, 163*a0275efaSAdi Nata .data = 30, 164*a0275efaSAdi Nata .expected = 180, /* 100*30^2 / (10^2 * 5) = 90000/500 */ 165*a0275efaSAdi Nata .name = "divider_leftover with quadratic term", 166*a0275efaSAdi Nata }, 167*a0275efaSAdi Nata /* Boundary: unit and negative-unit input */ 168*a0275efaSAdi Nata { 169*a0275efaSAdi Nata /* 170*a0275efaSAdi Nata * data=1: each mult_frac(tmp, 1, divider) strips one factor of 171*a0275efaSAdi Nata * divider from coef per degree, so coef is left-shifted right 172*a0275efaSAdi Nata * until intermediate precision is exhausted. 173*a0275efaSAdi Nata * 2*1 + 3*1 + 5 = 10 174*a0275efaSAdi Nata */ 175*a0275efaSAdi Nata .poly = &poly_simple, 176*a0275efaSAdi Nata .data = 1, 177*a0275efaSAdi Nata .expected = 10, 178*a0275efaSAdi Nata .name = "Boundary: data=1 (unit input)", 179*a0275efaSAdi Nata }, 180*a0275efaSAdi Nata { 181*a0275efaSAdi Nata /* 182*a0275efaSAdi Nata * data=-1: even degrees produce positive contributions, 183*a0275efaSAdi Nata * odd degrees produce negative ones. 184*a0275efaSAdi Nata * 2*(-1)^2 + 3*(-1) + 5 = 2 - 3 + 5 = 4 185*a0275efaSAdi Nata */ 186*a0275efaSAdi Nata .poly = &poly_simple, 187*a0275efaSAdi Nata .data = -1, 188*a0275efaSAdi Nata .expected = 4, 189*a0275efaSAdi Nata .name = "Boundary: data=-1 (negative unit input)", 190*a0275efaSAdi Nata }, 191*a0275efaSAdi Nata 192*a0275efaSAdi Nata /* Boundary: negative non-trivial input */ 193*a0275efaSAdi Nata { 194*a0275efaSAdi Nata /* 195*a0275efaSAdi Nata * 2*(-3)^2 + 3*(-3) + 5 = 18 - 9 + 5 = 14 196*a0275efaSAdi Nata * Verifies sign handling for negative data across all degrees. 197*a0275efaSAdi Nata */ 198*a0275efaSAdi Nata .poly = &poly_simple, 199*a0275efaSAdi Nata .data = -3, 200*a0275efaSAdi Nata .expected = 14, 201*a0275efaSAdi Nata .name = "Boundary: negative data with quadratic", 202*a0275efaSAdi Nata }, 203*a0275efaSAdi Nata 204*a0275efaSAdi Nata /* Boundary: total_divider = 0 is treated as 1 */ 205*a0275efaSAdi Nata { 206*a0275efaSAdi Nata .poly = &poly_zero_total_divider, 207*a0275efaSAdi Nata .data = 42, 208*a0275efaSAdi Nata .expected = 9, 209*a0275efaSAdi Nata .name = "Boundary: total_divider=0 defaults to 1", 210*a0275efaSAdi Nata }, 211*a0275efaSAdi Nata 212*a0275efaSAdi Nata /* Boundary: zero-coefficient high-degree term */ 213*a0275efaSAdi Nata { 214*a0275efaSAdi Nata /* 215*a0275efaSAdi Nata * The deg-2 term has coef=0, so it contributes 0 regardless 216*a0275efaSAdi Nata * of data. Result: 0 + 4*10 + 1 = 41 217*a0275efaSAdi Nata */ 218*a0275efaSAdi Nata .poly = &poly_zero_coef, 219*a0275efaSAdi Nata .data = 10, 220*a0275efaSAdi Nata .expected = 41, 221*a0275efaSAdi Nata .name = "Boundary: zero-coefficient term is inert", 222*a0275efaSAdi Nata }, 223*a0275efaSAdi Nata 224*a0275efaSAdi Nata /* Boundary: single high-degree term, no constant */ 225*a0275efaSAdi Nata { 226*a0275efaSAdi Nata /* 2 * 5^3 = 250; also verifies the loop terminates on deg-0 */ 227*a0275efaSAdi Nata .poly = &poly_cubic, 228*a0275efaSAdi Nata .data = 5, 229*a0275efaSAdi Nata .expected = 250, 230*a0275efaSAdi Nata .name = "Boundary: single cubic term", 231*a0275efaSAdi Nata }, 232*a0275efaSAdi Nata { 233*a0275efaSAdi Nata /* 2 * (-2)^3 = -16; odd power preserves sign of negative data */ 234*a0275efaSAdi Nata .poly = &poly_cubic, 235*a0275efaSAdi Nata .data = -2, 236*a0275efaSAdi Nata .expected = -16, 237*a0275efaSAdi Nata .name = "Boundary: single cubic term, negative data", 238*a0275efaSAdi Nata }, 239*a0275efaSAdi Nata 240*a0275efaSAdi Nata }; 241*a0275efaSAdi Nata 242*a0275efaSAdi Nata static void get_desc(const struct polynomial_test_param *param, char *desc) 243*a0275efaSAdi Nata { 244*a0275efaSAdi Nata strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE); 245*a0275efaSAdi Nata } 246*a0275efaSAdi Nata 247*a0275efaSAdi Nata KUNIT_ARRAY_PARAM(polynomial, test_params, get_desc); 248*a0275efaSAdi Nata 249*a0275efaSAdi Nata static void polynomial_calc_test(struct kunit *test) 250*a0275efaSAdi Nata { 251*a0275efaSAdi Nata const struct polynomial_test_param *param = test->param_value; 252*a0275efaSAdi Nata 253*a0275efaSAdi Nata KUNIT_EXPECT_EQ(test, polynomial_calc(param->poly, param->data), 254*a0275efaSAdi Nata param->expected); 255*a0275efaSAdi Nata } 256*a0275efaSAdi Nata 257*a0275efaSAdi Nata static struct kunit_case polynomial_test_cases[] = { 258*a0275efaSAdi Nata KUNIT_CASE_PARAM(polynomial_calc_test, polynomial_gen_params), 259*a0275efaSAdi Nata {} 260*a0275efaSAdi Nata }; 261*a0275efaSAdi Nata 262*a0275efaSAdi Nata static struct kunit_suite polynomial_test_suite = { 263*a0275efaSAdi Nata .name = "math-polynomial", 264*a0275efaSAdi Nata .test_cases = polynomial_test_cases, 265*a0275efaSAdi Nata }; 266*a0275efaSAdi Nata 267*a0275efaSAdi Nata kunit_test_suites(&polynomial_test_suite); 268*a0275efaSAdi Nata 269*a0275efaSAdi Nata MODULE_DESCRIPTION("math.polynomial_calc KUnit test suite"); 270*a0275efaSAdi Nata MODULE_LICENSE("GPL"); 271