C(++)ECCO
C++ Error Control COding: a header-only library for ECC simulations and experiments, modeling complete coding systems across arbitrary finite fields and complex inter-field relationships - Christian Senger <senger@inue.uni-stuttgart.de>
Loading...
Searching...
No Matches
polynomials.hpp
Go to the documentation of this file.
1/**
2 * @file polynomials.hpp
3 * @brief Polynomial arithmetic library
4 * @author Christian Senger <senger@inue.uni-stuttgart.de>
5 * @version 2.3.4
6 * @date 2026
7 *
8 * @copyright
9 * Copyright (c) 2026, Christian Senger <senger@inue.uni-stuttgart.de>
10 *
11 * Licensed for noncommercial use only, including academic teaching, research, and personal non-profit purposes.
12 * Commercial use is prohibited without a separate commercial license. See the [LICENSE](../../LICENSE) file in the
13 * repository root for full terms and how to request a commercial license.
14 *
15 * @section Description
16 *
17 * Univariate polynomials over any @ref CECCO::CoefficientType (finite fields, `double`,
18 * `std::complex<double>`, signed integers). Algorithms that need division (long division,
19 * GCD, LCM, derivatives, normalisation, irreducibility test) require @ref CECCO::FieldType
20 * coefficients. Cross-field constructors between two finite fields of the same characteristic
21 * route through @ref CECCO::details::largest_common_subfield_t. Polynomials are stored in
22 * canonical form (leading zero coefficients pruned automatically) and evaluated by Horner.
23 *
24 * @section Usage_Example
25 *
26 * @code{.cpp}
27 * using F7 = Fp<7>;
28 * Polynomial<F7> p = {1, 2, 3}; // 1 + 2x + 3x²
29 * Polynomial<F7> q = {4, 5}; // 4 + 5x
30 * auto r = p * q; // multiplication
31 * F7 v = p(F7(2)); // Horner-method evaluation
32 * auto [quot, rem] = p.poly_long_div(q); // long division
33 * auto g = GCD(p, q); // gcd via extended Euclid
34 *
35 * using F2 = Fp<2>;
36 * using F4 = Ext<F2, MOD{1, 1, 1}>;
37 * Polynomial<F2> a = {1, 0, 1}; // x² + 1 over F₂
38 * Polynomial<F4> b(a); // upcast F₂ ⊆ F₄
39 * @endcode
40 *
41 * @section Performance_Features
42 *
43 * - Hamming weight is lazily computed and cached.
44 * - Move-aware free arithmetic operators, so chained expressions don't copy intermediates.
45 * - Horner's method gives O(n) polynomial evaluation.
46 * - Canonical form (leading zeros pruned) is maintained by the mutating operations.
47 *
48 * @see @ref fields.hpp, @ref vectors.hpp, @ref matrices.hpp, @ref field_concepts_traits.hpp
49 */
50
51#ifndef POLYNOMIALS_HPP
52#define POLYNOMIALS_HPP
53
54#include <initializer_list>
55
56#include "matrices.hpp"
57
58/*
59// transitive
60#include <algorithm>
61#include <complex>
62#include <concepts>
63#include <functional>
64#include <iostream>
65#include <iterator>
66#include <numeric>
67#include <random>
68#include <ranges>
69#include <stdexcept>
70#include <type_traits>
71#include <utility>
72#include <vector>
73
74#include "field_concepts_traits.hpp"
75#include "helpers.hpp"
76*/
77
78namespace CECCO {
79
80template <ComponentType T>
81class Vector;
82template <CoefficientType T>
83class Polynomial;
84
85template <CoefficientType T>
86std::ostream& operator<<(std::ostream& os, const Polynomial<T>& rhs);
87
88template <CoefficientType T>
89const Polynomial<T>& ZeroPolynomial();
90
91/**
92 * @brief Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a @ref CECCO::CoefficientType
93 *
94 * @tparam T Coefficient type satisfying @ref CECCO::CoefficientType (finite field, `double`,
95 * `std::complex<double>`, or signed integer including `InfInt`)
96 *
97 * Coefficients are stored low-to-high in a contiguous buffer; the leading-zero invariant is
98 * maintained by an internal `prune()` so `data.size() - 1` is the degree (when non-empty and
99 * not the zero polynomial). The empty polynomial (no coefficients) is *distinct* from the
100 * zero polynomial: most queries throw `std::invalid_argument` on an empty polynomial.
101 *
102 * Methods that need division are gated by `requires FieldType<T>`; methods that compare
103 * against zero (degree, Hamming weight, …) are gated by `requires ReliablyComparableType<T>`.
104 *
105 * @section Usage_Example
106 *
107 * @code{.cpp}
108 * using F7 = Fp<7>;
109 * Polynomial<F7> p = {1, 2, 3}; // 1 + 2x + 3x²
110 * Polynomial<F7> q = {4, 5}; // 4 + 5x
111 * auto sum = p + q;
112 * auto prod = p * q;
113 * F7 v = p(F7(3)); // evaluation
114 * auto [quot, rem] = p.poly_long_div(q);
115 * size_t w = p.wH(); // Hamming weight (cached)
116 * @endcode
117 */
118template <CoefficientType T>
120 template <ReliablyComparableType U>
121 friend constexpr bool operator==(const Polynomial<U>& lhs, const Polynomial<U>& rhs);
122 friend std::ostream& operator<< <>(std::ostream& os, const Polynomial& rhs);
123
124 public:
125 // Cache configuration for this class
126 enum CacheIds { Weight = 0 };
127
128 /** @name Constructors
129 * @{
130 */
131
132 /// @brief Default constructor: empty polynomial (no coefficients; distinct from the zero polynomial)
133 constexpr Polynomial() noexcept = default;
134
135 /// @brief Constant polynomial from an `int` (constructs `T(e)`)
136 constexpr Polynomial(int e) : data(1) { data.back() = T(e); }
137
138 /// @brief Constant polynomial from a coefficient
139 constexpr Polynomial(const T& e) : data(1) { data.back() = e; }
140
141 /**
142 * @brief From an initializer list of coefficients in ascending degree order
143 *
144 * `l[i]` becomes the coefficient of x^i. Trailing (high-degree) zeros are pruned to keep
145 * canonical form: `Polynomial<int>{1, 2, 3}` represents 1 + 2x + 3x².
146 */
147 constexpr Polynomial(const std::initializer_list<T>& l) : data(l) { prune(); }
148
149 constexpr Polynomial(const Polynomial& other) : data(other.data), cache(other.cache) {}
150 constexpr Polynomial(Polynomial&& other) noexcept : data(std::move(other.data)), cache(std::move(other.cache)) {}
151
152 /**
153 * @brief Cross-field conversion between two finite fields of the same characteristic
154 *
155 * @tparam S Source field type (`Polynomial<S>`); must share characteristic with T
156 *
157 * Converts coefficient by coefficient via T's cross-field constructor, which routes through
158 * @ref CECCO::details::largest_common_subfield_t and so handles disjoint construction towers.
159 * Propagates `std::invalid_argument` if any coefficient is not representable in T.
160 */
161 template <FiniteFieldType S>
163 Polynomial(const Polynomial<S>& other);
164
165 /**
166 * @brief From a coefficient vector: `v[i]` becomes the coefficient of x^i
167 *
168 * Resulting degree is `v.get_n() - 1` (after pruning). For cross-field conversion, convert
169 * the vector first.
170 */
171 Polynomial(const Vector<T>& v);
172
173 /** @} */
174
175 /** @name Assignment Operators
176 * @{
177 */
178
179 /// @brief Assign a scalar: replace `*this` with the constant polynomial @p rhs
180 constexpr Polynomial& operator=(const T& rhs);
181
182 constexpr Polynomial& operator=(const Polynomial<T>& rhs);
183 constexpr Polynomial& operator=(Polynomial&& rhs) noexcept;
184
185 /// @brief Cross-field assignment (same semantics as the cross-field constructor)
186 template <FiniteFieldType S>
189
190 /** @} */
191
192 /** @name Unary Arithmetic Operations
193 * @{
194 */
195
196 /// @brief Unary `+` (lvalue): returns a copy
197 constexpr Polynomial operator+() const& { return *this; }
198 /// @brief Unary `+` (rvalue): returns the rvalue itself
199 constexpr Polynomial operator+() && noexcept { return std::move(*this); }
200
201 /// @brief Unary `−` (lvalue): returns a new polynomial with each coefficient negated
202 constexpr Polynomial operator-() const&;
203 /// @brief Unary `−` (rvalue): negates coefficients in place
204 constexpr Polynomial operator-() &&;
205
206 /**
207 * @brief Evaluate at @p s using Horner's method (O(n))
208 *
209 * @return p(s) = a₀ + a₁s + a₂s² + … + aₙsⁿ
210 * @throws std::invalid_argument if `*this` is empty
211 */
212 T operator()(const T& s) const;
213
214 /** @} */
215
216 /** @name Compound Assignment Operations
217 * @{
218 */
219
220 /// @brief Coefficient-wise addition; expands storage if @p rhs has higher degree, then prunes
221 constexpr Polynomial& operator+=(const Polynomial& rhs);
222 /// @brief Coefficient-wise subtraction; expands storage if @p rhs has higher degree, then prunes
223 constexpr Polynomial& operator-=(const Polynomial& rhs);
224
225 /**
226 * @brief Polynomial multiplication by convolution (O(n²))
227 *
228 * @return Reference to `*this` with degree `deg(this) + deg(rhs)`
229 * @throws std::invalid_argument if either operand is the empty polynomial
230 */
231 constexpr Polynomial& operator*=(const Polynomial& rhs);
232
233 /**
234 * @brief Polynomial division: `*this` becomes the quotient of `(*this) / rhs`
235 *
236 * @throws std::invalid_argument if @p rhs is the zero polynomial
237 */
240
241 /**
242 * @brief Polynomial remainder: `*this` becomes `(*this) mod rhs`
243 *
244 * Specialised path when `deg(this) == deg(rhs)` (one subtraction); long division otherwise.
245 *
246 * @throws std::invalid_argument if @p rhs is the zero polynomial
247 */
250
251 /// @brief Multiply every coefficient by the scalar @p s
252 constexpr Polynomial& operator*=(const T& s);
253
254 /**
255 * @brief Divide every coefficient by the scalar @p s
256 *
257 * @throws std::invalid_argument if @p s == T(0)
258 * @note Round-trip `(p / s) * s == p` is only guaranteed when T satisfies @ref CECCO::FieldType
259 * (otherwise integer rounding may corrupt coefficients).
260 */
261 Polynomial& operator/=(const T& s)
263
264 /// @brief Multiply every coefficient by the integer @p n; reduces @p n modulo characteristic if available
265 constexpr Polynomial& operator*=(size_t n);
266
267 /**
268 * @brief Long division: returns `(q, r)` with `*this == q · rhs + r` and `r == 0` or `deg(r) < deg(rhs)`
269 *
270 * @throws std::invalid_argument if @p rhs is the zero polynomial
271 */
272 std::pair<Polynomial<T>, Polynomial<T>> poly_long_div(const Polynomial<T>& rhs) const
274
275 /** @} */
276
277 /** @name Randomization
278 * @{
279 */
280
281 /**
282 * @brief Replace `*this` with a random polynomial of degree exactly @p d
283 *
284 * The leading coefficient is resampled until non-zero, so the result really has degree d.
285 * Distribution per coefficient: finite-field types draw uniformly from the field; signed
286 * integers from [−100, 100]; `double` and the parts of `std::complex<double>` from [−1, 1].
287 */
289
290 /** @} */
291
292 /** @name Differentiation
293 * @{
294 */
295
296 /**
297 * @brief In-place classical s-th derivative
298 *
299 * d^s/dx^s [∑ aᵢ xⁱ] = ∑_{i ≥ s} (i! / (i − s)!) aᵢ x^{i − s}. The s-th derivative of a
300 * polynomial of degree < s is the zero polynomial. `s == 0` is the identity.
301 *
302 * @throws std::invalid_argument if `*this` is empty
303 */
306
307 /**
308 * @brief In-place s-th Hasse derivative (= classical s-th derivative divided by s!)
309 *
310 * D^{(s)}[∑ aᵢ xⁱ] = ∑_{i ≥ s} C(i, s) aᵢ x^{i − s}. Useful in characteristic p where the
311 * classical derivative loses information whenever s ≥ p.
312 *
313 * @throws std::invalid_argument if `*this` is empty
314 */
317
318 /** @} */
319
320 /** @name Information and Properties
321 * @{
322 */
323
324 /**
325 * @brief Degree max{i : aᵢ ≠ 0}
326 *
327 * @throws std::invalid_argument if `*this` is empty or the zero polynomial (its degree is
328 * -infinity, undefined as a size_t; guard with is_zero() before applying any degree identity)
329 */
332
333 /// @brief Coefficient vector (length = number of stored coefficients; 0 for an empty polynomial)
335
336 /**
337 * @brief Trailing degree min{i : aᵢ ≠ 0}
338 *
339 * @throws std::invalid_argument if `*this` is empty or the zero polynomial
340 */
343
344 /**
345 * @brief Coefficient of the lowest non-zero power
346 *
347 * @throws std::invalid_argument if `*this` is empty or the zero polynomial
348 */
349 const T& trailing_coefficient() const
351
352 /**
353 * @brief Coefficient of the highest non-zero power
354 *
355 * @throws std::invalid_argument if `*this` is empty
356 */
357 const T& leading_coefficient() const;
358
359 /// @brief True iff `*this` has no coefficients (distinct from the zero polynomial)
360 constexpr bool is_empty() const noexcept { return data.empty(); }
361
362 /**
363 * @brief True iff `*this` is the zero polynomial (one coefficient, equal to T(0))
364 *
365 * @throws std::invalid_argument if `*this` is empty
366 */
367 bool is_zero() const
369 {
370 if (is_empty()) throw std::invalid_argument("trying to check whether empty polynomial is zero");
371 return data.size() == 1 && data[0] == T(0);
372 }
373
374 /**
375 * @brief True iff `*this` is the constant polynomial 1
376 *
377 * @throws std::invalid_argument if `*this` is empty
378 */
379 bool is_one() const
381 {
382 if (is_empty()) throw std::invalid_argument("trying to check whether empty polynomial is one");
383 return data.size() == 1 && data[0] == T(1);
384 }
385
386 /**
387 * @brief True iff the leading coefficient equals 1
388 *
389 * @throws std::invalid_argument if `*this` is empty
390 */
391 bool is_monic() const
393 {
394 if (is_empty()) throw std::invalid_argument("trying to check whether empty polynomial is monic");
395 return leading_coefficient() == T(1);
396 }
397
398 /**
399 * @brief Test irreducibility over the coefficient field by Rabin's test
400 *
401 * A polynomial g of degree t over 𝔽_q is irreducible iff x^{q^t} ≡ x (mod g) and
402 * gcd(x^{q^{t/p}} − x, g) = 1 for every prime p dividing t. Runs in time polynomial in t and
403 * log q, using modular exponentiation (@ref poly_long_div) and @ref GCD.
404 *
405 * @throws std::invalid_argument if `*this` is empty
406 */
407 bool is_irreducible() const
409 {
410 if (is_zero()) return false;
411 const size_t t = degree();
412 if (t == 0) return false;
413 if (t == 1) return true;
414
415 const auto& g = *this;
416 Polynomial<T> x(T(0));
417 x.set_coefficient(1, T(1));
418
419 // a ↦ a^q mod g (the Frobenius endomorphism), by square-and-multiply
420 const auto frobenius = [&g](Polynomial<T> a) {
421 Polynomial<T> result(T(1));
422 a = a % g;
423 for (size_t e = T::get_size(); e > 0; e >>= 1) {
424 if (e & 1) result = (result * a) % g;
425 if (e >> 1) a = (a * a) % g;
426 }
427 return result;
428 };
429
430 // x^{q^m} mod g, by iterating the Frobenius m times
431 const auto x_frobenius = [&](size_t m) {
432 Polynomial<T> r = x;
433 for (size_t i = 0; i < m; ++i) r = frobenius(r);
434 return r;
435 };
436
438 size_t m = t;
439 for (size_t p = 2; p * p <= m; ++p)
440 if (m % p == 0) {
442 while (m % p == 0) m /= p;
443 }
444 if (m > 1) primes.push_back(m);
445
446 for (auto p = primes.cbegin(); p != primes.cend(); ++p)
447 if (GCD(g, x_frobenius(t / *p) - x).degree() != 0) return false;
448
449 return x_frobenius(t) == x;
450 }
451
452 /// @brief Hamming weight: number of non-zero, non-erased coefficients; cached on first call
453 size_t wH() const
455 {
456 return cache.template get_or_compute<Weight>([this] { return calculate_weight(); });
457 }
458
459 /** @} */
460
461 /** @name Coefficient Access and Manipulation
462 * @{
463 */
464
465 /**
466 * @brief Add @p c to the coefficient of x^i; grows the polynomial if `i > degree`
467 *
468 * @param i Power of x
469 * @param c Value to add (perfect-forwarded)
470 */
471 template <typename U>
474
475 /**
476 * @brief Set the coefficient of x^i to @p c; grows the polynomial if `i > degree`
477 *
478 * @param i Power of x
479 * @param c New value (perfect-forwarded)
480 */
481 template <typename U>
482 constexpr Polynomial& set_coefficient(size_t i, U&& c)
484
485 /// @brief Reciprocal: reverse coefficients, sending p(x) to xⁿ · p(1/x)
486 constexpr Polynomial& reciprocal();
487
488 /**
489 * @brief Multiply by x^s by prepending `s` zero coefficients (the zero polynomial is unchanged)
490 * @throws std::invalid_argument if `*this` is empty
491 */
492 constexpr Polynomial& shift_up(size_t s)
494
495 /**
496 * @brief Floor-divide by x^s by dropping the `s` lowest coefficients; degree < `s` yields the
497 * zero polynomial
498 * @throws std::invalid_argument if `*this` is empty
499 */
500 constexpr Polynomial& shift_down(size_t s);
501
502 /// @brief Make monic by dividing every coefficient by the leading one (no-op on zero or already-monic)
505
506 /// @brief Coefficient of x^i; returns T(0) for `i > degree`
507 constexpr T operator[](size_t i) const;
508
509 /** @} */
510
511 private:
512 std::vector<T> data;
513
514 /// @brief Cache for Hamming weight (invalidated by mutating operations)
515 mutable details::Cache<details::CacheEntry<Weight, size_t>> cache;
516
517 constexpr size_t calculate_weight() const
519
520 constexpr Polynomial& prune();
521};
522
523/* member functions for Polynomial */
524
525template <CoefficientType T>
526template <FiniteFieldType S>
529 if (other.is_empty()) return;
530 if (other.is_zero()) {
531 data.assign(1, T(0));
532 return;
533 }
534 // cross-field conversion maps nonzero to nonzero, so canonical form survives without prune()
535 data.resize(other.degree() + 1);
536 for (size_t i = 0; i < data.size(); ++i) data[i] = T(other[i]);
537}
538
539template <CoefficientType T>
540Polynomial<T>::Polynomial(const Vector<T>& v) : data(v.get_n()) {
541 for (size_t i = 0; i < data.size(); ++i) data[i] = v[i];
542 prune();
543}
544
545template <CoefficientType T>
546constexpr Polynomial<T>& Polynomial<T>::operator=(const T& rhs) {
547 data.resize(1);
548 data.back() = rhs;
549 if (rhs == T(0))
550 cache.template set<Weight>(0);
551 else
552 cache.template set<Weight>(1);
553 return *this;
554}
555
556template <CoefficientType T>
557constexpr Polynomial<T>& Polynomial<T>::operator=(const Polynomial<T>& rhs) {
558 if (this == &rhs) return *this;
559 if (rhs.data.empty()) {
560 data.clear();
561 cache.invalidate();
562 return *this;
563 }
564 data = rhs.data;
565 cache = rhs.cache;
566 return *this;
567}
568
569template <CoefficientType T>
570constexpr Polynomial<T>& Polynomial<T>::operator=(Polynomial<T>&& rhs) noexcept {
571 if (this == &rhs) return *this;
572 if (rhs.data.empty()) {
573 data.clear();
574 cache.invalidate();
575 return *this;
576 }
577 data = std::move(rhs.data);
578 cache = std::move(rhs.cache);
579 return *this;
580}
581
582template <CoefficientType T>
583template <FiniteFieldType S>
586 if (rhs.is_empty()) {
587 data.clear();
588 } else if (rhs.is_zero()) {
589 data.assign(1, T(0));
590 } else {
591 data.resize(rhs.degree() + 1);
592 for (size_t i = 0; i < data.size(); ++i) data[i] = T(rhs[i]);
593 }
595 return *this;
596}
597
598template <CoefficientType T>
599constexpr Polynomial<T> Polynomial<T>::operator-() const& {
600 Polynomial res(*this);
601 std::ranges::for_each(res.data, [](T& c) { c = -c; });
602 return res;
603}
604
605template <CoefficientType T>
606constexpr Polynomial<T> Polynomial<T>::operator-() && {
607 std::ranges::for_each(data, [](T& c) { c = -c; });
608 return std::move(*this);
609}
610
611template <CoefficientType T>
612T Polynomial<T>::operator()(const T& s) const {
613 if (data.empty()) throw std::invalid_argument("trying to evaluate empty polynomial");
614 if (data.size() == 1) return data.front();
615
616 return std::accumulate(data.crbegin() + 1, data.crend(), data.back(),
617 [&s](const T& acc, const T& coeff) { return acc * s + coeff; });
618}
619
620template <CoefficientType T>
621constexpr Polynomial<T>& Polynomial<T>::operator+=(const Polynomial<T>& rhs) {
622 if (data.size() < rhs.data.size()) data.resize(rhs.data.size(), T(0));
623 std::transform(data.begin(), data.begin() + rhs.data.size(), rhs.data.begin(), data.begin(), std::plus<T>{});
624 cache.invalidate();
625 prune();
626 return *this;
627}
628
629template <CoefficientType T>
630constexpr Polynomial<T>& Polynomial<T>::operator-=(const Polynomial<T>& rhs) {
631 if (data.size() < rhs.data.size()) data.resize(rhs.data.size(), T(0));
632 std::transform(data.begin(), data.begin() + rhs.data.size(), rhs.data.begin(), data.begin(), std::minus<T>{});
633 cache.invalidate();
634 prune();
635 return *this;
636}
637
638template <CoefficientType T>
639constexpr Polynomial<T>& Polynomial<T>::operator*=(const Polynomial<T>& rhs) {
640 if (is_empty() || rhs.is_empty()) throw std::invalid_argument("trying to multiply with empty polynomial");
641 std::vector<T> res(data.size() + rhs.data.size() - 1, T(0));
642 for (size_t i = 0; i < data.size(); ++i) {
643 for (size_t j = 0; j < rhs.data.size(); ++j) res[i + j] += data[i] * rhs.data[j];
644 }
645 data = std::move(res);
646 cache.invalidate();
647 prune();
648 return *this;
649}
650
651template <CoefficientType T>
652Polynomial<T>& Polynomial<T>::operator/=(const Polynomial<T>& rhs)
654{
655 if (rhs.is_zero()) throw std::invalid_argument("division by zero (polynomial)");
656 *this = this->poly_long_div(rhs).first;
658 return *this;
659}
660
661template <CoefficientType T>
662Polynomial<T>& Polynomial<T>::operator%=(const Polynomial<T>& rhs)
664{
665 if (rhs.is_zero()) throw std::invalid_argument("division by zero (polynomial)");
666 if (is_zero()) return *this;
667
668 const size_t d = degree();
669 if (d == rhs.degree()) { // simply subtract polynomial modulus
671 for (size_t i = 0; i < d; ++i) {
672 data[i] -= scalar * rhs[i];
673 }
674 set_coefficient(d, 0);
676 prune();
677 } else { // full-blown polynomial long division
678 auto res = this->poly_long_div(rhs);
679 *this = res.second;
681 }
682
683 return *this;
684}
685
686template <CoefficientType T>
687constexpr Polynomial<T>& Polynomial<T>::operator*=(const T& s) {
688 if (s == T(0)) {
689 cache.template set<Weight>(0);
690 data.resize(1);
691 data.back() = T(0);
692 } else {
693 std::ranges::for_each(data, [&s](T& c) { c *= s; });
694 }
695 return *this;
696}
697
698template <CoefficientType T>
699constexpr Polynomial<T>& Polynomial<T>::operator*=(size_t n) {
700 if constexpr (FieldType<T>) {
701 if (T::get_characteristic() != 0) n = n % T::get_characteristic();
702 }
703 if (n == 0) {
704 cache.template set<Weight>(0);
705 data.resize(1);
706 data.back() = T(0);
707 } else {
708 std::ranges::for_each(data, [&n](T& c) { c *= n; });
709 }
710 return *this;
711}
712
713template <CoefficientType T>
714Polynomial<T>& Polynomial<T>::operator/=(const T& s)
716{
717 if (s == T(0)) throw std::invalid_argument("division by zero (polynomial)");
718 std::ranges::for_each(data, [&s](T& c) { c /= s; });
719 return *this;
720}
721
722template <CoefficientType T>
723std::pair<Polynomial<T>, Polynomial<T>> Polynomial<T>::poly_long_div(const Polynomial<T>& rhs) const
725{
726 if (rhs.is_zero()) throw std::invalid_argument("polynomial long division by zero polynomial");
727
728 const auto rhs_degree = rhs.degree();
729 const auto rhs_lc = rhs.leading_coefficient();
730
731 if (rhs_degree == 0) return std::make_pair(*this / rhs[0], ZeroPolynomial<T>());
732
733 if (is_zero() || degree() < rhs_degree) return std::make_pair(ZeroPolynomial<T>(), *this);
734
735 Polynomial<T> q;
736 Polynomial<T> r = *this;
737
738 while (!r.is_zero()) {
739 const size_t r_degree = r.degree();
740 if (r_degree < rhs_degree) break;
741 const T t = r.leading_coefficient() / rhs_lc;
742 const size_t i = r_degree - rhs_degree;
744 for (size_t j = 0; j <= rhs_degree; ++j) r.add_to_coefficient(i + j, -(t * rhs[j]));
745 }
746
747 return std::make_pair(std::move(q), std::move(r));
748}
749
750template <CoefficientType T>
751Polynomial<T>& Polynomial<T>::randomize(size_t d) {
752 data.resize(d + 1);
753 const T zero(0);
754 if constexpr (FieldType<T>) {
755 std::ranges::for_each(data, std::mem_fn(&T::randomize));
756 do {
757 data.back().randomize();
758 } while (data.back() == zero);
759 } else if constexpr (std::same_as<T, double>) {
760 std::uniform_real_distribution<double> dist(-1.0, 1.0);
761 std::ranges::for_each(data, [&](double& val) { val = dist(gen()); });
762 do {
763 data.back() = dist(gen());
764 } while (data.back() == zero);
765 } else if constexpr (std::same_as<T, std::complex<double>>) {
766 std::uniform_real_distribution<double> dist(-1.0, 1.0);
767 std::ranges::for_each(data,
768 [&](std::complex<double>& val) { val = std::complex<double>(dist(gen()), dist(gen())); });
769 do {
770 data.back() = std::complex<double>(dist(gen()), dist(gen()));
771 } while (data.back() == zero);
772 } else if constexpr (SignedIntType<T>) {
773 std::uniform_int_distribution<long long> dist(-100, 100);
774 std::ranges::for_each(data, [&](T& val) { val = T(dist(gen())); });
775 do {
776 data.back() = dist(gen());
777 } while (data.back() == zero);
778 }
779 cache.invalidate();
780 return *this;
781}
782
783template <CoefficientType T>
786{
787 if (data.empty()) throw std::invalid_argument("trying to differentiate empty polynomial");
788
789 if (s == 0) return *this;
790 const size_t d = data.size() - 1;
791 if (d == 0 || s > d) {
792 data.resize(1);
793 data[0] = T(0);
794 cache.template set<Weight>(0);
795 return *this;
796 }
797 for (size_t i = 0; i <= d - s; ++i) {
798 T coeff(1);
799 if constexpr (T::get_characteristic() != 0) {
800 for (size_t k = 1; k <= s; ++k) coeff *= T((i + k) % T::get_characteristic());
801 } else {
802 for (size_t k = 1; k <= s; ++k) coeff *= T(i + k);
803 }
804 data[i] = coeff * data[i + s];
805 }
806 data.resize(data.size() - s);
807 prune();
809 return *this;
810}
811
812template <CoefficientType T>
815{
816 if (data.empty()) throw std::invalid_argument("trying to Hasse differentiate empty polynomial");
817
818 if (s == 0) return *this;
819 const size_t d = data.size() - 1;
820 if (d == 0 || s > d) {
821 data.resize(1);
822 data[0] = T(0);
823 cache.template set<Weight>(0);
824 return *this;
825 }
826 if constexpr (FiniteFieldType<T>) {
828 for (size_t i = 0; i <= d - s; ++i) data[i] = triangle(i + s, s) * data[i + s];
829 } else {
830 // C(i + s, s) evaluated in T, overflow-free for arbitrary degrees (exact in characteristic 0)
831 for (size_t i = 0; i <= d - s; ++i) {
832 T coeff(1);
833 for (size_t k = 1; k <= s; ++k) coeff *= T(i + k) / T(k);
834 data[i] = coeff * data[i + s];
835 }
836 }
837 data.resize(data.size() - s);
838 prune();
840 return *this;
841}
842
843template <CoefficientType T>
846{
847 if (is_empty()) throw std::invalid_argument("calculating degree of empty polynomial");
848 if (is_zero()) throw std::invalid_argument("calculating degree of zero polynomial");
849 return (data.size() - 1);
850}
851
852template <CoefficientType T>
854 Vector<T> res(data.size());
855 std::copy(data.begin(), data.end(), res.data.begin());
856 res.cache.invalidate(); // raw writes bypass set_component's cache handling
857 return res;
858}
859
860template <CoefficientType T>
863{
864 if (is_empty()) throw std::invalid_argument("calculating trailing degree of empty polynomial");
865 if (is_zero()) throw std::invalid_argument("calculating trailing degree of zero polynomial");
866 if (data.size() == 1) return 0;
867
868 const T zero(0);
869 const auto first_nonzero =
870 std::find_if(data.begin(), data.end(), [&zero](const T& coeff) { return coeff != zero; });
871 return std::distance(data.begin(), first_nonzero);
872}
873
874template <CoefficientType T>
877{
878 if (is_empty())
879 throw std::invalid_argument(
880 "trying to access non-existent element (trailing "
881 "coefficient)");
882 if (is_zero())
883 throw std::invalid_argument(
884 "trying to access non-existent element (trailing "
885 "coefficient of zero polynomial)");
886 return data[trailing_degree()];
887}
888
889template <CoefficientType T>
890const T& Polynomial<T>::leading_coefficient() const {
891 if (is_empty())
892 throw std::invalid_argument(
893 "trying to access non-existent element (leading "
894 "coefficient)");
895 return data.back();
896}
897
898template <CoefficientType T>
899template <typename U>
900constexpr Polynomial<T>& Polynomial<T>::add_to_coefficient(size_t i, U&& c)
901 requires std::convertible_to<std::decay_t<U>, T>
902{
903 T c_converted = std::forward<U>(c);
904 if (c_converted == T(0)) return *this;
905
907 if (data.empty() || i >= data.size()) {
908 // automatic growth, change zero coefficient beyond degree
909 data.resize(i + 1, T(0));
911 } else if (i == data.size() - 1) {
912 // change MSC
913 data.back() += c_converted;
914 if (data.back() == T(0)) prune();
915 } else {
916 // change some coefficient
917 data[i] += c_converted;
918 }
919 return *this;
920}
921
922template <CoefficientType T>
923template <typename U>
926{
927 T new_value = std::forward<U>(c);
928 if (data.empty() || i >= data.size()) {
929 if (new_value == T(0)) return *this;
931 data.resize(i + 1, T(0));
932 data[i] = std::move(new_value);
933 } else {
934 if (data[i] == new_value) return *this;
936 data[i] = std::move(new_value);
937 if (i == data.size() - 1 && data.back() == T(0)) prune();
938 }
939 return *this;
940}
941
942template <CoefficientType T>
944 std::reverse(data.begin(), data.end());
945 prune(); // a zero constant term in the original becomes a leading zero after reversal
946 return *this;
947}
948
949template <CoefficientType T>
952{
953 if (data.empty()) throw std::invalid_argument("trying to shift empty polynomial");
954 if (s == 0 || is_zero()) return *this;
955 data.insert(data.begin(), s, T(0)); // the weight is unchanged, so the cache stays valid
956 return *this;
957}
958
959template <CoefficientType T>
961 if (data.empty()) throw std::invalid_argument("trying to shift empty polynomial");
962 if (s == 0) return *this;
963 if (s >= data.size()) {
964 data.resize(1);
965 data[0] = T(0);
966 cache.template set<Weight>(0);
967 return *this;
968 }
969 data.erase(data.begin(), data.begin() + s); // the leading coefficient stays, so no prune needed
971 return *this;
972}
973
974template <CoefficientType T>
977{
978 if (is_zero() || is_monic()) return *this;
979 *this /= leading_coefficient();
980 return *this;
981}
982
983template <CoefficientType T>
984constexpr T Polynomial<T>::operator[](size_t i) const {
985 if (i >= data.size()) return T(0);
986 return data[i];
987}
988
989template <CoefficientType T>
990constexpr size_t Polynomial<T>::calculate_weight() const
992{
993 size_t res = data.size() - std::count(data.cbegin(), data.cend(), T(0));
994
995#ifdef CECCO_ERASURE_SUPPORT
996 if constexpr (FieldType<T>) res -= std::count_if(data.cbegin(), data.cend(), [](T x) { return x.is_erased(); });
997#endif
998
999 return res;
1000}
1001
1002template <CoefficientType T>
1003constexpr Polynomial<T>& Polynomial<T>::prune() {
1004 if (data.empty()) return *this;
1005
1006 const T zero(0);
1007 const auto lc = std::find_if(data.crbegin(), data.crend(), [&zero](const T& e) { return e != zero; });
1008 if (lc != data.crend()) {
1010 } else {
1011 data.resize(1);
1012 data.back() = zero;
1013 }
1014 return *this;
1015}
1016
1017/* free functions wrt. Polynomial */
1018
1019template <CoefficientType T>
1020constexpr Polynomial<T> operator+(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1021 Polynomial<T> res(lhs);
1022 res += rhs;
1023 return res;
1024}
1025
1026template <CoefficientType T>
1027constexpr Polynomial<T> operator+(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1028 Polynomial<T> res(std::move(lhs));
1029 res += rhs;
1030 return res;
1031}
1032
1033template <CoefficientType T>
1034constexpr Polynomial<T> operator+(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
1035 Polynomial<T> res(std::move(rhs));
1036 res += lhs;
1037 return res;
1038}
1039
1040template <CoefficientType T>
1042 Polynomial<T> res(std::move(lhs));
1043 res += rhs;
1044 return res;
1045}
1046
1047template <CoefficientType T>
1048constexpr Polynomial<T> operator-(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1049 Polynomial<T> res(lhs);
1050 res -= rhs;
1051 return res;
1052}
1053
1054template <CoefficientType T>
1055constexpr Polynomial<T> operator-(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1056 Polynomial<T> res(std::move(lhs));
1057 res -= rhs;
1058 return res;
1059}
1060
1061template <CoefficientType T>
1062constexpr Polynomial<T> operator-(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
1063 Polynomial<T> res(-std::move(rhs));
1064 res += lhs;
1065 return res;
1066}
1067
1068template <CoefficientType T>
1070 Polynomial<T> res(std::move(lhs));
1071 res -= rhs;
1072 return res;
1073}
1074
1075template <CoefficientType T>
1076constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1077 Polynomial<T> res(lhs);
1078 res *= rhs;
1079 return res;
1080}
1081
1082template <CoefficientType T>
1083constexpr Polynomial<T> operator*(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1084 Polynomial<T> res(std::move(lhs));
1085 res *= rhs;
1086 return res;
1087}
1088
1089template <CoefficientType T>
1090constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
1091 Polynomial<T> res(std::move(rhs));
1092 res *= lhs;
1093 return res;
1094}
1095
1096template <CoefficientType T>
1098 Polynomial<T> res(std::move(lhs));
1099 res *= rhs;
1100 return res;
1101}
1102
1103template <CoefficientType T>
1104constexpr Polynomial<T> operator/(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1105 Polynomial<T> res(lhs);
1106 res /= rhs;
1107 return res;
1108}
1109
1110template <CoefficientType T>
1111constexpr Polynomial<T> operator/(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1112 Polynomial<T> res(std::move(lhs));
1113 res /= rhs;
1114 return res;
1115}
1116
1117template <CoefficientType T>
1118constexpr Polynomial<T> operator%(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1119 Polynomial<T> res(lhs);
1120 res %= rhs;
1121 return res;
1122}
1123
1124template <CoefficientType T>
1125constexpr Polynomial<T> operator%(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1126 Polynomial<T> res(std::move(lhs));
1127 res %= rhs;
1128 return res;
1129}
1130
1131template <CoefficientType T>
1132constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, const T& rhs) {
1133 Polynomial<T> res(lhs);
1134 res *= rhs;
1135 return res;
1136}
1137
1138template <CoefficientType T>
1139constexpr Polynomial<T> operator*(Polynomial<T>&& lhs, const T& rhs) {
1140 Polynomial<T> res(std::move(lhs));
1141 res *= rhs;
1142 return res;
1143}
1144
1145template <CoefficientType T>
1146constexpr Polynomial<T> operator*(const T& lhs, const Polynomial<T>& rhs) {
1147 Polynomial<T> res(rhs);
1148 res *= lhs;
1149 return res;
1150}
1151
1152template <CoefficientType T>
1153constexpr Polynomial<T> operator*(const T& lhs, Polynomial<T>&& rhs) {
1154 Polynomial<T> res(std::move(rhs));
1155 res *= lhs;
1156 return res;
1157}
1158
1159// polynomial / scalar (corresponding to operator/= with T)
1160template <FieldType T>
1161constexpr Polynomial<T> operator/(const Polynomial<T>& lhs, const T& rhs) {
1162 Polynomial<T> res(lhs);
1163 res /= rhs;
1164 return res;
1165}
1166
1167template <FieldType T>
1168constexpr Polynomial<T> operator/(Polynomial<T>&& lhs, const T& rhs) {
1169 Polynomial<T> res(std::move(lhs));
1170 res /= rhs;
1171 return res;
1172}
1173
1174template <CoefficientType T>
1175constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, size_t n) {
1176 Polynomial<T> res(lhs);
1177 res *= n;
1178 return res;
1179}
1180
1181template <CoefficientType T>
1183 Polynomial<T> res(std::move(lhs));
1184 res *= n;
1185 return res;
1186}
1187
1188template <CoefficientType T>
1189constexpr Polynomial<T> operator*(size_t n, const Polynomial<T>& rhs) {
1190 Polynomial<T> res(rhs);
1191 res *= n;
1192 return res;
1193}
1194
1195template <CoefficientType T>
1197 Polynomial<T> res(std::move(rhs));
1198 res *= n;
1199 return res;
1200}
1201
1202template <CoefficientType T>
1207
1208template <CoefficientType T>
1213template <CoefficientType T>
1216{
1217 Polynomial<T> res(poly);
1219 return res;
1220}
1221
1222template <CoefficientType T>
1225{
1228 return res;
1229}
1230
1231template <CoefficientType T>
1234{
1235 Polynomial<T> res(poly);
1237 return res;
1238}
1239
1240template <CoefficientType T>
1243{
1246 return res;
1247}
1248
1249template <CoefficientType T>
1251 Polynomial<T> res(poly);
1252 res.reciprocal();
1253 return res;
1254}
1255
1256template <CoefficientType T>
1259 res.reciprocal();
1260 return res;
1261}
1262
1263template <CoefficientType T>
1266{
1267 Polynomial<T> res(poly);
1268 res.shift_up(s);
1269 return res;
1270}
1271
1272template <CoefficientType T>
1275{
1277 res.shift_up(s);
1278 return res;
1279}
1280
1281template <CoefficientType T>
1283 Polynomial<T> res(poly);
1284 res.shift_down(s);
1285 return res;
1286}
1287
1288template <CoefficientType T>
1291 res.shift_down(s);
1292 return res;
1293}
1294
1295template <CoefficientType T>
1298{
1299 Polynomial<T> res(poly);
1300 res.normalize();
1301 return res;
1302}
1303
1304template <CoefficientType T>
1307{
1309 res.normalize();
1310 return res;
1311}
1312
1313template <ReliablyComparableType T>
1314constexpr bool operator==(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1315 return lhs.data == rhs.data;
1316}
1317
1318template <ReliablyComparableType T>
1319constexpr bool operator!=(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1320 return !(lhs == rhs);
1321}
1322
1323template <CoefficientType T>
1325 if (rhs.data.empty()) {
1326 os << "()";
1327 return os;
1328 }
1329
1330 auto is_positive = [](const T& c) {
1331 if constexpr (FieldType<T>) {
1332 return c.has_positive_sign();
1333 } else if constexpr (std::is_same_v<T, std::complex<double>>) {
1334 return c.real() >= 0 || c.imag() >= 0;
1335 } else {
1336 return c >= 0;
1337 }
1338 };
1339
1340 const T zero(0);
1341 const T one(1);
1342 const T minus_one(-one);
1343
1344 bool first = true;
1345 for (size_t i = 0; i < rhs.data.size(); ++i) {
1346 auto coeff = rhs.data[i];
1347 if (coeff != zero || rhs.data.size() == 1) {
1348 const bool negative = !is_positive(coeff);
1349 if (negative) coeff *= minus_one;
1350
1351 if (first)
1352 os << (negative ? "-" : "");
1353 else
1354 os << (negative ? " - " : " + ");
1355
1356 if (coeff != one || i == 0) os << coeff;
1357 if (i > 0) {
1358 os << "x";
1359 if (i > 1) os << "^" << i;
1360 }
1361 first = false;
1362 }
1363 }
1364
1365 return os;
1366}
1367
1368/**
1369 * @brief Monomial a · xⁱ
1370 *
1371 * @param i Power of x
1372 * @param a Coefficient (defaults to T(1)); a zero coefficient yields the zero polynomial
1373 */
1374template <CoefficientType T>
1375constexpr Polynomial<T> Monomial(size_t i, const T& a = T(1)) {
1376 Polynomial<T> res(0);
1378 return res;
1379}
1380
1381/// @brief Constant polynomial 0 (cached)
1382template <CoefficientType T>
1384 static const Polynomial<T> zero(0);
1385 return zero;
1386}
1387
1388/// @brief Constant polynomial 1 (cached)
1389template <CoefficientType T>
1391 static const Polynomial<T> one(1);
1392 return one;
1393}
1394
1395/**
1396 * @brief Greatest common divisor of two polynomials via the (extended) Euclidean algorithm
1397 *
1398 * @param a First polynomial
1399 * @param b Second polynomial
1400 * @param s_ptr Optional out-pointer for the Bézout coefficient of @p a
1401 * @param t_ptr Optional out-pointer for the Bézout coefficient of @p b
1402 * @return gcd(a, b); if both @p s_ptr and @p t_ptr are non-null, additionally `gcd(a, b) = s·a + t·b`
1403 *
1404 * Operands are reordered internally so that the higher-degree one drives the recursion.
1405 */
1406template <CoefficientType T>
1409{
1410 const bool swapped = !b.is_zero() && (a.is_zero() || a.degree() < b.degree());
1411 if (swapped) std::swap(a, b);
1412
1413 if (s_ptr != nullptr && t_ptr != nullptr) { // extended EA
1414 *s_ptr = OnePolynomial<T>();
1415 *t_ptr = ZeroPolynomial<T>();
1418 while (!b.is_zero()) {
1419 auto [q, rem] = a.poly_long_div(b);
1420 a = std::move(b);
1421 b = std::move(rem);
1422 Polynomial<T> u1 = std::move(u);
1423 u = *s_ptr - q * u1;
1424 *s_ptr = std::move(u1);
1425 Polynomial<T> v1 = std::move(v);
1426 v = *t_ptr - q * v1;
1427 *t_ptr = std::move(v1);
1428 }
1429 } else { // "normal" EA
1430 while (!b.is_zero()) {
1431 Polynomial<T> rem = a % b;
1432 a = std::move(b);
1433 b = std::move(rem);
1434 }
1435 }
1436 if (swapped && s_ptr != nullptr && t_ptr != nullptr) std::swap(*s_ptr, *t_ptr);
1437 return a;
1438}
1439
1440/**
1441 * @brief Greatest common divisor of a list of polynomials, computed iteratively
1442 *
1443 * @return gcd(p₁, p₂, …, pₙ)
1444 * @throws std::invalid_argument if @p polys is empty
1445 */
1446template <CoefficientType T>
1449{
1450 if (polys.empty())
1451 throw std::invalid_argument("trying to calculate polynomial GCD but didn't provide any polynomials");
1452 if (polys.size() == 1) return polys[0];
1453
1454 Polynomial<T> res = polys.front();
1455 for (auto it = polys.cbegin() + 1; it != polys.cend(); ++it) {
1456 res = GCD<T>(std::move(res), *it);
1457 }
1458 return res;
1459}
1460
1461/// @brief Least common multiple `lcm(a, b) = (a · b) / gcd(a, b)`
1462template <CoefficientType T>
1465{
1466 return (a * b) / GCD(a, b);
1467}
1468
1469/**
1470 * @brief Least common multiple of a list of polynomials, computed iteratively
1471 *
1472 * @return lcm(p₁, p₂, …, pₙ)
1473 * @throws std::invalid_argument if @p polys is empty
1474 */
1475template <CoefficientType T>
1478{
1479 if (polys.empty())
1480 throw std::invalid_argument("trying to calculate polynomial LCM but didn't provide any polynomials");
1481 if (polys.size() == 1) return polys[0];
1482
1483 Polynomial<T> res = polys.front();
1484 for (auto it = polys.cbegin() + 1; it != polys.cend(); ++it) {
1485 res = LCM<T>(res, *it);
1486 }
1487 return res;
1488}
1489
1490/**
1491 * @brief Polynomial exponentiation by square-and-multiply
1492 *
1493 * @return base^exponent
1494 *
1495 * @warning Does **not** follow C++ precedence for `^`: in `b * base ^ exponent` the parser
1496 * evaluates `(b * base) ^ exponent`. Parenthesise as `b * (base ^ exponent)`, or call
1497 * @ref CECCO::sqm directly.
1498 *
1499 * @throws std::invalid_argument if @p exponent is negative
1500 */
1501template <CoefficientType T>
1502constexpr Polynomial<T> operator^(const Polynomial<T>& base, int exponent) {
1503 if (exponent < 0) throw std::invalid_argument("polynomial exponentiation requires a non-negative exponent!");
1504 return sqm<Polynomial<T>>(base, exponent);
1505}
1506
1507/**
1508 * @brief Coefficient vector [a₀, a₁, …, aₘ] of the Conway polynomial for 𝔽_{p^m}
1509 *
1510 * @tparam p Prime characteristic
1511 * @tparam m Extension degree
1512 * @return Coefficients low-to-high; empty vector if the (p, m) pair is not in the built-in table
1513 *
1514 * The built-in table covers all primes ≤ 97 and degrees ≤ ~13 (more for small p).
1515 */
1516template <uint16_t p, size_t m>
1518 if constexpr (p == 2) {
1519 if constexpr (m == 1)
1520 return {1, 1};
1521 else if constexpr (m == 2)
1522 return {1, 1, 1};
1523 else if constexpr (m == 3)
1524 return {1, 1, 0, 1};
1525 else if constexpr (m == 4)
1526 return {1, 1, 0, 0, 1};
1527 else if constexpr (m == 5)
1528 return {1, 0, 1, 0, 0, 1};
1529 else if constexpr (m == 6)
1530 return {1, 1, 0, 1, 1, 0, 1};
1531 else if constexpr (m == 7)
1532 return {1, 1, 0, 0, 0, 0, 0, 1};
1533 else if constexpr (m == 8)
1534 return {1, 0, 1, 1, 1, 0, 0, 0, 1};
1535 else if constexpr (m == 9)
1536 return {1, 0, 0, 0, 1, 0, 0, 0, 0, 1};
1537 else if constexpr (m == 10)
1538 return {1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1};
1539 else if constexpr (m == 11)
1540 return {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
1541 else if constexpr (m == 12)
1542 return {1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1};
1543 else if constexpr (m == 13)
1544 return {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
1545 } else if constexpr (p == 3) {
1546 if constexpr (m == 1)
1547 return {1, 1};
1548 else if constexpr (m == 2)
1549 return {2, 2, 1};
1550 else if constexpr (m == 3)
1551 return {1, 2, 0, 1};
1552 else if constexpr (m == 4)
1553 return {2, 0, 0, 2, 1};
1554 else if constexpr (m == 5)
1555 return {1, 2, 0, 0, 0, 1};
1556 else if constexpr (m == 6)
1557 return {2, 2, 1, 0, 2, 0, 1};
1558 else if constexpr (m == 7)
1559 return {1, 0, 2, 0, 0, 0, 0, 1};
1560 else if constexpr (m == 8)
1561 return {2, 2, 2, 0, 1, 2, 0, 0, 1};
1562 } else if constexpr (p == 5) {
1563 if constexpr (m == 1)
1564 return {3, 1};
1565 else if constexpr (m == 2)
1566 return {2, 4, 1};
1567 else if constexpr (m == 3)
1568 return {3, 3, 0, 1};
1569 else if constexpr (m == 4)
1570 return {2, 4, 4, 0, 1};
1571 else if constexpr (m == 5)
1572 return {3, 4, 0, 0, 0, 1};
1573 } else if constexpr (p == 7) {
1574 if constexpr (m == 1)
1575 return {4, 1};
1576 else if constexpr (m == 2)
1577 return {3, 6, 1};
1578 else if constexpr (m == 3)
1579 return {4, 0, 6, 1};
1580 else if constexpr (m == 4)
1581 return {3, 4, 5, 0, 1};
1582 } else if constexpr (p == 11) {
1583 if constexpr (m == 1)
1584 return {9, 1};
1585 else if constexpr (m == 2)
1586 return {2, 7, 1};
1587 else if constexpr (m == 3)
1588 return {9, 2, 0, 1};
1589 else if constexpr (m == 4)
1590 return {2, 10, 8, 0, 1};
1591 } else if constexpr (p == 13) {
1592 if constexpr (m == 1)
1593 return {11, 1};
1594 else if constexpr (m == 2)
1595 return {2, 12, 1};
1596 else if constexpr (m == 3)
1597 return {11, 2, 0, 1};
1598 } else if constexpr (p == 17) {
1599 if constexpr (m == 1)
1600 return {14, 1};
1601 else if constexpr (m == 2)
1602 return {3, 16, 1};
1603 else if constexpr (m == 3)
1604 return {14, 1, 0, 1};
1605 } else if constexpr (p == 19) {
1606 if constexpr (m == 1)
1607 return {17, 1};
1608 else if constexpr (m == 2)
1609 return {2, 18, 1};
1610 else if constexpr (m == 3)
1611 return {17, 4, 0, 1};
1612 } else if constexpr (p == 23) {
1613 if constexpr (m == 1)
1614 return {18, 1};
1615 else if constexpr (m == 2)
1616 return {5, 21, 1};
1617 } else if constexpr (p == 29) {
1618 if constexpr (m == 1)
1619 return {27, 1};
1620 else if constexpr (m == 2)
1621 return {2, 24, 1};
1622 } else if constexpr (p == 31) {
1623 if constexpr (m == 1)
1624 return {28, 1};
1625 else if constexpr (m == 2)
1626 return {3, 29, 1};
1627 } else if constexpr (p == 37) {
1628 if constexpr (m == 1)
1629 return {35, 1};
1630 else if constexpr (m == 2)
1631 return {2, 33, 1};
1632 } else if constexpr (p == 41) {
1633 if constexpr (m == 1)
1634 return {35, 1};
1635 else if constexpr (m == 2)
1636 return {6, 38, 1};
1637 } else if constexpr (p == 43) {
1638 if constexpr (m == 1)
1639 return {40, 1};
1640 else if constexpr (m == 2)
1641 return {3, 42, 1};
1642 } else if constexpr (p == 47) {
1643 if constexpr (m == 1)
1644 return {42, 1};
1645 else if constexpr (m == 2)
1646 return {5, 45, 1};
1647 } else if constexpr (p == 53) {
1648 if constexpr (m == 1)
1649 return {51, 1};
1650 else if constexpr (m == 2)
1651 return {2, 49, 1};
1652 } else if constexpr (p == 59) {
1653 if constexpr (m == 1)
1654 return {57, 1};
1655 else if constexpr (m == 2)
1656 return {2, 58, 1};
1657 } else if constexpr (p == 61) {
1658 if constexpr (m == 1)
1659 return {59, 1};
1660 else if constexpr (m == 2)
1661 return {2, 60, 1};
1662 } else if constexpr (p == 67) {
1663 if constexpr (m == 1)
1664 return {65, 1};
1665 else if constexpr (m == 2)
1666 return {2, 63, 1};
1667 } else if constexpr (p == 71) {
1668 if constexpr (m == 1)
1669 return {64, 1};
1670 else if constexpr (m == 2)
1671 return {7, 69, 1};
1672 } else if constexpr (p == 73) {
1673 if constexpr (m == 1)
1674 return {68, 1};
1675 else if constexpr (m == 2)
1676 return {5, 70, 1};
1677 } else if constexpr (p == 79) {
1678 if constexpr (m == 1)
1679 return {76, 1};
1680 else if constexpr (m == 2)
1681 return {3, 78, 1};
1682 } else if constexpr (p == 83) {
1683 if constexpr (m == 1)
1684 return {81, 1};
1685 else if constexpr (m == 2)
1686 return {2, 82, 1};
1687 } else if constexpr (p == 89) {
1688 if constexpr (m == 1)
1689 return {86, 1};
1690 else if constexpr (m == 2)
1691 return {3, 82, 1};
1692 } else if constexpr (p == 97) {
1693 if constexpr (m == 1)
1694 return {92, 1};
1695 else if constexpr (m == 2)
1696 return {5, 96, 1};
1697 }
1698 // ... support all fields with less than 10k elements
1699 return Vector<Fp<p>>();
1700}
1701
1702/**
1703 * @brief Conway polynomial for 𝔽_{p^m} as a `Polynomial<Fp<p>>`
1704 *
1705 * Wraps @ref ConwayCoefficients.
1706 *
1707 * @throws std::logic_error if (p, m) is not tabulated
1708 */
1709template <uint16_t p, size_t m>
1711 const auto coefficients = ConwayCoefficients<p, m>();
1712 if (coefficients.get_n() == 0)
1713 throw std::logic_error("No Conway polynomial tabulated for p = " + std::to_string(p) +
1714 ", m = " + std::to_string(m) + "!");
1715 return Polynomial<Fp<p>>(coefficients);
1716}
1717
1718/**
1719 * @brief Sample a random monic irreducible polynomial of degree @p degree over T
1720 *
1721 * Tries random polynomials until @ref Polynomial::is_irreducible accepts one. Runtime is
1722 * bounded in expectation by the density of irreducibles, but unbounded in the worst case;
1723 * suitable as a `modulus` for @ref CECCO::Ext.
1724 */
1725template <FiniteFieldType T>
1727 if (degree == 0) return Polynomial<T>(T(1));
1728 Polynomial<T> res;
1729 do {
1731 } while (!res.is_irreducible());
1732
1733 res.normalize();
1734 return res;
1735}
1736
1737} // namespace CECCO
1738
1739#endif
Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a CECCO::CoefficientType.
constexpr Polynomial< T > & add_to_coefficient(size_t i, U &&c)
constexpr Polynomial operator+() &&noexcept
Unary + (rvalue): returns the rvalue itself.
std::pair< Polynomial< T >, Polynomial< T > > poly_long_div(const Polynomial< T > &rhs) const
Long division: returns (q, r) with *this == q · rhs + r and r == 0 or deg(r) < deg(rhs).
Polynomial(const Vector< T > &v)
From a coefficient vector: v[i] becomes the coefficient of x^i.
constexpr Polynomial & operator+=(const Polynomial &rhs)
Coefficient-wise addition; expands storage if rhs has higher degree, then prunes.
Polynomial & operator/=(const T &s)
Divide every coefficient by the scalar s.
const T & trailing_coefficient() const
Coefficient of the lowest non-zero power.
constexpr Polynomial & set_coefficient(size_t i, U &&c)
Set the coefficient of x^i to c; grows the polynomial if i > degree.
constexpr T operator[](size_t i) const
Coefficient of x^i; returns T(0) for i > degree.
Vector< T > get_coefficients() const
Coefficient vector (length = number of stored coefficients; 0 for an empty polynomial).
Polynomial & Hasse_differentiate(size_t s)
In-place s-th Hasse derivative (= classical s-th derivative divided by s!).
Polynomial & operator/=(const Polynomial &rhs)
Polynomial division: *this becomes the quotient of (*this) / rhs.
T operator()(const T &s) const
Evaluate at s using Horner's method (O(n)).
const T & leading_coefficient() const
Coefficient of the highest non-zero power.
constexpr Polynomial & operator*=(const T &s)
Multiply every coefficient by the scalar s.
constexpr Polynomial & shift_down(size_t s)
Floor-divide by x^s by dropping the s lowest coefficients; degree < s yields the zero polynomial.
constexpr Polynomial operator+() const &
Unary + (lvalue): returns a copy.
constexpr Polynomial operator-() const &
Unary − (lvalue): returns a new polynomial with each coefficient negated.
constexpr Polynomial(Polynomial &&other) noexcept
constexpr Polynomial(int e)
Constant polynomial from an int (constructs T(e)).
constexpr bool is_empty() const noexcept
True iff *this has no coefficients (distinct from the zero polynomial).
Polynomial & differentiate(size_t s)
In-place classical s-th derivative.
constexpr Polynomial & reciprocal()
Reciprocal: reverse coefficients, sending p(x) to xⁿ · p(1/x).
constexpr Polynomial & operator=(Polynomial &&rhs) noexcept
constexpr Polynomial() noexcept=default
Default constructor: empty polynomial (no coefficients; distinct from the zero polynomial).
size_t degree() const
Degree max{i : aᵢ ≠ 0}.
constexpr Polynomial & operator*=(size_t n)
Multiply every coefficient by the integer n; reduces n modulo characteristic if available.
constexpr Polynomial & normalize()
Make monic by dividing every coefficient by the leading one (no-op on zero or already-monic).
Polynomial & randomize(size_t d)
Replace *this with a random polynomial of degree exactly d.
size_t trailing_degree() const
Trailing degree min{i : aᵢ ≠ 0}.
Polynomial & operator%=(const Polynomial &rhs)
Polynomial remainder: *this becomes (*this) mod rhs.
constexpr Polynomial(const T &e)
Constant polynomial from a coefficient.
bool is_zero() const
True iff *this is the zero polynomial (one coefficient, equal to T(0)).
friend constexpr bool operator==(const Polynomial< U > &lhs, const Polynomial< U > &rhs)
constexpr Polynomial(const Polynomial &other)
constexpr Polynomial & shift_up(size_t s)
Multiply by x^s by prepending s zero coefficients (the zero polynomial is unchanged).
constexpr Polynomial operator-() &&
Unary − (rvalue): negates coefficients in place.
constexpr Polynomial & operator-=(const Polynomial &rhs)
Coefficient-wise subtraction; expands storage if rhs has higher degree, then prunes.
constexpr Polynomial & operator*=(const Polynomial &rhs)
Polynomial multiplication by convolution (O(n²)).
constexpr Polynomial & operator=(const T &rhs)
Assign a scalar: replace *this with the constant polynomial rhs.
constexpr Polynomial & operator=(const Polynomial< T > &rhs)
Vector v = (v₀, v₁, …, vₙ₋₁) over a CECCO::ComponentType.
Definition vectors.hpp:124
Provides a framework for error correcting codes.
Definition blocks.hpp:57
const Polynomial< T > & ZeroPolynomial()
Constant polynomial 0 (cached).