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.2.9
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::ComponentType (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 <ComponentType T>
83class Polynomial;
84
85template <ComponentType T>
86constexpr bool operator==(const Polynomial<T>& lhs, const Polynomial<T>& rhs);
87template <ComponentType T>
88std::ostream& operator<<(std::ostream& os, const Polynomial<T>& rhs);
89
90template <ComponentType T>
92
93/**
94 * @brief Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a @ref CECCO::ComponentType
95 *
96 * @tparam T Coefficient type satisfying @ref CECCO::ComponentType (finite field, `double`,
97 * `std::complex<double>`, or signed integer including `InfInt`)
98 *
99 * Coefficients are stored low-to-high in a contiguous buffer; the leading-zero invariant is
100 * maintained by an internal `prune()` so `data.size() - 1` is the degree (when non-empty and
101 * not the zero polynomial). The empty polynomial (no coefficients) is *distinct* from the
102 * zero polynomial — most queries throw `std::invalid_argument` on an empty polynomial.
103 *
104 * Methods that need division are gated by `requires FieldType<T>`; methods that compare
105 * against zero (degree, Hamming weight, …) are gated by `requires ReliablyComparableType<T>`.
106 *
107 * @section Usage_Example
108 *
109 * @code{.cpp}
110 * using F7 = Fp<7>;
111 * Polynomial<F7> p = {1, 2, 3}; // 1 + 2x + 3x²
112 * Polynomial<F7> q = {4, 5}; // 4 + 5x
113 * auto sum = p + q;
114 * auto prod = p * q;
115 * F7 v = p(F7(3)); // evaluation
116 * auto [quot, rem] = p.poly_long_div(q);
117 * size_t w = p.wH(); // Hamming weight (cached)
118 * @endcode
119 */
120template <ComponentType T>
122 friend bool operator== <>(const Polynomial& lhs, const Polynomial& rhs);
123 friend std::ostream& operator<< <>(std::ostream& os, const Polynomial& rhs);
124
125 public:
126 // Cache configuration for this class
127 enum CacheIds { Weight = 0 };
128
129 /** @name Constructors
130 * @{
131 */
132
133 /// @brief Default constructor: empty polynomial (no coefficients; distinct from the zero polynomial)
134 constexpr Polynomial() noexcept = default;
135
136 /// @brief Constant polynomial from an `int` (constructs `T(e)`)
137 constexpr Polynomial(int e) : data(1) { data.back() = T(e); }
138
139 /// @brief Constant polynomial from a coefficient
140 constexpr Polynomial(const T& e) : data(1) { data.back() = e; }
141
142 /**
143 * @brief From an initializer list of coefficients in ascending degree order
144 *
145 * `l[i]` becomes the coefficient of x^i. Trailing (high-degree) zeros are pruned to keep
146 * canonical form: `Polynomial<int>{1, 2, 3}` represents 1 + 2x + 3x².
147 */
148 constexpr Polynomial(const std::initializer_list<T>& l) : data(l) { prune(); }
149
150 constexpr Polynomial(const Polynomial& other) : data(other.data), cache(other.cache) {}
151 constexpr Polynomial(Polynomial&& other) noexcept : data(std::move(other.data)), cache(std::move(other.cache)) {}
152
153 /**
154 * @brief Cross-field conversion between two finite fields of the same characteristic
155 *
156 * @tparam S Source field type (`Polynomial<S>`); must share characteristic with T
157 *
158 * Converts coefficient by coefficient via T's cross-field constructor, which routes through
159 * @ref CECCO::details::largest_common_subfield_t and so handles disjoint construction towers.
160 * Propagates `std::invalid_argument` if any coefficient is not representable in T.
161 */
162 template <FiniteFieldType S>
164 Polynomial(const Polynomial<S>& other);
165
166 /**
167 * @brief From a coefficient vector: `v[i]` becomes the coefficient of x^i
168 *
169 * Resulting degree is `v.get_n() - 1` (after pruning). For cross-field conversion, convert
170 * the vector first.
171 */
172 Polynomial(const Vector<T>& v);
173
174 /** @} */
175
176 /** @name Assignment Operators
177 * @{
178 */
179
180 /// @brief Assign a scalar: replace `*this` with the constant polynomial @p rhs
181 constexpr Polynomial& operator=(const T& rhs);
182
183 constexpr Polynomial& operator=(const Polynomial<T>& rhs);
184 constexpr Polynomial& operator=(Polynomial&& rhs) noexcept;
185
186 /// @brief Cross-field assignment (same semantics as the cross-field constructor)
187 template <FiniteFieldType S>
190
191 /** @} */
192
193 /** @name Unary Arithmetic Operations
194 * @{
195 */
196
197 /// @brief Unary `+` (lvalue): returns a copy
198 constexpr Polynomial operator+() const& { return *this; }
199 /// @brief Unary `+` (rvalue): returns the rvalue itself
200 constexpr Polynomial operator+() && noexcept { return std::move(*this); }
201
202 /// @brief Unary `−` (lvalue): returns a new polynomial with each coefficient negated
203 constexpr Polynomial operator-() const&;
204 /// @brief Unary `−` (rvalue): negates coefficients in place
205 constexpr Polynomial operator-() &&;
206
207 /**
208 * @brief Evaluate at @p s using Horner's method (O(n))
209 *
210 * @return p(s) = a₀ + a₁s + a₂s² + … + aₙsⁿ
211 * @throws std::invalid_argument if `*this` is empty
212 */
213 T operator()(const T& s) const;
214
215 /** @} */
216
217 /** @name Compound Assignment Operations
218 * @{
219 */
220
221 /// @brief Coefficient-wise addition; expands storage if @p rhs has higher degree, then prunes
222 constexpr Polynomial& operator+=(const Polynomial& rhs);
223 /// @brief Coefficient-wise subtraction; expands storage if @p rhs has higher degree, then prunes
224 constexpr Polynomial& operator-=(const Polynomial& rhs);
225
226 /**
227 * @brief Polynomial multiplication by convolution (O(n²))
228 *
229 * @return Reference to `*this` with degree `deg(this) + deg(rhs)`
230 * @throws std::invalid_argument if either operand is the empty polynomial
231 */
232 constexpr Polynomial& operator*=(const Polynomial& rhs);
233
234 /**
235 * @brief Polynomial division: `*this` becomes the quotient of `(*this) / rhs`
236 *
237 * @throws std::invalid_argument if @p rhs is the zero polynomial
238 */
241
242 /**
243 * @brief Polynomial remainder: `*this` becomes `(*this) mod rhs`
244 *
245 * Specialised path when `deg(this) == deg(rhs)` (one subtraction); long division otherwise.
246 *
247 * @throws std::invalid_argument if @p rhs is the zero polynomial
248 */
251
252 /// @brief Multiply every coefficient by the scalar @p s
253 constexpr Polynomial& operator*=(const T& s);
254
255 /**
256 * @brief Divide every coefficient by the scalar @p s
257 *
258 * @throws std::invalid_argument if @p s == T(0)
259 * @note Round-trip `(p / s) * s == p` is only guaranteed when T satisfies @ref CECCO::FieldType
260 * (otherwise integer rounding may corrupt coefficients).
261 */
262 Polynomial& operator/=(const T& s);
263
264 /// @brief Multiply every coefficient by the integer @p n; reduces @p n modulo characteristic
265 constexpr Polynomial& operator*=(size_t n)
267
268 /**
269 * @brief Long division: returns `(q, r)` with `*this == q · rhs + r` and `r == 0` or `deg(r) < deg(rhs)`
270 *
271 * @throws std::invalid_argument if @p rhs is the zero polynomial
272 */
273 std::pair<Polynomial<T>, Polynomial<T>> poly_long_div(const Polynomial<T>& rhs) const
275
276 /** @} */
277
278 /** @name Randomization
279 * @{
280 */
281
282 /**
283 * @brief Replace `*this` with a random polynomial of degree exactly @p d
284 *
285 * The leading coefficient is resampled until non-zero, so the result really has degree d.
286 * Distribution per coefficient: finite-field types draw uniformly from the field; signed
287 * integers from [−100, 100]; `double` and the parts of `std::complex<double>` from [−1, 1].
288 */
290
291 /** @} */
292
293 /** @name Differentiation
294 * @{
295 */
296
297 /**
298 * @brief In-place classical s-th derivative
299 *
300 * d^s/dx^s [∑ aᵢ xⁱ] = ∑_{i ≥ s} (i! / (i − s)!) aᵢ x^{i − s}. The s-th derivative of a
301 * polynomial of degree < s is the zero polynomial. `s == 0` is the identity.
302 *
303 * @throws std::invalid_argument if `*this` is empty
304 */
307
308 /**
309 * @brief In-place s-th Hasse derivative (= classical s-th derivative divided by s!)
310 *
311 * D^{(s)}[∑ aᵢ xⁱ] = ∑_{i ≥ s} C(i, s) aᵢ x^{i − s}. Useful in characteristic p where the
312 * classical derivative loses information whenever s ≥ p.
313 *
314 * @throws std::invalid_argument if `*this` is empty
315 */
318
319 /** @} */
320
321 /** @name Information and Properties
322 * @{
323 */
324
325 /**
326 * @brief Degree max{i : aᵢ ≠ 0}
327 *
328 * @throws std::invalid_argument if `*this` is empty
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
340 */
343
344 /**
345 * @brief Coefficient of the lowest non-zero power
346 *
347 * @throws std::invalid_argument if `*this` is empty
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 degree() == 0 && trailing_coefficient() == 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 by trial division against every monic polynomial of degree ≤ deg/2
400 *
401 * Cost grows as q^{deg/2} in field size — practical only for small fields and small degrees.
402 *
403 * @throws std::invalid_argument if `*this` is empty
404 */
405 constexpr bool is_irreducible() const
407 {
408 const size_t d = degree();
409 if (d == 0) return false;
410 if (d == 1) return true;
411
412 const size_t q = T::get_size();
413
414 for (size_t i = 1; i <= d / 2; ++i) {
415 size_t count = 1;
416 for (size_t j = 0; j < i; ++j) count *= q;
417
418 for (size_t k = 0; k < count; ++k) {
419 size_t x = k;
420 Vector<T> v;
421 for (size_t j = 0; j < i; ++j) {
422 v = v.append(T(x % q));
423 x /= q;
424 }
425 auto p = Polynomial(v.append(T(1)));
426 if (((*this) % p).is_zero()) return false;
427 }
428 }
429
430 return true;
431 }
432
433 /// @brief Hamming weight: number of non-zero, non-erased coefficients; cached on first call
434 size_t wH() const
436 {
437 return cache.template get_or_compute<Weight>([this] { return calculate_weight(); });
438 }
439
440 /** @} */
441
442 /** @name Coefficient Access and Manipulation
443 * @{
444 */
445
446 /**
447 * @brief Add @p c to the coefficient of x^i; grows the polynomial if `i > degree`
448 *
449 * @param i Power of x
450 * @param c Value to add (perfect-forwarded)
451 */
452 template <typename U>
455
456 /**
457 * @brief Set the coefficient of x^i to @p c; grows the polynomial if `i > degree`
458 *
459 * @param i Power of x
460 * @param c New value (perfect-forwarded)
461 */
462 template <typename U>
463 constexpr Polynomial& set_coefficient(size_t i, U&& c)
465
466 /// @brief Reciprocal: reverse coefficients, sending p(x) to xⁿ · p(1/x)
467 constexpr Polynomial& reciprocal();
468
469 /// @brief Make monic by dividing every coefficient by the leading one (no-op on zero or already-monic)
472
473 /// @brief Coefficient of x^i; returns T(0) for `i > degree`
474 constexpr T operator[](size_t i) const;
475
476 /** @} */
477
478 private:
479 std::vector<T> data;
480
481 /// @brief Cache for Hamming weight (invalidated by mutating operations)
482 mutable details::Cache<details::CacheEntry<Weight, size_t>> cache;
483
484 constexpr size_t calculate_weight() const
486
487 constexpr Polynomial& prune();
488};
489
490/* member functions for Polynomial */
491
492template <ComponentType T>
493template <FiniteFieldType S>
496 if (other.is_empty()) return;
497 const size_t deg = other.degree();
498 for (size_t i = 0; i <= deg; ++i) set_coefficient(i, T(other[i])); // Uses enhanced cross-field constructors
499}
500
501template <ComponentType T>
502Polynomial<T>::Polynomial(const Vector<T>& v) : data(v.get_n()) {
503 for (size_t i = 0; i < data.size(); ++i) data[i] = v[i];
504 prune();
505}
506
507template <ComponentType T>
508constexpr Polynomial<T>& Polynomial<T>::operator=(const T& rhs) {
509 data.resize(1);
510 data.back() = rhs;
511 if (rhs == T(0))
512 cache.template set<Weight>(0);
513 else
514 cache.template set<Weight>(1);
515 return *this;
516}
517
518template <ComponentType T>
519constexpr Polynomial<T>& Polynomial<T>::operator=(const Polynomial<T>& rhs) {
520 if (this == &rhs) return *this;
521 if (rhs.data.empty()) {
522 data.clear();
523 cache.invalidate();
524 return *this;
525 }
526 data = rhs.data;
527 cache = rhs.cache;
528 return *this;
529}
530
531template <ComponentType T>
532constexpr Polynomial<T>& Polynomial<T>::operator=(Polynomial<T>&& rhs) noexcept {
533 if (this == &rhs) return *this;
534 if (rhs.data.empty()) {
535 data.clear();
536 cache.invalidate();
537 return *this;
538 }
539 data = std::move(rhs.data);
540 cache = std::move(rhs.cache);
541 return *this;
542}
543
544template <ComponentType T>
545template <FiniteFieldType S>
548 if (rhs.data.empty()) {
549 data.clear();
551 return *this;
552 }
553 data.resize(0);
554 for (size_t i = 0; i <= rhs.degree(); ++i)
555 this->set_coefficient(i, T(rhs[i])); // Uses enhanced cross-field constructors
557 return *this;
558}
559
560template <ComponentType T>
561constexpr Polynomial<T> Polynomial<T>::operator-() const& {
562 Polynomial res(*this);
563 std::ranges::for_each(res.data, [](T& c) { c = -c; });
564 return res;
565}
566
567template <ComponentType T>
568constexpr Polynomial<T> Polynomial<T>::operator-() && {
569 std::ranges::for_each(data, [](T& c) { c = -c; });
570 cache.invalidate();
571 return std::move(*this);
572}
573
574template <ComponentType T>
575T Polynomial<T>::operator()(const T& s) const {
576 if (data.empty()) throw std::invalid_argument("trying to evaluate empty polynomial");
577 if (data.size() == 1) return data.front();
578
579 // Use std::accumulate with Horner's method for polynomial evaluation
580 return std::accumulate(data.crbegin() + 1, data.crend(), data.back(),
581 [&s](const T& acc, const T& coeff) { return acc * s + coeff; });
582}
583
584template <ComponentType T>
585constexpr Polynomial<T>& Polynomial<T>::operator+=(const Polynomial<T>& rhs) {
586 if (data.size() < rhs.data.size()) data.resize(rhs.data.size());
587 std::transform(data.begin(), data.begin() + rhs.data.size(), rhs.data.begin(), data.begin(), std::plus<T>{});
588 cache.invalidate();
589 prune();
590 return *this;
591}
592
593template <ComponentType T>
594constexpr Polynomial<T>& Polynomial<T>::operator-=(const Polynomial<T>& rhs) {
595 if (data.size() < rhs.data.size()) data.resize(rhs.data.size());
596 std::transform(data.begin(), data.begin() + rhs.data.size(), rhs.data.begin(), data.begin(), std::minus<T>{});
597 cache.invalidate();
598 prune();
599 return *this;
600}
601
602template <ComponentType T>
603constexpr Polynomial<T>& Polynomial<T>::operator*=(const Polynomial<T>& rhs) {
604 if (is_empty() || rhs.is_empty()) throw std::invalid_argument("trying to multiply with empty polynomial");
605 Polynomial res;
606 res.data.resize(data.size() + rhs.data.size() - 1);
607 for (size_t i = 0; i < data.size(); ++i) {
608 for (size_t j = 0; j < rhs.data.size(); ++j) res.add_to_coefficient(i + j, data[i] * rhs.data[j]);
609 }
610 data = std::move(res.data);
611 cache.invalidate();
612 prune();
613 return *this;
614}
615
616template <ComponentType T>
617Polynomial<T>& Polynomial<T>::operator/=(const Polynomial<T>& rhs)
619{
620 if (rhs.is_zero()) throw std::invalid_argument("division by zero (polynomial)");
621 *this = this->poly_long_div(rhs).first;
623 return *this;
624}
625
626template <ComponentType T>
627Polynomial<T>& Polynomial<T>::operator%=(const Polynomial<T>& rhs)
629{
630 if (rhs.is_zero()) throw std::invalid_argument("division by zero (polynomial)");
631
632 const size_t d = degree();
633 if (d == rhs.degree()) { // simply subtract polynomial modulus
635 for (size_t i = 0; i < d; ++i) {
636 data[i] -= scalar * rhs[i];
637 }
638 set_coefficient(d, 0);
640 prune();
641 } else { // full-blown polynomial long division
642 auto res = this->poly_long_div(rhs);
643 *this = res.second;
645 }
646
647 return *this;
648}
649
650template <ComponentType T>
651constexpr Polynomial<T>& Polynomial<T>::operator*=(const T& s) {
652 if (s == T(0)) {
653 cache.template set<Weight>(0);
654 data.resize(1);
655 data.back() = T(0);
656 } else {
657 std::ranges::for_each(data, [&s](T& c) { c *= s; });
658 cache.invalidate();
659 }
660 return *this;
661}
662
663template <ComponentType T>
664constexpr Polynomial<T>& Polynomial<T>::operator*=(size_t n)
666{
667 if (T::get_characteristic() != 0) {
668 n = n % T::get_characteristic();
669 }
670 if (n == 0) {
671 cache.template set<Weight>(0);
672 data.resize(1);
673 data.back() = T(0);
674 } else {
675 std::ranges::for_each(data, [&n](T& c) { c *= n; });
677 }
678 return *this;
679}
680
681template <ComponentType T>
682Polynomial<T>& Polynomial<T>::operator/=(const T& s) {
683 if (s == T(0)) throw std::invalid_argument("division by zero (polynomial)");
684 std::ranges::for_each(data, [&s](T& c) { c /= s; });
685 cache.invalidate();
686 return *this;
687}
688
689template <ComponentType T>
690std::pair<Polynomial<T>, Polynomial<T>> Polynomial<T>::poly_long_div(const Polynomial<T>& rhs) const
692{
693 if (rhs.is_zero()) throw std::invalid_argument("polynomial long division by zero polynomial");
694
695 const auto rhs_degree = rhs.degree();
696 const auto rhs_lc = rhs.leading_coefficient();
697
698 if (rhs_degree == 0) return std::make_pair(*this / rhs[0], Polynomial<T>({0}));
699
700 if (degree() < rhs_degree) return std::make_pair(Polynomial<T>({0}), *this);
701
702 Polynomial<T> q;
703 Polynomial<T> r = *this;
704
705 while (r.degree() >= rhs_degree) {
706 const T t = r.leading_coefficient() / rhs_lc;
707 const size_t i = r.degree() - rhs_degree;
709 for (size_t j = 0; j <= rhs_degree; ++j) r.add_to_coefficient(i + j, -(t * rhs[j]));
710 }
711
712 return std::make_pair(std::move(q), std::move(r));
713}
714
715template <ComponentType T>
716Polynomial<T>& Polynomial<T>::randomize(size_t d) {
717 data.resize(d + 1);
718 if constexpr (FieldType<T>) {
719 std::ranges::for_each(data, std::mem_fn(&T::randomize));
720 do {
721 data.back().randomize();
722 } while (data.back() == T(0));
723 } else if constexpr (std::same_as<T, double>) {
724 std::uniform_real_distribution<double> dist(-1.0, 1.0);
725 std::ranges::for_each(data, [&](double& val) { val = dist(gen()); });
726 do {
727 data.back() = dist(gen());
728 } while (data.back() == T(0));
729 } else if constexpr (std::same_as<T, std::complex<double>>) {
730 std::uniform_real_distribution<double> dist(-1.0, 1.0);
731 std::ranges::for_each(data,
732 [&](std::complex<double>& val) { val = std::complex<double>(dist(gen()), dist(gen())); });
733 do {
734 data.back() = std::complex<double>(dist(gen()), dist(gen()));
735 } while (data.back() == T(0));
736 } else if constexpr (SignedIntType<T>) {
737 std::uniform_int_distribution<long long> dist(-100, 100);
738 std::ranges::for_each(data, [&](T& val) { val = T(dist(gen())); });
739 do {
740 data.back() = dist(gen());
741 } while (data.back() == T(0));
742 }
743 cache.invalidate();
744 return *this;
745}
746
747template <ComponentType T>
750{
751 if (data.empty()) throw std::invalid_argument("trying to differentiate empty polynomial");
752
753 if (s == 0) return *this;
754 const size_t d = data.size() - 1;
755 if (d == 0 || s > d) {
756 data.resize(1);
757 data[0] = T(0);
758 return *this;
759 }
760 if constexpr (requires { typename T::BASE_FIELD; }) {
761 using B = typename T::BASE_FIELD;
762 for (size_t i = 0; i <= d - s; ++i) {
763 B coeff(1);
764 for (size_t k = 1; k <= s; ++k) coeff *= B(i + k);
765 data[i] = T(coeff) * data[i + s];
766 }
767 } else {
768 for (size_t i = 0; i <= d - s; ++i) {
769 T coeff(1);
770 for (size_t k = 1; k <= s; ++k) coeff *= T(i + k);
771 data[i] = coeff * data[i + s];
772 }
773 }
774 data.resize(data.size() - s);
775 prune();
777 return *this;
778}
779
780template <ComponentType T>
783{
784 if (data.empty()) throw std::invalid_argument("trying to Hasse differentiate empty polynomial");
785
786 if (s == 0) return *this;
787 const size_t d = data.size() - 1;
788 if (d == 0 || s > d) {
789 data.resize(1);
790 data[0] = T(0);
791 return *this;
792 }
793 for (size_t i = 0; i <= d - s; ++i) data[i] = bin<size_t>(i + s, s) * data[i + s];
794 data.resize(data.size() - s);
795 prune();
797 return *this;
798}
799
800template <ComponentType T>
803{
804 if (is_empty()) throw std::invalid_argument("calculating degree of empty polynomial");
805 return (data.size() - 1);
806}
807
808template <ComponentType T>
810 Vector<T> res(data.size());
811 for (size_t i = 0; i < data.size(); ++i) res.set_component(i, data[i]);
812 return res;
813}
814
815template <ComponentType T>
818{
819 if (is_empty()) throw std::invalid_argument("calculating trailing degree of empty polynomial");
820 const size_t d = degree();
821 if (d == 0) return 0;
822
823 const auto first_nonzero = std::find_if(data.begin(), data.end(), [](const T& coeff) { return coeff != T(0); });
824 return std::distance(data.begin(), first_nonzero);
825}
826
827template <ComponentType T>
830{
831 if (is_empty())
832 throw std::invalid_argument(
833 "trying to access non-existent element (trailing "
834 "coefficient)");
835 return data[trailing_degree()];
836}
837
838template <ComponentType T>
839const T& Polynomial<T>::leading_coefficient() const {
840 if (is_empty())
841 throw std::invalid_argument(
842 "trying to access non-existent element (leading "
843 "coefficient)");
844 return data.back();
845}
846
847template <ComponentType T>
848template <typename U>
849constexpr Polynomial<T>& Polynomial<T>::add_to_coefficient(size_t i, U&& c)
850 requires std::convertible_to<std::decay_t<U>, T>
851{
852 T c_converted = std::forward<U>(c);
853 if (c_converted == T(0)) return *this;
854
856 if (data.empty() || i >= data.size()) {
857 // automatic growth, change zero coefficient beyond degree
858 data.resize(i + 1);
860 } else if (i == data.size() - 1) {
861 // change MSC
862 data.back() += c_converted;
863 if (data.back() == T(0)) prune();
864 } else {
865 // change some coefficient
866 data[i] += c_converted;
867 }
868 return *this;
869}
870
871template <ComponentType T>
872template <typename U>
875{
876 T new_value = std::forward<U>(c);
877 if (data.empty() || i >= data.size()) {
878 if (new_value == T(0)) return *this;
880 data.resize(i + 1);
881 data[i] = std::move(new_value);
882 } else {
883 if (data[i] == new_value) return *this;
885 data[i] = std::move(new_value);
886 if (i == data.size() - 1 && data.back() == T(0)) prune();
887 }
888 return *this;
889}
890
891template <ComponentType T>
893 std::reverse(data.begin(), data.end());
894 prune(); // a zero constant term in the original becomes a leading zero after reversal
895 return *this;
896}
897
898template <ComponentType T>
901{
902 if (is_zero() || is_monic()) return *this;
903 *this /= leading_coefficient();
904 return *this;
905}
906
907template <ComponentType T>
908constexpr T Polynomial<T>::operator[](size_t i) const {
909 if (i >= data.size()) return T(0);
910 return data[i];
911}
912
913template <ComponentType T>
914constexpr size_t Polynomial<T>::calculate_weight() const
916{
917 size_t res = data.size() - std::count(data.cbegin(), data.cend(), T(0));
918
919#ifdef CECCO_ERASURE_SUPPORT
920 if constexpr (FieldType<T>) res -= std::count_if(data.cbegin(), data.cend(), [](T x) { return x.is_erased(); });
921#endif
922
923 return res;
924}
925
926template <ComponentType T>
927constexpr Polynomial<T>& Polynomial<T>::prune() {
928 if (data.empty()) return *this;
929
930 const auto lc = std::find_if(data.crbegin(), data.crend(), [](const T& e) { return e != T(0); });
931 if (lc != data.crend()) {
933 } else {
934 data.resize(1);
935 data.back() = T(0);
936 }
937 return *this;
938}
939
940/* free functions wrt. Polynomial */
941
942template <ComponentType T>
943constexpr Polynomial<T> operator+(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
945 res += rhs;
946 return res;
947}
948
949template <ComponentType T>
950constexpr Polynomial<T> operator+(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
952 res += rhs;
953 return res;
954}
955
956template <ComponentType T>
957constexpr Polynomial<T> operator+(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
959 res += lhs;
960 return res;
961}
962
963template <ComponentType T>
966 res += rhs;
967 return res;
968}
969
970template <ComponentType T>
971constexpr Polynomial<T> operator-(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
973 res -= rhs;
974 return res;
975}
976
977template <ComponentType T>
978constexpr Polynomial<T> operator-(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
980 res -= rhs;
981 return res;
982}
983
984template <ComponentType T>
985constexpr Polynomial<T> operator-(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
987 res = -res;
988 res += lhs;
989 return res;
990}
991
992template <ComponentType T>
995 res -= rhs;
996 return res;
997}
998
999template <ComponentType T>
1000constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1001 Polynomial<T> res(lhs);
1002 res *= rhs;
1003 return res;
1004}
1005
1006template <ComponentType T>
1007constexpr Polynomial<T> operator*(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1008 Polynomial<T> res(std::move(lhs));
1009 res *= rhs;
1010 return res;
1011}
1012
1013template <ComponentType T>
1014constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, Polynomial<T>&& rhs) {
1015 Polynomial<T> res(std::move(rhs));
1016 res *= lhs;
1017 return res;
1018}
1019
1020template <ComponentType T>
1022 Polynomial<T> res(std::move(lhs));
1023 res *= rhs;
1024 return res;
1025}
1026
1027template <ComponentType T>
1028constexpr Polynomial<T> operator/(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1029 Polynomial<T> res(lhs);
1030 res /= rhs;
1031 return res;
1032}
1033
1034template <ComponentType T>
1035constexpr Polynomial<T> operator/(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1036 Polynomial<T> res(std::move(lhs));
1037 res /= rhs;
1038 return res;
1039}
1040
1041template <ComponentType T>
1042constexpr Polynomial<T> operator%(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1043 Polynomial<T> res(lhs);
1044 res %= rhs;
1045 return res;
1046}
1047
1048template <ComponentType T>
1049constexpr Polynomial<T> operator%(Polynomial<T>&& lhs, const Polynomial<T>& rhs) {
1050 Polynomial<T> res(std::move(lhs));
1051 res %= rhs;
1052 return res;
1053}
1054
1055template <ComponentType T>
1056constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, const T& rhs) {
1057 Polynomial<T> res(lhs);
1058 res *= rhs;
1059 return res;
1060}
1061
1062template <ComponentType T>
1063constexpr Polynomial<T> operator*(Polynomial<T>&& lhs, const T& rhs) {
1064 Polynomial<T> res(std::move(lhs));
1065 res *= rhs;
1066 return res;
1067}
1068
1069template <ComponentType T>
1070constexpr Polynomial<T> operator*(const T& lhs, const Polynomial<T>& rhs) {
1071 Polynomial<T> res(rhs);
1072 res *= lhs;
1073 return res;
1074}
1075
1076template <ComponentType T>
1077constexpr Polynomial<T> operator*(const T& lhs, Polynomial<T>&& rhs) {
1078 Polynomial<T> res(std::move(rhs));
1079 res *= lhs;
1080 return res;
1081}
1082
1083// polynomial / scalar (corresponding to operator/= with T)
1084template <ComponentType T>
1085constexpr Polynomial<T> operator/(const Polynomial<T>& lhs, const T& rhs) {
1086 Polynomial<T> res(lhs);
1087 res /= rhs;
1088 return res;
1089}
1090
1091template <ComponentType T>
1092constexpr Polynomial<T> operator/(Polynomial<T>&& lhs, const T& rhs) {
1093 Polynomial<T> res(std::move(lhs));
1094 res /= rhs;
1095 return res;
1096}
1097
1098template <ComponentType T>
1099constexpr Polynomial<T> operator*(const Polynomial<T>& lhs, size_t n) {
1100 Polynomial<T> res(lhs);
1101 res *= n;
1102 return res;
1103}
1104
1105template <ComponentType T>
1107 Polynomial<T> res(std::move(lhs));
1108 res *= n;
1109 return res;
1110}
1111
1112template <ComponentType T>
1113constexpr Polynomial<T> operator*(size_t n, const Polynomial<T>& rhs) {
1114 Polynomial<T> res(rhs);
1115 res *= n;
1116 return res;
1117}
1118
1119template <ComponentType T>
1121 Polynomial<T> res(std::move(rhs));
1122 res *= n;
1123 return res;
1124}
1125
1126template <ComponentType T>
1131
1132template <ComponentType T>
1137template <ComponentType T>
1140{
1141 Polynomial<T> res(poly);
1143 return res;
1144}
1145
1146template <ComponentType T>
1149{
1152 return res;
1153}
1154
1155template <ComponentType T>
1158{
1159 Polynomial<T> res(poly);
1161 return res;
1162}
1163
1164template <ComponentType T>
1167{
1170 return res;
1171}
1172
1173template <ComponentType T>
1175 Polynomial<T> res(poly);
1176 res.reciprocal();
1177 return res;
1178}
1179
1180template <ComponentType T>
1183 res.reciprocal();
1184 return res;
1185}
1186
1187template <ComponentType T>
1190{
1191 Polynomial<T> res(poly);
1192 res.normalize();
1193 return res;
1194}
1195
1196template <ComponentType T>
1199{
1201 res.normalize();
1202 return res;
1203}
1204
1205template <ComponentType T>
1206constexpr bool operator==(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1207 return lhs.data == rhs.data;
1208}
1209
1210template <ComponentType T>
1211constexpr bool operator!=(const Polynomial<T>& lhs, const Polynomial<T>& rhs) {
1212 return !(lhs == rhs);
1213}
1214
1215template <ComponentType T>
1217 if (rhs.data.empty()) {
1218 os << "()";
1219 return os;
1220 }
1221
1222 bool next_negative = false;
1223 for (size_t i = 0; i < rhs.data.size(); ++i) {
1224 auto coeff = rhs.data[i];
1225 if (next_negative) coeff *= -T(1);
1226 if (coeff != T(0) || rhs.data.size() == 1) {
1227 if (coeff != T(1) || i == 0) {
1228 os << coeff;
1229 }
1230 if (i > 0) {
1231 os << "x";
1232 if (i > 1) os << "^" << i;
1233 }
1234 if (i < rhs.data.size() - 1) {
1235 bool positive_sign;
1236 if constexpr (FieldType<T>) {
1238 } else if constexpr (std::is_same_v<T, std::complex<double>>) {
1239 positive_sign = rhs.data[i + 1].real() >= 0 || rhs.data[i + 1].imag() >= 0;
1240 } else {
1241 positive_sign = rhs.data[i + 1] >= 0;
1242 }
1243
1244 if (positive_sign) {
1245 next_negative = false;
1246 os << " + ";
1247 } else {
1248 next_negative = true;
1249 os << " - ";
1250 }
1251 }
1252 }
1253 }
1254
1255 return os;
1256}
1257
1258/**
1259 * @brief Monomial a · xⁱ
1260 *
1261 * @param i Power of x
1262 * @param a Coefficient (defaults to T(1))
1263 */
1264template <ComponentType T>
1265constexpr Polynomial<T> Monomial(size_t i, auto&& a = T(1))
1266 requires std::convertible_to<std::decay_t<decltype(a)>, T>
1267{
1268 Polynomial<T> res;
1269 res.set_coefficient(i, std::forward<decltype(a)>(a));
1270 return res;
1271}
1272
1273/// @brief Constant polynomial 0 (cached)
1274template <ComponentType T>
1276 static const Polynomial<T> zero(0);
1277 return zero;
1278}
1279
1280/// @brief Constant polynomial 1 (cached)
1281template <ComponentType T>
1283 static const Polynomial<T> one(1);
1284 return one;
1285}
1286
1287/**
1288 * @brief Greatest common divisor of two polynomials via the (extended) Euclidean algorithm
1289 *
1290 * @param a First polynomial
1291 * @param b Second polynomial
1292 * @param s Optional out-pointer for the Bézout coefficient of @p a
1293 * @param t Optional out-pointer for the Bézout coefficient of @p b
1294 * @return gcd(a, b); if both @p s and @p t are non-null, additionally `gcd(a, b) = s·a + t·b`
1295 *
1296 * Operands are reordered internally so that the higher-degree one drives the recursion.
1297 */
1298template <ComponentType T>
1299Polynomial<T> GCD(Polynomial<T> a, Polynomial<T> b, Polynomial<T>* s = nullptr, Polynomial<T>* t = nullptr)
1301{
1302 if (a.degree() < b.degree()) std::swap(a, b);
1303
1304 if (s != nullptr && t != nullptr) { // extended EA
1305 *s = Polynomial<T>({1});
1306 *t = Polynomial<T>({0});
1307 Polynomial<T> u = Polynomial<T>({0});
1308 Polynomial<T> v = Polynomial<T>({1});
1309 // while (b.degree() > 0) {
1310 while (!b.is_zero()) {
1311 const Polynomial<T> q = a / b;
1312 Polynomial<T> b1 = std::move(b);
1313 b = a - q * b1;
1314 a = std::move(b1);
1315 Polynomial<T> u1 = std::move(u);
1316 u = *s - q * u1;
1317 *s = std::move(u1);
1318 Polynomial<T> v1 = std::move(v);
1319 v = *t - q * v1;
1320 *t = std::move(v1);
1321 }
1322 } else { // "normal" EA
1323 // while (b.degree() > 0) {
1324 while (!b.is_zero()) {
1325 const Polynomial<T> q = a / b;
1326 Polynomial<T> b1 = std::move(b);
1327 b = a - q * b1;
1328 a = std::move(b1);
1329 }
1330 }
1331 return a;
1332}
1333
1334/**
1335 * @brief Greatest common divisor of a list of polynomials, computed iteratively
1336 *
1337 * @return gcd(p₁, p₂, …, pₙ)
1338 * @throws std::invalid_argument if @p polys is empty
1339 */
1340template <ComponentType T>
1343{
1344 if (polys.empty())
1345 throw std::invalid_argument("trying to calculate polynomial GCD but didn't provide any polynomials");
1346 if (polys.size() == 1) return polys[0];
1347
1348 Polynomial<T> res = polys.front();
1349 for (auto it = polys.cbegin() + 1; it != polys.cend(); ++it) {
1350 res = GCD<T>(std::move(res), *it);
1351 }
1352 return res;
1353}
1354
1355/// @brief Least common multiple `lcm(a, b) = (a · b) / gcd(a, b)`
1356template <ComponentType T>
1359{
1360 return (a * b) / GCD(a, b);
1361}
1362
1363/**
1364 * @brief Least common multiple of a list of polynomials, computed iteratively
1365 *
1366 * @return lcm(p₁, p₂, …, pₙ)
1367 * @throws std::invalid_argument if @p polys is empty
1368 */
1369template <ComponentType T>
1372{
1373 if (polys.empty())
1374 throw std::invalid_argument("trying to calculate polynomial LCM but didn't provide any polynomials");
1375 if (polys.size() == 1) return polys[0];
1376
1377 Polynomial<T> res = polys.front();
1378 for (auto it = polys.cbegin() + 1; it != polys.cend(); ++it) {
1379 res = LCM<T>(res, *it);
1380 }
1381 return res;
1382}
1383
1384/**
1385 * @brief Polynomial exponentiation by square-and-multiply
1386 *
1387 * @return base^exponent
1388 *
1389 * @warning Does **not** follow C++ precedence for `^`: in `b * base ^ exponent` the parser
1390 * evaluates `(b * base) ^ exponent`. Parenthesise as `b * (base ^ exponent)`, or call
1391 * @ref CECCO::sqm directly.
1392 */
1393template <ComponentType T>
1394constexpr Polynomial<T> operator^(const Polynomial<T>& base, int exponent) {
1395 return sqm<Polynomial<T>>(base, exponent);
1396}
1397
1398/**
1399 * @brief Coefficient vector [a₀, a₁, …, aₘ] of the Conway polynomial for 𝔽_{p^m}
1400 *
1401 * @tparam p Prime characteristic
1402 * @tparam m Extension degree
1403 * @return Coefficients low-to-high; empty vector if the (p, m) pair is not in the built-in table
1404 *
1405 * The built-in table covers all primes ≤ 97 and degrees ≤ ~13 (more for small p).
1406 */
1407template <uint16_t p, size_t m>
1409 if constexpr (p == 2) {
1410 if constexpr (m == 1)
1411 return {1, 1};
1412 else if constexpr (m == 2)
1413 return {1, 1, 1};
1414 else if constexpr (m == 3)
1415 return {1, 1, 0, 1};
1416 else if constexpr (m == 4)
1417 return {1, 1, 0, 0, 1};
1418 else if constexpr (m == 5)
1419 return {1, 0, 1, 0, 0, 1};
1420 else if constexpr (m == 6)
1421 return {1, 1, 0, 1, 1, 0, 1};
1422 else if constexpr (m == 7)
1423 return {1, 1, 0, 0, 0, 0, 0, 1};
1424 else if constexpr (m == 8)
1425 return {1, 0, 1, 1, 1, 0, 0, 0, 1};
1426 else if constexpr (m == 9)
1427 return {1, 0, 0, 0, 1, 0, 0, 0, 0, 1};
1428 else if constexpr (m == 10)
1429 return {1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1};
1430 else if constexpr (m == 11)
1431 return {1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
1432 else if constexpr (m == 12)
1433 return {1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1};
1434 else if constexpr (m == 13)
1435 return {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1};
1436 } else if constexpr (p == 3) {
1437 if constexpr (m == 1)
1438 return {1, 1};
1439 else if constexpr (m == 2)
1440 return {2, 2, 1};
1441 else if constexpr (m == 3)
1442 return {1, 2, 0, 1};
1443 else if constexpr (m == 4)
1444 return {2, 0, 0, 2, 1};
1445 else if constexpr (m == 5)
1446 return {1, 2, 0, 0, 0, 1};
1447 else if constexpr (m == 6)
1448 return {2, 2, 1, 0, 2, 0, 1};
1449 else if constexpr (m == 7)
1450 return {1, 0, 2, 0, 0, 0, 0, 1};
1451 else if constexpr (m == 8)
1452 return {2, 2, 2, 0, 1, 2, 0, 0, 1};
1453 } else if constexpr (p == 5) {
1454 if constexpr (m == 1)
1455 return {3, 1};
1456 else if constexpr (m == 2)
1457 return {2, 4, 1};
1458 else if constexpr (m == 3)
1459 return {3, 3, 0, 1};
1460 else if constexpr (m == 4)
1461 return {2, 4, 4, 0, 1};
1462 else if constexpr (m == 5)
1463 return {3, 4, 0, 0, 0, 1};
1464 } else if constexpr (p == 7) {
1465 if constexpr (m == 1)
1466 return {4, 1};
1467 else if constexpr (m == 2)
1468 return {3, 6, 1};
1469 else if constexpr (m == 3)
1470 return {4, 0, 6, 1};
1471 else if constexpr (m == 4)
1472 return {3, 4, 5, 0, 1};
1473 } else if constexpr (p == 11) {
1474 if constexpr (m == 1)
1475 return {9, 1};
1476 else if constexpr (m == 2)
1477 return {2, 7, 1};
1478 else if constexpr (m == 3)
1479 return {9, 2, 0, 1};
1480 else if constexpr (m == 4)
1481 return {3, 4, 5, 0, 1};
1482 } else if constexpr (p == 13) {
1483 if constexpr (m == 1)
1484 return {11, 1};
1485 else if constexpr (m == 2)
1486 return {2, 12, 1};
1487 else if constexpr (m == 3)
1488 return {11, 2, 0, 1};
1489 } else if constexpr (p == 17) {
1490 if constexpr (m == 1)
1491 return {14, 1};
1492 else if constexpr (m == 2)
1493 return {3, 16, 1};
1494 else if constexpr (m == 3)
1495 return {14, 1, 0, 1};
1496 } else if constexpr (p == 19) {
1497 if constexpr (m == 1)
1498 return {17, 1};
1499 else if constexpr (m == 2)
1500 return {2, 18, 1};
1501 else if constexpr (m == 3)
1502 return {17, 4, 0, 1};
1503 } else if constexpr (p == 23) {
1504 if constexpr (m == 1)
1505 return {18, 1};
1506 else if constexpr (m == 2)
1507 return {5, 21, 1};
1508 } else if constexpr (p == 29) {
1509 if constexpr (m == 1)
1510 return {27, 1};
1511 else if constexpr (m == 2)
1512 return {2, 24, 1};
1513 } else if constexpr (p == 31) {
1514 if constexpr (m == 1)
1515 return {28, 1};
1516 else if constexpr (m == 2)
1517 return {3, 29, 1};
1518 } else if constexpr (p == 37) {
1519 if constexpr (m == 1)
1520 return {35, 1};
1521 else if constexpr (m == 2)
1522 return {2, 33, 1};
1523 } else if constexpr (p == 43) {
1524 if constexpr (m == 1)
1525 return {40, 1};
1526 else if constexpr (m == 2)
1527 return {3, 42, 1};
1528 } else if constexpr (p == 47) {
1529 if constexpr (m == 1)
1530 return {42, 1};
1531 else if constexpr (m == 2)
1532 return {5, 45, 1};
1533 } else if constexpr (p == 53) {
1534 if constexpr (m == 1)
1535 return {51, 1};
1536 else if constexpr (m == 2)
1537 return {2, 49, 1};
1538 } else if constexpr (p == 59) {
1539 if constexpr (m == 1)
1540 return {57, 1};
1541 else if constexpr (m == 2)
1542 return {2, 58, 1};
1543 } else if constexpr (p == 61) {
1544 if constexpr (m == 1)
1545 return {59, 1};
1546 else if constexpr (m == 2)
1547 return {2, 60, 1};
1548 } else if constexpr (p == 67) {
1549 if constexpr (m == 1)
1550 return {65, 1};
1551 else if constexpr (m == 2)
1552 return {2, 63, 1};
1553 } else if constexpr (p == 71) {
1554 if constexpr (m == 1)
1555 return {64, 1};
1556 else if constexpr (m == 2)
1557 return {7, 69, 1};
1558 } else if constexpr (p == 73) {
1559 if constexpr (m == 1)
1560 return {68, 1};
1561 else if constexpr (m == 2)
1562 return {5, 70, 1};
1563 } else if constexpr (p == 79) {
1564 if constexpr (m == 1)
1565 return {76, 1};
1566 else if constexpr (m == 2)
1567 return {3, 78, 1};
1568 } else if constexpr (p == 83) {
1569 if constexpr (m == 1)
1570 return {81, 1};
1571 else if constexpr (m == 2)
1572 return {2, 82, 1};
1573 } else if constexpr (p == 89) {
1574 if constexpr (m == 1)
1575 return {86, 1};
1576 else if constexpr (m == 2)
1577 return {3, 82, 1};
1578 } else if constexpr (p == 97) {
1579 if constexpr (m == 1)
1580 return {92, 1};
1581 else if constexpr (m == 2)
1582 return {5, 96, 1};
1583 }
1584 // ... support all fields with less than 10k elements
1585 return Vector<Fp<p>>();
1586}
1587
1588/**
1589 * @brief Conway polynomial for 𝔽_{p^m} as a `Polynomial<Fp<p>>`
1590 *
1591 * Wraps @ref ConwayCoefficients; returns the empty polynomial if (p, m) is not tabulated.
1592 */
1593template <uint16_t p, size_t m>
1595 return Polynomial<Fp<p>>(ConwayCoefficients<p, m>());
1596}
1597
1598/**
1599 * @brief Sample a random monic irreducible polynomial of degree @p degree over T
1600 *
1601 * Tries random polynomials until @ref Polynomial::is_irreducible accepts one. Runtime is
1602 * bounded in expectation by the density of irreducibles, but unbounded in the worst case;
1603 * suitable as a `modulus` for @ref CECCO::Ext.
1604 */
1605template <FieldType T>
1607 if (degree == 0) return Polynomial<T>(T(1));
1608 Polynomial<T> res;
1609 do {
1611 } while (!res.is_irreducible());
1612
1613 res.normalize();
1614 return res;
1615}
1616
1617} // namespace CECCO
1618
1619#endif
Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a CECCO::ComponentType.
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.
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 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.
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.
Polynomial & operator/=(const T &s)
Divide every coefficient by the scalar s.
size_t trailing_degree() const
Trailing degree min{i : aᵢ ≠ 0}.
friend bool operator==(const Polynomial &lhs, const Polynomial &rhs)
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)).
constexpr Polynomial(const Polynomial &other)
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:115
Provides a framework for error correcting codes.
Definition blocks.hpp:57
Polynomial< T > ZeroPolynomial()
Constant polynomial 0 (cached).
constexpr bool operator==(const Polynomial< T > &lhs, const Polynomial< T > &rhs)