projects/linux·blob master · refs

270 lines · 5.9 KiB

1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <kunit/test.h>
4#include <linux/polynomial.h>
5
6struct polynomial_test_param {
7 const struct polynomial *poly;
8 long data;
9 long expected;
10 const char *name;
11};
12
13/* f(x) = 5 */
14static 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 */
22static 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 */
32static 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 */
41static 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 */
53static 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 */
66static 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 */
78static 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 */
90static 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 */
104static const struct polynomial poly_zero_total_divider = {
105 .total_divider = 0,
106 .terms = {
107 {0, 9, 1, 1},
108 }
109};
110
111
112static 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
242static void get_desc(const struct polynomial_test_param *param, char *desc)
243{
244 strscpy(desc, param->name, KUNIT_PARAM_DESC_SIZE);
245}
246
247KUNIT_ARRAY_PARAM(polynomial, test_params, get_desc);
248
249static 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
257static struct kunit_case polynomial_test_cases[] = {
258 KUNIT_CASE_PARAM(polynomial_calc_test, polynomial_gen_params),
259 {}
260};
261
262static struct kunit_suite polynomial_test_suite = {
263 .name = "math-polynomial",
264 .test_cases = polynomial_test_cases,
265};
266
267kunit_test_suites(&polynomial_test_suite);
268
269MODULE_DESCRIPTION("math.polynomial_calc KUnit test suite");
270MODULE_LICENSE("GPL");