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