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
codes.hpp
Go to the documentation of this file.
1/**
2 * @file codes.hpp
3 * @brief Error control codes library
4 * @author Christian Senger <senger@inue.uni-stuttgart.de>
5 * @version 2.1.1
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
16#ifndef CODES_HPP
17#define CODES_HPP
18
19#include <bit>
20
21#include "code_bounds.hpp"
22#include "trellises.hpp"
23/*
24// transitive
25#include <algorithm>
26#include <array>
27#include <cmath>
28#include <concepts>
29#include <cstddef>
30#include <cstdint>
31#include <exception>
32#include <fstream>
33#include <functional>
34#include <iomanip>
35#include <iostream>
36#include <iterator>
37#include <limits>
38#include <numeric>
39#include <optional>
40#include <random>
41#include <ranges>
42#include <set>
43#include <stdexcept>
44#include <string>
45#include <type_traits>
46#include <unordered_map>
47#include <utility>
48#include <vector>
49
50#include "fields.hpp"
51#include "helpers.hpp"
52#include "matrices.hpp"
53#include "polynomials.hpp"
54#include "vectors.hpp"
55*/
56
57#define BOLD(x) "\033[1m" x "\033[0m"
58
59namespace CECCO {
60
61template <FiniteFieldType T>
63 constexpr size_t q = T::get_size();
64
65 auto a = Vector<InfInt>(n + 1);
66 for (size_t i = 0; i < n + 1; ++i) a.set_component(i, A[i]);
67
68 Matrix<InfInt> K(n + 1, n + 1);
69 for (size_t i = 0; i < n + 1; ++i) {
70 if (a[i] == 0) continue;
71 for (size_t j = 0; j < n + 1; ++j) {
72 InfInt sum = 0;
73 // for (size_t h = 0; h <= std::min<size_t>(i, j); ++h) {
74 for (size_t h = 0; h <= j; ++h) {
75 /*
76 sum += sqm<InfInt>(-1, h) * bin<InfInt>(i, h) * bin<InfInt>(n - i, j - h) *
77 sqm<InfInt>(q - 1, j - h);
78 // the following code calculates exactly this expression - with a couple of
79 // optimizations in order to avoid unnecessary computations
80 */
81 InfInt a = bin<InfInt>(n - i, j - h);
82 if (a != 0) {
83 const auto b = bin<InfInt>(i, h);
84 if (b != 0) {
85 const auto c = sqm<InfInt>(q - 1, j - h);
86 if (h % 2)
87 sum -= a * b * c;
88 else
89 sum += a * b * c;
90 }
91 }
92 }
93 K.set_component(i, j, sum);
94 }
95 }
96 return Polynomial<InfInt>(Vector<InfInt>(a) * Matrix<InfInt>(K) / sqm<InfInt>(q, k));
97}
98
99namespace details {
100
101inline const int index = std::ios_base::xalloc();
102
103} // namespace details
104
105inline std::ostream& showbasic(std::ostream& os) {
106 os.iword(details::index) = 0;
107 return os;
108}
109
110inline std::ostream& showmost(std::ostream& os) {
111 os.iword(details::index) = 1;
112 return os;
113}
114
115inline std::ostream& showall(std::ostream& os) {
116 os.iword(details::index) = 2;
117 return os;
118}
119
120inline std::ostream& showspecial(std::ostream& os) {
121 os.iword(details::index) = 3;
122 return os;
123}
124
125template <ComponentType T>
126class Code {
127 public:
128 Code(size_t n) : n(n) {}
129
130 Code(const Code& other) : n(other.n) {}
131
132 Code(Code&&) = default;
133
134 virtual ~Code() = default;
135
136 Code& operator=(const Code& other) {
137 if (this != &other) {
138 n = other.n;
139 }
140 return *this;
141 }
142
143 Code& operator=(Code&&) = default;
144
145 size_t get_n() const noexcept { return n; }
146
147 virtual void get_info(std::ostream& os) const {};
148 virtual Vector<T> enc(const Vector<T>& u) const { throw std::logic_error("Encoding not supported for this code!"); }
149 virtual Vector<T> encinv(const Vector<T>& c) const {
150 throw std::logic_error("Inverse encoding not supported for this code!");
151 }
152 virtual Vector<T> dec_BD(const Vector<T>& r) const {
153 throw std::logic_error("Bounded distance decoding not supported for this code!");
154 }
155 virtual Vector<T> dec_boosted_BD(const Vector<T>& r) const {
156 throw std::logic_error("Boosted bounded distance decoding not supported for this code!");
157 }
158 virtual Vector<T> dec_ML(const Vector<T>& r) const {
159 throw std::logic_error("ML decoding not supported for this code!");
160 }
161 virtual Vector<T> dec_Viterbi(const Vector<T>& r, const std::string& filename = "") const {
162 throw std::logic_error("Viterbi decoding not supported for this code!");
163 }
164 virtual Vector<T> dec_ML_soft(const Vector<double>& llrs, size_t cache_size) const {
165 throw std::logic_error("Soft-input ML decoding not supported for this code!");
166 }
167 virtual Vector<T> dec_Viterbi_soft(const Vector<double>& llrs, const std::string& filename = "") const {
168 throw std::logic_error("Soft-input Viterbi decoding not supported for this code!");
169 }
170 virtual Vector<double> dec_BCJR(const Vector<double>& llrs, const std::string& filename = "") const {
171 throw std::logic_error("BCJR decoding not supported for this code!");
172 }
173 virtual Vector<T> dec_burst(const Vector<T>& r) const {
174 throw std::logic_error("Burst decoding not supported for this code!");
175 }
176 virtual Vector<T> dec_recursive(const Vector<T>& r) const {
177 throw std::logic_error("Recursive decoding not supported for this code!");
178 }
179 virtual Vector<T> dec_Meggitt(const Vector<T>& r) const {
180 throw std::logic_error("Meggitt decoding not supported for this code!");
181 }
182 virtual Vector<T> dec_WBA(const Vector<T>& r) const {
183 throw std::logic_error("Welch-Berlekamp decoding not supported for this code!");
184 }
185 virtual Vector<T> dec_BMA(const Vector<T>& r) const {
186 throw std::logic_error("Berlekamp-Massey decoding not supported for this code!");
187 }
188#ifdef CECCO_ERASURE_SUPPORT
189 virtual Vector<T> dec_BD_EE(const Vector<T>& r) const {
190 throw std::logic_error("BD error/erasure decoding not supported for this code!");
191 }
192 virtual Vector<T> dec_ML_EE(const Vector<T>& r) const {
193 throw std::logic_error("ML error/erasure decoding not supported for this code!");
194 }
195 virtual Vector<T> dec_Viterbi_EE(const Vector<T>& r, const std::string& filename = "") const {
196 throw std::logic_error("Viterbi error/erasure decoding not supported for this code!");
197 }
198 virtual Vector<T> dec_recursive_EE(const Vector<T>& r) const {
199 throw std::logic_error("Recursive error/erasure decoding not supported for this code!");
200 }
201 virtual Vector<T> dec_WBA_EE(const Vector<T>& r) const {
202 throw std::logic_error("Welch-Berlekamp error/erasure decoding not supported for this code!");
203 }
204 virtual Vector<T> dec_BMA_EE(const Vector<T>& r) const {
205 throw std::logic_error("Berlekamp-Massey error/erasure decoding not supported for this code!");
206 }
207#endif
208
209 protected:
211#ifdef CECCO_ERASURE_SUPPORT
213 std::vector<size_t> X;
214 for (size_t i = 0; i < n; ++i) {
215 if (r[i].is_erased()) X.push_back(i);
216 }
217 return X;
218 }
219#endif
220};
221
222template <ComponentType T>
223class EmptyCode : public Code<T> {
224 public:
225 EmptyCode(size_t n) : Code<T>(n) {}
226
227 EmptyCode(const EmptyCode&) = default;
228 EmptyCode(EmptyCode&&) = default;
229 EmptyCode& operator=(const EmptyCode&) = default;
231
232 void get_info(std::ostream& os) const override {
233 if (os.iword(details::index) > 0) {
234 Code<T>::get_info(os);
235 os << "Empty code";
236 }
237 }
238};
239
240template <FieldType T>
241class LinearCode;
242
243template <FiniteFieldType T>
244class UniverseCode;
245
246template <FiniteFieldType T>
247class ZeroCode;
248
249template <FiniteFieldType T>
250class SimplexCode;
251
252template <FiniteFieldType T>
254
255template <FieldType T, class B>
256 requires std::derived_from<B, LinearCode<T>>
257class ExtendedCode;
258
259template <FiniteFieldType T>
261 friend bool operator==(const CodewordIterator& a, const CodewordIterator& b) { return a.counter == b.counter; }
262 friend bool operator!=(const CodewordIterator& a, const CodewordIterator& b) { return !(a == b); }
263
264 public:
265 // Required for STL compatibility
266 using iterator_category = std::forward_iterator_tag;
267 using value_type = Vector<T>;
268 using difference_type = std::ptrdiff_t;
269 using pointer = const Vector<T>*;
270 using reference = const Vector<T>&;
271
272 CodewordIterator(const LinearCode<T>& C, InfInt s) : C(C), counter(s), u(C.get_k()) {
273 constexpr size_t q = T::get_size();
274 if (s < C.get_size()) { // for cend()
275 const size_t k = C.get_k();
276 for (size_t i = 0; i < k; ++i) {
277 u.set_component(i, T((s % q).toInt()));
278 s /= q;
279 }
280 c = u * C.get_G();
281 }
282 }
283
284 const Vector<T>& operator*() const noexcept { return c; }
285
287 constexpr size_t q = T::get_size();
288 const size_t k = C.get_k();
289 ++counter;
290 if (counter < C.get_size()) {
291 auto j = counter;
292 for (size_t i = 0; i < k; ++i) {
293 const auto quot = j / q;
294 const auto rem = (j % q).toInt();
295 u.set_component(i, T(rem));
296 j = quot;
297 }
298 c = u * C.get_G();
299 }
300 return *this;
301 }
302
303 private:
304 const LinearCode<T>& C;
305 InfInt counter;
306 Vector<T> u;
307 Vector<T> c;
308};
309
310class decoding_failure : public std::exception {
311 public:
312 decoding_failure(const std::string& message) : message(message) {}
313 const char* what() const noexcept override { return message.c_str(); }
314
315 private:
316 const std::string message;
317};
318
319template <FieldType T>
320class LinearCode : public Code<T> {
321 public:
322 // expose base field (used by the extend() factories)
323 using FIELD = T;
324
325 // NOTE for documentation: X is interpreted as generator matrix G if it has k rows, or as
326 // parity-check matrix H if it has n-k rows. If k == n-k, the ambiguity is resolved in favor
327 // of G; callers who mean H must pass the dual code's generator instead.
328 LinearCode(size_t n, size_t k, const Matrix<T>& X) : Code<T>(n), k(k), MI(k, k) {
329 if (X.get_n() != this->n) throw std::invalid_argument("G must have " + std::to_string(this->n) + " columns");
330 if (k == 0) {
331 if (!X.is_zero() && !X.is_invertible())
332 throw std::invalid_argument("Cannot construct linear code: G must be a zero matrix");
333 G = ZeroMatrix<T>(1, n);
334 HT = IdentityMatrix<T>(n);
335 return;
336 }
337
338 if (X.get_m() == k) {
339 // X supposed to be generator matrix G
340 if (X.rank() != k)
341 throw std::invalid_argument("Cannot construct linear code: G must have full rank " + std::to_string(k));
342 G = X;
343 HT = G.basis_of_nullspace().rref().transpose();
344 const auto Gp = rref(G);
345 size_t i = 0;
346 for (size_t j = 0; j < k; ++j) {
347 const auto u = unit_vector<T>(k, j);
348 while (Gp.get_col(i) != u) ++i;
349 infoset.push_back(i);
350 }
351 } else if (X.get_m() == this->n - k) {
352 // X supposed to be parity check matrix H
353 if (X.rank() != this->n - k)
354 throw std::invalid_argument("Cannot construct linear code: H must have full rank " +
355 std::to_string(this->n - k));
356 G = X.basis_of_nullspace();
357 HT = transpose(X);
358 G.rref();
359 size_t i = 0;
360 for (size_t j = 0; j < k; ++j) {
361 const auto u = unit_vector<T>(k, j);
362 while (G.get_col(i) != u) ++i;
363 infoset.push_back(i);
364 }
365 } else {
366 throw std::invalid_argument("Cannot construct linear code: matrix must have " + std::to_string(k) +
367 " rows (as G) or " + std::to_string(this->n - k) + " rows (as H), got " +
368 std::to_string(X.get_m()));
369 }
370 size_t j = 0;
371 for (auto it = infoset.cbegin(); it != infoset.cend(); ++it) {
372 MI.set_submatrix(0, j, G.get_submatrix(0, *it, k, 1));
373 ++j;
374 }
375 MI.invert();
376 }
377
378 LinearCode(size_t k, Polynomial<T> gamma) try
379 : LinearCode(
380 k + gamma.degree(), k,
381 ToeplitzMatrix(pad_back(pad_front(Vector<T>(gamma), k + gamma.degree()), 2 * k + gamma.degree() - 1), k,
382 k + gamma.degree())) {
383 set_gamma(std::move(gamma));
384 } catch (const std::invalid_argument& e) {
385 throw std::invalid_argument(std::string("Cannot construct linear code from polynomial: ") + e.what());
386 }
387
388 LinearCode(const LinearCode& other)
389 : Code<T>(other),
390 k(other.k),
391 G(other.G),
392 HT(other.HT),
393 MI(other.MI),
395 dmin(other.dmin),
402#ifdef CECCO_ERASURE_SUPPORT
405#endif
407 gamma(other.gamma),
408 minimal_trellis(other.minimal_trellis) {
409 }
410
412 : Code<T>(std::move(other)),
413 k(other.k),
414 G(std::move(other.G)),
415 HT(std::move(other.HT)),
416 MI(std::move(other.MI)),
425#ifdef CECCO_ERASURE_SUPPORT
428#endif
430 gamma(std::move(other.gamma)),
431 minimal_trellis(std::move(other.minimal_trellis)) {
432 }
433
435 if (this != &other) {
436 Code<T>::operator=(other);
437 k = other.k;
438 G = other.G;
439 HT = other.HT;
440 MI = other.MI;
441 infoset = other.infoset;
442 dmin = other.dmin;
443 weight_enumerator = other.weight_enumerator;
444 codewords = other.codewords;
445 standard_array = other.standard_array;
446 tainted = other.tainted;
447 tainted_burst = other.tainted_burst;
448 Meggitt_table = other.Meggitt_table;
449#ifdef CECCO_ERASURE_SUPPORT
450 punctured_codes_BD = other.punctured_codes_BD;
451 punctured_codes_ML = other.punctured_codes_ML;
452#endif
453 polynomial = other.polynomial;
454 gamma = other.gamma;
455 minimal_trellis = other.minimal_trellis;
456 }
457 return *this;
458 }
459
461 if (this != &other) {
462 Code<T>::operator=(std::move(other));
463 k = other.k;
464 G = std::move(other.G);
465 HT = std::move(other.HT);
466 MI = std::move(other.MI);
467 infoset = std::move(other.infoset);
468 dmin = std::move(other.dmin);
469 weight_enumerator = std::move(other.weight_enumerator);
470 codewords = std::move(other.codewords);
471 standard_array = std::move(other.standard_array);
472 tainted = std::move(other.tainted);
473 tainted_burst = std::move(other.tainted_burst);
474 Meggitt_table = std::move(other.Meggitt_table);
475#ifdef CECCO_ERASURE_SUPPORT
476 punctured_codes_BD = std::move(other.punctured_codes_BD);
477 punctured_codes_ML = std::move(other.punctured_codes_ML);
478#endif
479 polynomial = std::move(other.polynomial);
480 gamma = std::move(other.gamma);
481 minimal_trellis = std::move(other.minimal_trellis);
482 }
483 return *this;
484 }
485
486 size_t get_k() const noexcept { return k; }
487 double get_R() const noexcept { return static_cast<double>(k) / this->n; }
488
491 {
492 return sqm<InfInt>(T::get_size(), k);
493 }
494
495 const Matrix<T>& get_G() const noexcept { return G; }
496 const Matrix<T>& get_HT() const noexcept { return HT; }
497 Matrix<T> get_H() const { return transpose(HT); }
498
499 virtual size_t get_dmin() const {
500 dmin.call_once([this] {
501 if (dmin.has_value()) return;
502 if (k == 0) throw std::logic_error("Cannot calculate dmin of a dimension zero code!");
503
504 // if weight enumerator is calculated, use it...
506 if constexpr (FiniteFieldType<T>) {
507 for (size_t i = 1; i <= weight_enumerator.value().degree(); ++i) {
508 if (weight_enumerator.value()[i] != 0) {
509 dmin = i;
510 return;
511 }
512 }
513 }
514 // ... otherwise:
515 } else {
516 if (k == 1) {
517 dmin = G.get_row(0).wH();
518 return;
519 }
520
521 // if less than 100 codewords calculate weight enumerator (even when not required)
522 if (get_size() < 100) {
523 if constexpr (FiniteFieldType<T>) {
525 for (size_t i = 1; i <= weight_enumerator.value().degree(); ++i) {
526 if (weight_enumerator.value()[i] != 0) {
527 dmin = i;
528 return;
529 }
530 }
531 }
532 }
533
534 std::clog
535 << "--> Calculating dmin, this requires finding minimal number of linearly dependent columns in H"
536 << std::endl;
537 // find min. number of linearly dependent rows of HT
538 for (size_t d = 1; d <= this->n - k + 1; ++d) {
539 Matrix<T> M(d, this->n - k);
540 std::vector<bool> selection(this->n); // zero-initialized
541 std::fill(selection.begin() + this->n - d, selection.end(), true);
542
543 do {
544 size_t i = 0;
545 for (size_t j = 0; j < this->n; ++j) {
546 if (selection[j]) {
547 M.set_submatrix(i, 0, HT.get_submatrix(j, 0, 1, this->n - k));
548 ++i;
549 }
550 }
551 if (M.rank() < d) {
552 dmin = d;
553 return;
554 }
556 }
557 }
558 });
559
560 return dmin.value();
561 }
562
563 size_t get_tmax() const {
564 if (k == 0) throw std::logic_error("Cannot calculate tmax of a dimension zero code!");
565 return (get_dmin() - 1) / 2;
566 }
567
568 virtual const Polynomial<InfInt>& get_weight_enumerator() const {
569 if constexpr (!FiniteFieldType<T>) {
570 throw std::logic_error("Cannot calculate weight enumerator of code over infinite field!");
571 } else {
573 constexpr size_t q = T::get_size();
574 if (weight_enumerator.has_value()) return;
575 if (k == 0) {
577 } else if (k <= this->n - k) { // calculate directly
578 std::clog << "--> Calculating weight enumerator, this requires iterating through "
579 << sqm<InfInt>(q, k) << " codewords" << std::endl;
581 for (auto it = cbegin(); it != cend(); ++it)
583 } else if (k == this->n) {
584 UniverseCode<T> Cp(this->n);
586 } else { // calculate based on dual code and MacWilliams identity
587 std::clog << "--> Using MacWilliams' identity for: " << std::endl;
588 LinearCode<T> Cp(this->n, this->n - k, transpose(HT));
590 }
591 });
592
593 return weight_enumerator.value();
594 }
595 }
596
597 long double P_word(double pe) const {
598 const size_t tmax = get_tmax();
599 long double res = 0.0;
600 for (size_t i = tmax + 1; i <= this->n; ++i)
601 res += bin<InfInt>(this->n, i).toUnsignedLongLong() * std::pow(static_cast<long double>(pe), i) *
602 std::pow(1.0L - pe, this->n - i);
603 return res;
604 }
605
606 long double P_error(double pe) const
608 {
609 const auto& A = get_weight_enumerator();
610 const size_t tmax = get_tmax();
611 long double res = 0.0;
612 for (size_t h = 1; h <= this->n; ++h) {
613 InfInt sum = 0;
614 for (size_t s = 0; s <= tmax; ++s) {
615 for (size_t ell = 1; ell <= this->n; ++ell) sum += A[ell] * N(ell, h, s);
616 }
617 res += std::pow(static_cast<long double>(pe) / (T::get_size() - 1), h) * std::pow(1.0L - pe, this->n - h) *
619 }
620 return res;
621 }
622
623 long double P_failure(double pe) const
625 {
626 long double res = P_word(pe) - P_error(pe);
627 if (std::fabs(res) < 10 * std::numeric_limits<long double>::epsilon())
628 return 0;
629 else
630 return res;
631 }
632
633 long double Bhattacharyya_bound(long double gamma) const {
634 if constexpr (!std::is_same_v<T, Fp<2>>)
635 throw std::logic_error("Bhattacharyya bound can only be calculated for binary codes!");
636 const auto& A = get_weight_enumerator();
637 const size_t dmin = get_dmin();
638
639 long double res = 0;
640 for (size_t i = dmin; i <= A.degree(); ++i) res += A[i].toUnsignedLongLong() * std::pow(gamma, i);
641
642 return res;
643 }
644
645 const Polynomial<T>& get_gamma() const {
646 if (!is_polynomial())
647 throw std::logic_error("Cannot calculate generator polynomial of a code that is not polynomial!");
648 return gamma.value();
649 }
650
651 void set_dmin(size_t d) const noexcept { dmin.emplace(d); }
652
653 void set_weight_enumerator(const Polynomial<InfInt>& p) const noexcept
655 {
657 }
658
664
665 void set_gamma(const Polynomial<T>& g) const noexcept {
666 polynomial.emplace(true);
667 gamma.emplace(g);
668 }
669
670 void set_gamma(Polynomial<T>&& g) const noexcept {
671 polynomial.emplace(true);
673 }
674
677 {
679 if (standard_array.has_value()) return;
680
681 std::clog << "--> Calculating standard array" << std::endl;
682
683 constexpr size_t q = T::get_size();
684
685 const size_t nof_cosets = sqm<size_t>(q, this->n - k);
686 size_t count = 0;
687 bool done = false;
688
689 try {
691 tainted.emplace(std::vector<bool>(nof_cosets, false));
692 tainted_burst.emplace(std::vector<bool>(nof_cosets, false));
693 } catch (const std::bad_alloc& e) {
694 std::cerr << "Memory allocation failed, standard array would be too large!" << std::endl;
695 throw e;
696 }
697
702 std::uniform_real_distribution<double> uniform(0.0, 1.0);
703
704 for (size_t wt = 0; wt <= this->n; ++wt) {
705 std::vector<bool> mask(this->n, false);
706 std::fill(mask.begin(), mask.begin() + wt, true);
707
708 do {
710 pos.reserve(wt);
711 for (auto it = mask.cbegin(); it != mask.cend(); ++it) {
712 if (*it) pos.push_back(static_cast<size_t>(it - mask.cbegin()));
713 }
714
715 Vector<T> v(this->n, T(0));
716 for (size_t j = 0; j < wt; ++j) v.set_component(pos[j], T(1));
717
718 std::vector<size_t> digits(wt, 1);
719
720 for (;;) {
721 const auto s = v * HT;
722 const size_t i = s.as_integer();
723
724 if (standard_array.value()[i].is_empty()) {
725 standard_array.value()[i] = v;
726
727 leader_wH[i] = wt;
729 leader_tie_count[i] = 1;
730
731 ++count;
732 if (count == nof_cosets) done = true;
733 } else {
734 if (wt == leader_wH[i]) {
735 tainted.value()[i] = true;
737 if (uniform(gen()) < 1.0 / leader_tie_count[i]) {
738 standard_array.value()[i] = v;
740 }
741 if (!tainted_burst.value()[i]) {
744 }
745 }
746 }
747
748 size_t j = 0;
749 while (j < wt) {
750 if (digits[j] < q - 1) {
751 ++digits[j];
753 break;
754 }
755 digits[j] = 1;
756 v.set_component(pos[j], T(1));
757 ++j;
758 }
759 if (j == wt) break;
760 }
761 } while (std::prev_permutation(mask.begin(), mask.end()));
762
763 if (done) return;
764 }
765 });
766
767 return standard_array.value();
768 }
769
770 const std::vector<bool>& get_tainted() const
772 {
773 return tainted.value();
774 }
775
778 {
779 Meggitt_table.call_once([this] {
780 if (Meggitt_table.has_value()) return;
781
782 if (k == 0) throw std::invalid_argument("Meggitt table only available for codes with k>0!");
783 if (!is_cyclic()) throw std::logic_error("Meggitt table only available for cyclic codes!");
784
785 std::clog << "--> Calculating Meggitt table" << std::endl;
786
787 constexpr size_t q = T::get_size();
788 const size_t n = this->n;
789 const size_t nk = n - k;
790 const size_t t = get_tmax();
791 const auto& gamma = get_gamma();
792
793 try {
795 auto& table = Meggitt_table.value();
796
797 for (size_t wt = 1; wt <= t; ++wt) {
798 std::vector<bool> mask(n, false);
799 std::fill(mask.begin(), mask.begin() + wt, true);
800
801 do {
803 pos.reserve(wt);
804 for (size_t j = 0; j < n; ++j)
805 if (mask[j]) pos.push_back(j);
806
807 Vector<T> v(n, T(0));
808 for (size_t j = 0; j < wt; ++j) v.set_component(pos[j], T(1));
809
810 std::vector<size_t> digits(wt, 1);
811
812 for (;;) {
814
815 size_t j = 0;
816 while (j < wt) {
817 if (digits[j] < q - 1) {
818 ++digits[j];
820 break;
821 }
822 digits[j] = 1;
823 v.set_component(pos[j], T(1));
824 ++j;
825 }
826 if (j == wt) break;
827 }
828
829 } while (std::prev_permutation(mask.begin() + 1, mask.end()));
830 }
831 } catch (const std::bad_alloc& e) {
833 std::cerr << "Memory allocation failed, Meggitt table too large!" << std::endl;
834 throw;
835 }
836 });
837
838 return Meggitt_table.value();
839 }
840
841 auto cbegin() const noexcept
843 {
844 if (k == 0)
845 return CodewordIterator<T>(*this, get_size());
846 else
847 return CodewordIterator<T>(*this, 0);
848 }
849
850 auto cend() const noexcept
852 {
853 return CodewordIterator<T>(*this, get_size());
854 }
855
856 /**
857 * @brief Test whether two linear codes are identical
858 *
859 * @tparam S Field type of the other code (must equal T for a meaningful comparison)
860 * @param other Linear code to compare against
861 * @param L_ptr If non-null, receives the invertible k × k matrix L such that G' = L · G
862 * @return True if both codes span the same codebook, false otherwise
863 *
864 * Two [n, k] linear codes are identical if they have the same codebook, which
865 * is the case if and only if their generator matrices have the same RREF.
866 * When n − k < k, the comparison is performed on the parity-check matrices
867 * for efficiency.
868 */
869 template <FieldType S>
870 bool is_identical(const LinearCode<S>& other, Matrix<T>* L_ptr = nullptr) const {
871 if constexpr (!std::is_same_v<T, S>) {
872 return false;
873 } else {
874 if (this->n != other.n || k != other.k) return false;
875 if (this == &other) {
876 if (L_ptr != nullptr) *L_ptr = IdentityMatrix<T>(k);
877 return true;
878 }
879
880 bool res;
881 if (this->n - k < other.k)
883 else
884 res = rref(G) == rref(other.G);
885
886 if (res && L_ptr != nullptr) {
887 auto Bp = transpose(Matrix<T>(other.get_G().get_col(infoset[0])));
888 for (size_t t = 1; t < k; ++t)
890 *L_ptr = Bp * MI;
891 }
892
893 return res;
894 }
895 }
896
897 /**
898 * @brief Test whether two linear codes are equivalent
899 *
900 * @tparam S Field type of the other code (must equal T for a meaningful comparison)
901 * @param other Linear code to compare against
902 * @param L_ptr If non-null, receives the invertible k × k matrix L such that G' = L · G · P
903 * @param P_ptr If non-null, receives the n × n permutation matrix P such that G' = L · G · P
904 * @return True if the codes are equivalent, false otherwise
905 *
906 * Two [n, k] linear codes are equivalent if their generator matrices are related
907 * by G' = L · G · P for some invertible matrix L and permutation matrix P.
908 * When n − k < k, the search is performed on the dual codes for efficiency.
909 *
910 * @note The search has combinatorial complexity and may be slow for large codes
911 */
912 template <FieldType S>
913 bool is_equivalent(const LinearCode<S>& other, Matrix<T>* L_ptr = nullptr, Matrix<T>* P_ptr = nullptr) const {
914 if constexpr (!std::is_same_v<T, S>) {
915 return false;
916 } else {
917 if (this->n != other.n || k != other.k) return false;
918
919 if (this == &other) {
920 if (L_ptr != nullptr) *L_ptr = IdentityMatrix<T>(k);
921 if (P_ptr != nullptr) *P_ptr = IdentityMatrix<T>(this->n);
922 return true;
923 }
924
925 if (this->n - k < k) {
926 auto Cperp = this->get_dual();
927 auto Cotherperp = other.get_dual();
928
929 Matrix<T> Pperp;
930 Matrix<T>* Pperp_ptr = (P_ptr != nullptr || L_ptr != nullptr) ? &Pperp : nullptr;
931
932 // L on the dual side is not useful here, so don't request it.
933 if (!Cperp.is_equivalent(Cotherperp, nullptr, Pperp_ptr)) return false;
934
935 if (P_ptr != nullptr) *P_ptr = Pperp;
936
937 if (L_ptr != nullptr) {
938 std::vector<size_t> p(this->n, this->n);
939 for (size_t i = 0; i < this->n; ++i) {
940 for (size_t j = 0; j < this->n; ++j) {
941 if (Pperp(i, j) == T(1)) {
942 p[i] = j;
943 break;
944 }
945 }
946 }
947
948 auto Bp = transpose(Matrix<T>(other.get_G().get_col(p[this->infoset[0]])));
949 for (size_t t = 1; t < this->k; ++t)
951 *L_ptr = Bp * this->MI;
952 }
953
954 return true;
955 } else {
956 auto next_combination = [](std::vector<size_t>& comb, size_t n_total) -> bool {
957 const size_t kk = comb.size();
958 for (size_t i = kk; i-- > 0;) {
959 if (comb[i] < n_total - kk + i) {
960 ++comb[i];
961 for (size_t j = i + 1; j < kk; ++j) comb[j] = comb[j - 1] + 1;
962 return true;
963 }
964 }
965 return false;
966 };
967
969 Gp_cols.reserve(this->n);
970 for (size_t j = 0; j < this->n; ++j) Gp_cols.push_back(other.get_G().get_col(j));
971
974 for (size_t j = 0; j < this->n; ++j) target_col_keys.push_back(Gp_cols[j].as_integer());
976
977 std::vector<size_t> J(k);
978 std::iota(J.begin(), J.end(), 0);
979
980 do {
981 auto Bp_sorted = transpose(Matrix<T>(Gp_cols[J[0]]));
982 for (size_t t = 1; t < k; ++t) Bp_sorted.horizontal_join(transpose(Matrix<T>(Gp_cols[J[t]])));
983
984 if (Bp_sorted.rank() != k) continue;
985
986 std::vector<size_t> Jperm = J;
987 do {
988 auto Bp = transpose(Matrix<T>(Gp_cols[Jperm[0]]));
989 for (size_t t = 1; t < k; ++t) Bp.horizontal_join(transpose(Matrix<T>(Gp_cols[Jperm[t]])));
990
991 const auto L = Bp * MI;
992 const auto Lt = transpose(L); // buffered once per candidate
993
996
997 for (size_t col = 0; col < this->n; ++col) {
998 auto h = Vector<T>(G.get_col(col) * Lt);
1000 }
1002
1004 // Fast path: caller only wants yes/no
1005 if (L_ptr == nullptr && P_ptr == nullptr) return true;
1006
1007 if (L_ptr != nullptr) *L_ptr = L;
1008
1009 if (P_ptr != nullptr) {
1011 transformed_cols.reserve(this->n);
1012 for (size_t col = 0; col < this->n; ++col)
1014
1015 std::vector<size_t> perm(this->n, this->n);
1016 std::vector<bool> used(this->n, false);
1017
1018 bool match_ok = true;
1019 for (size_t i = 0; i < this->n && match_ok; ++i) {
1020 bool found = false;
1021 for (size_t j = 0; j < this->n; ++j) {
1022 if (used[j]) continue;
1023 if (transformed_cols[i] == Gp_cols[j]) {
1024 perm[i] = j;
1025 used[j] = true;
1026 found = true;
1027 break;
1028 }
1029 }
1030 if (!found) match_ok = false;
1031 }
1032
1033 if (!match_ok) {
1034 // If matching fails, continue searching (e.g. due to key collisions)
1035 continue;
1036 }
1037
1039 }
1040
1041 return true;
1042 }
1043
1044 } while (std::next_permutation(Jperm.begin(), Jperm.end()));
1045
1046 } while (next_combination(J, this->n));
1047
1048 return false;
1049 }
1050 }
1051 }
1052
1053 bool is_perfect() const
1055 {
1056 if (k == 0) return false;
1057 return std::fabs(HammingUpperBound<T>(this->n, get_dmin()) - k) <
1058 10 * std::numeric_limits<long double>::epsilon();
1059 }
1060
1061 bool is_MDS() const {
1062 if (k == 0) return false;
1063 return SingletonUpperBound(this->n, get_dmin()) == k;
1064 }
1065
1066 bool is_equidistant() const
1068 {
1069 if (k == 0) return true;
1070 return std::fabs(PlotkinUpperBound<T>(this->n, get_dmin()) - k) <
1071 10 * std::numeric_limits<long double>::epsilon();
1072 }
1073
1074 bool is_weakly_self_dual() const { return G * transpose(G) == ZeroMatrix<T>(k, k); }
1075 bool is_dual_containing() const { return transpose(HT) * HT == ZeroMatrix<T>(this->n - k, this->n - k); }
1076 bool is_self_dual() const { return 2 * k == this->n && is_weakly_self_dual() && is_dual_containing(); }
1077
1078 bool is_polynomial() const {
1079 polynomial.call_once([this] {
1080 if (polynomial.has_value()) return;
1081
1082 if (k == 0) {
1083 polynomial.emplace(true);
1084 auto g = ZeroPolynomial<T>();
1085 g.set_coefficient(0, -T(1));
1086 g.set_coefficient(this->n, T(1));
1087 gamma.emplace(g);
1088 return;
1089 }
1090
1091 auto g = ZeroPolynomial<T>();
1092 for (size_t i = 0; i < k; ++i) {
1093 g = GCD(g, Polynomial<T>(G.get_row(i)));
1094 if (g.degree() < this->n - k && !g.is_zero()) {
1095 polynomial.emplace(false);
1096 return;
1097 }
1098 }
1099 if (g.trailing_degree() > 0) {
1100 polynomial.emplace(false);
1101 return;
1102 }
1103 g = normalize(g); // degree at this point can only be n-k
1104 polynomial.emplace(true);
1105 gamma.emplace(g);
1106 });
1107 return polynomial.value();
1108 }
1109
1110 bool is_cyclic() const {
1111 if (!is_polynomial()) return false;
1112 if (k == 0) return true;
1113
1114 auto p = ZeroPolynomial<T>();
1115 p.set_coefficient(0, -T(1));
1116 p.set_coefficient(gamma.value().degree() + k, T(1));
1117 p %= gamma.value();
1118 if (p.is_zero())
1119 return true;
1120 else
1121 return false;
1122 }
1123
1124 virtual void get_info(std::ostream& os) const override {
1125 if (os.iword(details::index) != 3) {
1126 if (os.iword(details::index) > 0) Code<T>::get_info(os);
1127 if constexpr (FiniteFieldType<T>) {
1128 constexpr size_t q = T::get_size();
1129 os << "[F_" << q << "; " << this->n << ", " << k << "]";
1130 if (os.iword(details::index) > 1) {
1132 os << ", dmin = ";
1133 try {
1134 os << get_dmin();
1135 } catch (const std::logic_error& e) {
1136 os << "undefined";
1137 }
1138 }
1139 } else {
1140 os << "[Q; " << this->n << ", " << k << "]";
1141 if (os.iword(details::index) > 1) {
1142 os << ", dmin = ";
1143 try {
1144 os << get_dmin();
1145 } catch (const std::logic_error& e) {
1146 os << "undefined";
1147 }
1148 }
1149 }
1150 }
1151 if (os.iword(details::index) > 0) {
1152 if (os.iword(details::index) != 3) os << std::endl;
1153 os << BOLD("Linear code") " with properties: { ";
1154 if (os.iword(details::index) != 3) {
1155 os << std::endl;
1156 os << "G = " << std::endl;
1157 os << G << std::endl;
1158 os << "H = " << std::endl;
1159 os << get_H();
1160 os << std::endl;
1161 }
1162 }
1163 if (os.iword(details::index) > 1 && os.iword(details::index) < 3) {
1164 if constexpr (FiniteFieldType<T>) os << "A(x) = " << get_weight_enumerator() << std::setfill(' ') << " ";
1165 os << "tmax = ";
1166 try {
1167 os << get_tmax() << " ";
1168 } catch (const std::logic_error& e) {
1169 os << "undefined ";
1170 }
1171 }
1172
1173 if (os.iword(details::index) > 1) {
1174 if (is_polynomial()) {
1175 os << "polynomial(";
1176 if (is_cyclic()) os << "cyclic, ";
1177 os << "gamma = " << get_gamma() << ") " << std::flush;
1178 }
1179 if constexpr (FiniteFieldType<T>)
1180 if (is_perfect()) os << "perfect " << std::flush;
1181 if (is_MDS()) os << "MDS " << std::flush;
1182 if constexpr (FiniteFieldType<T>)
1183 if (is_equidistant()) os << "equidistant " << std::flush;
1184 }
1185 if (os.iword(details::index) > 0) {
1186 if (!is_self_dual() && is_weakly_self_dual()) os << "weakly_self-dual " << std::flush;
1187 if (!is_self_dual() && is_dual_containing()) os << "dual-containing " << std::flush;
1188 if (is_self_dual()) os << "self-dual " << std::flush;
1189 if (os.iword(details::index) != 3) os << std::endl;
1190 os << "}" << std::flush;
1191 }
1192 }
1193
1195 auto dual_code = LinearCode<T>(this->n, this->n - k, get_H());
1196 if constexpr (FiniteFieldType<T>) {
1199 }
1200 return dual_code;
1201 }
1202
1204 auto Gp = G;
1205 Gp.rref();
1206 for (size_t i = 0; i < k; ++i) {
1207 const auto u = unit_vector<T>(k, i);
1208 for (size_t j = 0; j < this->n; ++j) {
1209 if (Gp.get_col(j) == u) {
1210 if (j != i) Gp.swap_columns(i, j);
1211 break;
1212 }
1213 }
1214 }
1215 LinearCode<T> res(this->n, k, Gp);
1216 if (dmin.has_value()) res.set_dmin(*dmin);
1217 if constexpr (FiniteFieldType<T>)
1219 return res;
1220 }
1221
1233
1235 if (!is_polynomial()) throw std::invalid_argument("Code is not polynomial, cannot bring G in polynomial form!");
1236
1237 const auto& g = get_gamma();
1238 auto Gp = ToeplitzMatrix(pad_back(pad_front(Vector<T>(g), this->n), this->n + k - 1), k, this->n);
1239 return Gp;
1240 }
1241
1243 auto Gp = G;
1244 for (;;) {
1245 // find start and end indices
1247 std::vector<size_t> ends(k);
1248 for (size_t i = 0; i < k; ++i) {
1249 for (size_t j = 0; j < this->n; ++j) {
1250 if (Gp(i, j) != T(0)) {
1251 starts[i] = j;
1252 break;
1253 }
1254 }
1255 for (size_t j = this->n; j-- > 0;) {
1256 if (Gp(i, j) != T(0)) {
1257 ends[i] = j;
1258 break;
1259 }
1260 }
1261 }
1262
1263 // break if in trellis-oriented form
1266
1267 if (start_set.size() == k && end_set.size() == k) break;
1268
1269 bool found_start = false;
1270 for (size_t i = 0; i < k; ++i) {
1271 for (size_t j = i + 1; j < k; ++j) {
1272 if (starts[i] == starts[j]) {
1273 const size_t s = starts[i];
1274 Gp.scale_row(T(1) / Gp(i, s), i);
1275 Gp.scale_row(T(1) / Gp(j, s), j);
1276 if (ends[i] < ends[j])
1277 Gp.add_scaled_row(-T(1), i, j);
1278 else
1279 Gp.add_scaled_row(-T(1), j, i);
1280 found_start = true;
1281 break;
1282 }
1283 }
1284 if (found_start) break;
1285 }
1286
1287 if (!found_start) {
1288 bool found_end = false;
1289 for (size_t i = 0; i < k; ++i) {
1290 for (size_t j = i + 1; j < k; ++j) {
1291 if (ends[i] == ends[j]) {
1292 const size_t e = ends[i];
1293 Gp.scale_row(T(1) / Gp(i, e), i);
1294 Gp.scale_row(T(1) / Gp(j, e), j);
1295 if (starts[i] > starts[j])
1296 Gp.add_scaled_row(-T(1), i, j);
1297 else
1298 Gp.add_scaled_row(-T(1), j, i);
1299 found_end = true;
1300 break;
1301 }
1302 }
1303 if (found_end) break;
1304 }
1305 }
1306 }
1307
1308 return Gp;
1309 }
1310
1313 {
1314 const size_t n = this->n;
1315
1316 Trellis<T> res;
1317 size_t i = 0;
1318 for (auto it = cbegin(); it != cend(); ++it) {
1319 res.add_edge(0, 0, i, (*it)[0]);
1320 for (size_t j = 1; j < n - 1; ++j) {
1321 res.add_edge(j, i, i, (*it)[j]);
1322 }
1323 res.add_edge(n - 1, i, 0, (*it)[n - 1]);
1324 ++i;
1325 }
1326
1327 return res;
1328 }
1329
1332 {
1333 minimal_trellis.call_once([this] {
1334 if (minimal_trellis.has_value()) return;
1335 const size_t q = T::get_size();
1336 const size_t n = this->n;
1337 const auto Gp = get_G_in_trellis_oriented_form();
1338
1339 std::clog << "--> Calculating minimal trellis for code with " << sqm<InfInt>(q, k) << " codewords"
1340 << std::endl;
1341
1342 auto row_trellis = [n, &Gp](size_t i) {
1343 size_t s = 0, e = 0;
1344
1345 for (size_t j = 0; j < n; ++j) {
1346 if (Gp(i, j) != T(0)) {
1347 s = j;
1348 break;
1349 }
1350 }
1351 for (size_t j = n; j-- > 0;) {
1352 if (Gp(i, j) != T(0)) {
1353 e = j;
1354 break;
1355 }
1356 }
1357
1358 const bool span_one = (s == e);
1359 const bool open_ended = span_one && (e == n - 1);
1360
1361 Trellis<T> tr;
1362
1363 for (size_t j = 0; j < n; ++j) {
1364 if (j < s || (!open_ended && j > (span_one ? s + 1 : e)) || (open_ended && j > e)) {
1365 tr.add_edge(j, 0, 0, T(0));
1366 } else if (j == s) {
1367 for (uint32_t a = 0; a < q; ++a) tr.add_edge(j, 0, a, T(a) * Gp(i, s));
1368 } else if (!open_ended && j == (span_one ? s + 1 : e)) {
1369 if (span_one) {
1370 for (uint32_t a = 0; a < q; ++a) tr.add_edge(j, a, 0, T(0));
1371 } else {
1372 for (uint32_t a = 0; a < q; ++a) tr.add_edge(j, a, 0, T(a) * Gp(i, j));
1373 }
1374 } else {
1375 for (uint32_t a = 0; a < q; ++a) tr.add_edge(j, a, a, T(a) * Gp(i, j));
1376 }
1377 }
1378
1379 return tr;
1380 };
1381
1382 auto result = row_trellis(0);
1383 for (size_t i = 1; i < k; ++i) result = result * row_trellis(i);
1384
1386 });
1387 return *minimal_trellis;
1388 }
1389
1390 Vector<T> enc(const Vector<T>& u) const override { return u * G; }
1391
1392 Vector<T> encinv(const Vector<T>& c) const override {
1393 if (k == 0) throw std::logic_error("Cannot invert encoding wrt. a dimension zero code!");
1394
1395 Vector<T> c_sub(k);
1396 size_t i = 0;
1397 for (auto it = infoset.cbegin(); it != infoset.cend(); ++it) {
1398 c_sub.set_component(i, c[*it]);
1399 ++i;
1400 }
1401 return c_sub * MI;
1402 }
1403
1404 virtual Vector<T> dec_BD(const Vector<T>& r) const override {
1405 if constexpr (!FiniteFieldType<T>) {
1406 throw std::logic_error("BD decoding only available for codes over finite fields!");
1407 } else {
1408#ifdef CECCO_ERASURE_SUPPORT
1409 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
1410#endif
1412
1413 if (k == 0) return Vector<T>(this->n);
1414
1415 const auto c_est = LinearCode<T>::dec_ML(r);
1416 if (dH(r, c_est) > this->get_tmax()) throw decoding_failure("Linear code BD decoder failed!");
1417 return c_est;
1418 }
1419 }
1420
1421 virtual Vector<T> dec_boosted_BD(const Vector<T>& r) const override {
1422 if constexpr (!FiniteFieldType<T>) {
1423 throw std::logic_error("BD decoding only available for codes over finite fields!");
1424 } else {
1425#ifdef CECCO_ERASURE_SUPPORT
1426 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
1427#endif
1429
1430 if (k == 0) return Vector<T>(this->n);
1431
1433 const auto s = r * HT; // calculate syndrome...
1434 if (s.is_zero()) return r;
1435 const size_t i = s.as_integer(); // ... and interpret it as q-ary number
1436 if (tainted.value()[i]) throw decoding_failure("Linear code boosted BD decoder failed!");
1437 return r - standard_array.value()[i];
1438 }
1439 }
1440
1441 virtual Vector<T> dec_ML(const Vector<T>& r) const override {
1442 if constexpr (!FiniteFieldType<T>) {
1443 throw std::logic_error("ML decoding only available for codes over finite fields!");
1444 } else {
1445#ifdef CECCO_ERASURE_SUPPORT
1446 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
1447#endif
1449
1450 if (k == 0) return Vector<T>(this->n);
1451
1453 const auto s = r * HT; // calculate syndrome...
1454 if (s.is_zero()) return r;
1455 const size_t i = s.as_integer(); // ... and interpret it as q-ary number
1456 return r - standard_array.value()[i];
1457 }
1458 }
1459
1460 virtual Vector<T> dec_burst(const Vector<T>& r) const override {
1461 if constexpr (!FiniteFieldType<T>) {
1462 throw std::logic_error("Burst decoding only available for codes over finite fields!");
1463 } else {
1464#ifdef CECCO_ERASURE_SUPPORT
1466 throw std::invalid_argument("Trying to correct erasures with a burst decoder!");
1467#endif
1469
1470 if (k == 0) return Vector<T>(this->n);
1471
1473 const auto s = r * HT; // calculate syndrome...
1474 if (s.is_zero()) return r;
1475 const size_t i = s.as_integer(); // ... and interpret it as q-ary number
1476 if (tainted_burst.value()[i]) { // decoding failure
1477 throw decoding_failure(
1478 "Linear code burst error decoder failed, coset of syndrome empty or tainted (ambiguous leader)!");
1479 } else { // correct decoding or decoding error
1480 return r - standard_array.value()[i];
1481 }
1482 }
1483 }
1484
1485 virtual Vector<T> dec_Meggitt(const Vector<T>& r) const override {
1486 if constexpr (!FiniteFieldType<T>) {
1487 throw std::logic_error("Meggitt BD decoding only available for codes over finite fields!");
1488 } else {
1489#ifdef CECCO_ERASURE_SUPPORT
1491 throw std::invalid_argument("Trying to correct erasures with a Meggitt BD decoder!");
1492#endif
1494
1495 if (k == 0) return Vector<T>(this->n);
1496
1497 const size_t n = this->n;
1498 const size_t redundancy = n - k;
1499 const auto& gamma = get_gamma();
1500 const T lead_inv = T(1) / gamma[redundancy];
1501 const auto& table = get_Meggitt_table();
1502
1503 // Initial polynomial syndrome: s = r(x) mod gamma(x)
1504 // x * r(x) mod (x^n - 1) = rotate_right(r, 1), so each LFSR step
1505 // advances the syndrome to match the next right-cyclic shift of r.
1507
1508 if (s.is_zero()) return r;
1509
1510 for (size_t i = 0; i < n; ++i) {
1511 const auto it = table.find(s.as_integer());
1512 if (it != table.end()) return r - rotate_left(it->second, i);
1513 // LFSR: s <- x * s mod gamma
1514 const T feedback = s[redundancy - 1] * lead_inv;
1515 for (size_t j = redundancy - 1; j > 0; --j) s.set_component(j, s[j - 1] - feedback * gamma[j]);
1516 s.set_component(0, -feedback * gamma[0]);
1517 }
1518
1519 throw decoding_failure("Meggitt BD decoder failed!");
1520 }
1521 }
1522
1523 virtual Vector<T> dec_Viterbi(const Vector<T>& r, const std::string& filename = "") const override {
1524 if constexpr (!FiniteFieldType<T>) {
1525 throw std::logic_error("Viterbi decoding only available for codes over finite fields!");
1526 } else {
1527#ifdef CECCO_ERASURE_SUPPORT
1529#endif
1531 if (!filename.empty() && T::get_size() > 64)
1532 throw std::invalid_argument("Viterbi trellis TikZ export not supported for fields with size > 64!");
1533 if (k == 0) return Vector<T>(this->n);
1534
1535 const auto& Tr = get_minimal_trellis();
1536 typename Trellis<T>::template Viterbi_Workspace<uint16_t> ws(Tr);
1538 if (!filename.empty()) ws.v.emplace(r);
1540 return c_est;
1541 }
1542 }
1543
1544 virtual Vector<T> dec_Viterbi_soft(const Vector<double>& llrs, const std::string& filename = "") const override {
1545 if constexpr (!std::is_same_v<T, Fp<2>>) {
1546 throw std::logic_error("Soft-input Viterbi decoding only available for codes over F_2!");
1547 } else {
1549 if (k == 0) return Vector<T>(this->n);
1550
1551 const auto& Tr = get_minimal_trellis();
1552 typename Trellis<T>::template Viterbi_Workspace<double> ws(Tr);
1554 if (!filename.empty()) ws.v.emplace(llrs);
1556 return c_est;
1557 }
1558 }
1559
1560 virtual Vector<double> dec_BCJR(const Vector<double>& llrs, const std::string& filename = "") const override {
1561 if constexpr (!std::is_same_v<T, Fp<2>>) {
1562 throw std::logic_error("BCJR decoding only available for codes over F_2!");
1563 } else {
1565 if (k == 0) return Vector<double>(this->n, 0.0);
1566
1567 const auto& Tr = get_minimal_trellis();
1568 typename Trellis<T>::BCJR_Workspace ws(Tr);
1571 if (!filename.empty()) {
1572 ws.v.emplace(llrs);
1574 }
1575 return result;
1576 }
1577 }
1578
1579 virtual Vector<T> dec_ML_soft(const Vector<double>& llrs, size_t cache_limit) const override {
1580 if constexpr (!std::is_same_v<T, Fp<2>>) {
1581 throw std::logic_error("Soft-input ML decoding only available for codes over F_2!");
1582 } else {
1584
1585 if (k == 0) return Vector<T>(this->n);
1586
1587 Vector<T> c_est;
1588 double best = std::numeric_limits<double>::max();
1589
1590 if (this->get_size() <= cache_limit) {
1591 codewords.call_once([this] {
1592 if (codewords.has_value()) return;
1593 codewords.emplace(this->get_G().span());
1594 });
1595
1596 for (auto it = codewords.value().cbegin(); it != codewords.value().cend(); ++it) {
1597 double val = 0.0;
1598 for (size_t i = 0; i < this->n; ++i) {
1599 if ((*it)[i] != T(0)) val += llrs[i];
1600 }
1601 if (val < best) {
1602 c_est = *it;
1603 best = val;
1604 }
1605 }
1606 } else {
1607 for (auto it = cbegin(); it != cend(); ++it) {
1608 double val = 0.0;
1609 for (size_t i = 0; i < this->n; ++i) {
1610 if ((*it)[i] != T(0)) val += llrs[i];
1611 }
1612 if (val < best) {
1613 c_est = *it;
1614 best = val;
1615 }
1616 }
1617 }
1618
1619 return c_est;
1620 }
1621 }
1622
1623#ifdef CECCO_ERASURE_SUPPORT
1624 virtual Vector<T> dec_BD_EE(const Vector<T>& r) const override {
1625 if constexpr (!FiniteFieldType<T>) {
1626 throw std::logic_error("BD error/erasure decoding only available for codes over finite fields!");
1627 } else {
1629
1630 if (k == 0) return Vector<T>(this->n);
1631
1632 std::vector<size_t> X;
1633 std::vector<size_t> E;
1634 for (size_t i = 0; i < this->n; ++i) {
1635 if (r[i].is_erased())
1636 X.push_back(i);
1637 else
1638 E.push_back(i);
1639 }
1640 const size_t tau = X.size();
1641
1642 if (tau == 0) return dec_BD(r);
1643 if (tau > get_dmin() - 1) {
1644 throw decoding_failure("Linear code BD error/erasure decoder failed!");
1645 }
1646
1648
1649 const auto& PC = punctured_codes_BD.value()[pos_to_index(X)].value();
1650
1651 const auto r_E = delete_components(r, X);
1652 const auto c_E = PC.dec_ML(r_E);
1653 const auto HT_E = delete_rows(this->HT, X);
1654 const auto b = -c_E * HT_E;
1655 const auto HT_X = delete_rows(this->HT, E);
1657
1658 Vector<T> sol(tau);
1659 bool found = false;
1660 for (size_t i = 0; i < B.get_m(); ++i) {
1661 const T a = B(i, B.get_n() - 1);
1662 if (!a.is_zero()) {
1663 sol = B.get_row(i).delete_component(B.get_n() - 1);
1664 sol /= -a;
1665 found = true;
1666 break;
1667 }
1668 }
1669 if (!found) {
1670 throw decoding_failure("Linear code BD error/erasure decoder failed!");
1671 }
1672
1673 Vector<T> c_est(this->n);
1674 for (size_t i = 0; i < E.size(); ++i) c_est.set_component(E[i], c_E[i]);
1675 for (size_t j = 0; j < tau; ++j) c_est.set_component(X[j], sol[j]);
1676
1677 size_t t = 0;
1678 for (size_t i = 0; i < this->n; ++i) {
1679 if (!r[i].is_erased() && r[i] != c_est[i]) ++t;
1680 }
1681
1682 if (2 * t + tau > get_dmin() - 1) {
1683 throw decoding_failure("Linear code BD error/erasure decoder failed!");
1684 }
1685
1686 return c_est;
1687 }
1688 }
1689
1690 virtual Vector<T> dec_Viterbi_EE(const Vector<T>& r, const std::string& filename = "") const override {
1691 if constexpr (!FiniteFieldType<T>) {
1692 throw std::logic_error("Viterbi error/erasure decoding only available for codes over finite fields!");
1693 } else {
1695 if (!filename.empty() && T::get_size() > 64)
1696 throw std::invalid_argument("Viterbi trellis TikZ export not supported for fields with size > 64!");
1697 if (k == 0) return Vector<T>(this->n);
1698
1699 const auto& Tr = get_minimal_trellis();
1700 typename Trellis<T>::template Viterbi_Workspace<uint16_t> ws(Tr);
1702 if (!filename.empty()) ws.v.emplace(r);
1704 return c_est;
1705 }
1706 }
1707
1708 virtual Vector<T> dec_ML_EE(const Vector<T>& r) const override {
1709 if constexpr (!FiniteFieldType<T>) {
1710 throw std::logic_error("ML error/erasure decoding only available for codes over finite fields!");
1711 } else {
1713
1714 if (k == 0) return Vector<T>(this->n);
1715
1716 std::vector<size_t> X;
1717 std::vector<size_t> E;
1718 for (size_t i = 0; i < this->n; ++i) {
1719 if (r[i].is_erased())
1720 X.push_back(i);
1721 else
1722 E.push_back(i);
1723 }
1724 const size_t tau = X.size();
1725
1726 if (tau == 0) return dec_ML(r);
1727 if (tau == this->n) return Vector<T>(this->n);
1728
1729 const LinearCode<T>* pc_ptr;
1730
1731 if (tau <= get_dmin() - 1) {
1734 } else {
1736 size_t bd_count = 0;
1737 for (size_t t = 1; t <= get_dmin() - 1; ++t) bd_count += bin<size_t>(this->n, t);
1739 }
1740
1741 const auto& PC = *pc_ptr;
1742
1743 const auto r_E = delete_components(r, X);
1744 const auto c_E = PC.dec_ML(r_E);
1745 const auto HT_E = delete_rows(this->HT, X);
1746 const auto b = -c_E * HT_E;
1747 const auto HT_X = delete_rows(this->HT, E);
1749
1750 Vector<T> sol(tau);
1751 bool found = false;
1752 for (size_t i = 0; i < B.get_m(); ++i) {
1753 const T a = B(i, B.get_n() - 1);
1754 if (!a.is_zero()) {
1755 sol = B.get_row(i).delete_component(B.get_n() - 1);
1756 sol /= -a;
1757 found = true;
1758 break;
1759 }
1760 }
1761 if (!found) throw decoding_failure("Linear code ML error/erasure decoder failed!");
1762
1763 Vector<T> c_est(this->n);
1764 for (size_t i = 0; i < E.size(); ++i) c_est.set_component(E[i], c_E[i]);
1765 for (size_t j = 0; j < tau; ++j) c_est.set_component(X[j], sol[j]);
1766
1767 return c_est;
1768 }
1769 }
1770
1771 Vector<T> dec_GMD(const Vector<T>& r, const std::vector<double>& reliability) const
1773 {
1774 const size_t n = this->n;
1775
1776 if (reliability.size() != n)
1777 throw std::invalid_argument("GMD decoder: length of reliability vector must match code length");
1778
1779 std::vector<size_t> order(n);
1780 std::iota(order.begin(), order.end(), 0);
1781 std::shuffle(order.begin(), order.end(), gen());
1783 [&](size_t a, size_t b) { return reliability[a] < reliability[b]; });
1784
1786 double best_score = std::numeric_limits<double>::infinity();
1787
1788 const size_t d = get_dmin();
1789
1790 auto rp = r;
1791 const auto trial = [&]() {
1792 try {
1793 const auto c_est = dec_BD_EE(rp);
1794 double score = 0.0;
1795 for (size_t i = 0; i < n; ++i)
1796 if (c_est[i] != r[i]) score += reliability[i];
1797 if (score < best_score) {
1798 best_score = score;
1799 best = c_est;
1800 }
1801 } catch (const decoding_failure&) {
1802 }
1803 };
1804
1805 trial();
1806 for (size_t tau = 2; tau < d; tau += 2) {
1807 rp.erase_components({order[tau - 2], order[tau - 1]});
1808 trial();
1809 if (best_score == 0.0) break;
1810 }
1811
1812 if (!best.has_value()) throw decoding_failure("GMD decoder failed (all trials failed)!");
1813 return std::move(*best);
1814 }
1815#endif
1816
1817 protected:
1818#ifdef CECCO_ERASURE_SUPPORT
1819 static bool erasures_present(const Vector<T>& r) {
1820 for (size_t i = 0; i < r.get_n(); ++i) {
1821 if (r[i].is_erased()) return true;
1822 }
1823 return false;
1824 }
1825#endif
1826
1827 template <ComponentType S>
1828 void validate_length(const Vector<S>& r) const {
1829 if (r.get_n() != this->n)
1830 throw std::invalid_argument(std::string("Received vector length must be ") + std::to_string(this->n));
1831 }
1832
1845#ifdef CECCO_ERASURE_SUPPORT
1848#endif
1852
1853 private:
1854 template <typename cost_t>
1855 Vector<T> viterbi_forward_pass_and_traceback(const Trellis<T>& Tr,
1856 typename Trellis<T>::template Viterbi_Workspace<cost_t>& ws,
1857 const std::string& filename = "") const {
1858 std::uniform_real_distribution<double> uniform(0.0, 1.0);
1859
1860 const size_t n = this->n;
1861 using ws_t = typename Trellis<T>::template Viterbi_Workspace<cost_t>;
1862
1863 std::ofstream file;
1864 if constexpr (T::get_size() <= 64) {
1865 if (!filename.empty()) {
1866 file.open(filename);
1867 Tr.template tikz_header<ws_t>(file);
1868 }
1869 }
1870
1871 std::fill(ws.path_costs_prev.begin(), ws.path_costs_prev.end(), ws_t::init);
1872 ws.path_costs_prev[0] = cost_t{0};
1873
1874 for (size_t s = 0; s < n; ++s) {
1875 std::fill(ws.path_costs_curr.begin(), ws.path_costs_curr.end(), ws_t::init);
1876 std::fill(ws.tie_counts.begin(), ws.tie_counts.end(), 0);
1877 for (size_t j = 0; j < Tr.E[s].size(); ++j) {
1878 const auto& e = Tr.E[s][j];
1879 const cost_t cost = ws.path_costs_prev[e.from_id] + ws.edge_costs[s][j];
1880 if (cost < ws.path_costs_curr[e.to_id]) {
1881 ws.path_costs_curr[e.to_id] = cost;
1882 ws.backptrs[s + 1][e.to_id] = &e;
1883 ws.tie_counts[e.to_id] = 1;
1884 } else if (cost == ws.path_costs_curr[e.to_id]) {
1885 ++ws.tie_counts[e.to_id];
1886 if (uniform(gen()) < 1.0 / ws.tie_counts[e.to_id]) ws.backptrs[s + 1][e.to_id] = &e;
1887 }
1888 }
1889 std::swap(ws.path_costs_prev, ws.path_costs_curr);
1890 if constexpr (T::get_size() <= 64) {
1891 if (file.is_open()) Tr.tikz_picture(file, &ws, s + 1);
1892 }
1893 }
1894
1895 Vector<T> c_est(n);
1896
1897 size_t v = 0;
1898 cost_t best = ws.path_costs_prev[0];
1899 uint16_t sink_ties = 1;
1900 for (size_t u = 1; u < Tr.V[n].size(); ++u) {
1901 const cost_t c = ws.path_costs_prev[u];
1902 if (c < best) {
1903 best = c;
1904 v = u;
1905 sink_ties = 1;
1906 } else if (c == best) {
1907 ++sink_ties;
1908 if (uniform(gen()) < 1.0 / sink_ties) v = u;
1909 }
1910 }
1911
1912 for (size_t s = n; s > 0; --s) {
1913 const auto* e = ws.backptrs[s][v];
1914 c_est.set_component(s - 1, e->value);
1915 v = e->from_id;
1916 }
1917
1918 return c_est;
1919 }
1920
1921 Vector<double> bcjr_forward_backward(const Trellis<T>& Tr, typename Trellis<T>::BCJR_Workspace& ws) const
1922 requires std::is_same_v<T, Fp<2>>
1923 {
1924 constexpr double neg_inf = -std::numeric_limits<double>::infinity();
1925 const size_t n = this->n;
1926
1927 auto max_star = [](double a, double b) noexcept {
1928 if (a == neg_inf) return b;
1929 if (b == neg_inf) return a;
1930 const double d = std::abs(a - b);
1931 return std::max(a, b) + ((d > 500.0) ? 0.0 : std::log(1.0 + std::exp(-d)));
1932 };
1933
1934 ws.alpha[0][0] = 0.0;
1935 for (size_t s = 0; s < n; ++s) {
1936 for (size_t j = 0; j < Tr.E[s].size(); ++j) {
1937 const auto& e = Tr.E[s][j];
1938 const double val = ws.alpha[s][e.from_id] - ws.edge_costs[s][j];
1939 ws.alpha[s + 1][e.to_id] = max_star(ws.alpha[s + 1][e.to_id], val);
1940 }
1941 }
1942
1943 for (size_t u = 0; u < Tr.V[n].size(); ++u) ws.beta[n][u] = 0.0;
1944 for (size_t s = n; s > 0; --s) {
1945 for (size_t j = 0; j < Tr.E[s - 1].size(); ++j) {
1946 const auto& e = Tr.E[s - 1][j];
1947 const double val = ws.beta[s][e.to_id] - ws.edge_costs[s - 1][j];
1948 ws.beta[s - 1][e.from_id] = max_star(ws.beta[s - 1][e.from_id], val);
1949 }
1950 }
1951
1952 Vector<double> res(n);
1953 for (size_t s = 0; s < n; ++s) {
1954 double L0 = neg_inf;
1955 double L1 = neg_inf;
1956 for (size_t j = 0; j < Tr.E[s].size(); ++j) {
1957 const auto& e = Tr.E[s][j];
1958 const double val = ws.alpha[s][e.from_id] - ws.edge_costs[s][j] + ws.beta[s + 1][e.to_id];
1959 if (e.value == T(0))
1960 L0 = max_star(L0, val);
1961 else
1962 L1 = max_star(L1, val);
1963 }
1964 res.set_component(s, L0 - L1);
1965 }
1966
1967 return res;
1968 }
1969
1970#ifdef CECCO_ERASURE_SUPPORT
1971 void init_punctured_codes_BD() const {
1973 if (punctured_codes_BD.has_value()) return;
1974 std::clog << "--> Preparing punctured codes for BD error/erasure decoding" << std::endl;
1975 size_t count = 0;
1976 for (size_t tau = 1; tau <= get_dmin() - 1; ++tau) count += bin<size_t>(this->n, tau);
1978 for (size_t tau = 1; tau <= get_dmin() - 1; ++tau) {
1979 std::vector<bool> mask(this->n, false);
1980 std::fill(mask.begin(), mask.begin() + tau, true);
1981 do {
1982 std::vector<size_t> X;
1983 X.reserve(tau);
1984 for (auto it = mask.cbegin(); it != mask.cend(); ++it)
1985 if (*it) X.push_back(static_cast<size_t>(it - mask.cbegin()));
1987 } while (std::prev_permutation(mask.begin(), mask.end()));
1988 }
1989 });
1990 }
1991
1992 void init_punctured_codes_ML() const {
1994 if (punctured_codes_ML.has_value()) return;
1995 std::clog << "--> Preparing punctured codes for ML error/erasure decoding" << std::endl;
1996 size_t bd_count = 0;
1997 for (size_t t = 1; t <= get_dmin() - 1; ++t) bd_count += bin<size_t>(this->n, t);
1998 size_t count = 0;
1999 for (size_t tau = get_dmin(); tau <= this->n; ++tau) count += bin<size_t>(this->n, tau);
2001 for (size_t tau = get_dmin(); tau <= this->n; ++tau) {
2002 std::vector<bool> mask(this->n, false);
2003 std::fill(mask.begin(), mask.begin() + tau, true);
2004 do {
2005 std::vector<size_t> X;
2006 X.reserve(tau);
2007 for (auto it = mask.cbegin(); it != mask.cend(); ++it)
2008 if (*it) X.push_back(static_cast<size_t>(it - mask.cbegin()));
2010 } while (std::prev_permutation(mask.begin(), mask.end()));
2011 }
2012 });
2013 }
2014
2015 size_t pos_to_index(const std::vector<size_t>& pos) const {
2016 const size_t tau = pos.size();
2017
2018 if (tau == 0) throw std::invalid_argument("Cannot calculate punctured code index from erasure positions!");
2019
2020 size_t offset = 0;
2021 for (size_t t = 1; t < tau; ++t) offset += bin<size_t>(this->n, t);
2022
2023 size_t rank = 0;
2024 for (size_t i = 0; i < tau; ++i) {
2025 const size_t start = (i == 0) ? 0 : (pos[i - 1] + 1);
2026 for (size_t x = start; x < pos[i]; ++x) rank += bin<size_t>(this->n - 1 - x, tau - 1 - i);
2027 }
2028 return offset + rank;
2029 }
2030#endif
2031
2032 InfInt N(size_t ell, size_t h, size_t s) const noexcept {
2033 const size_t n = this->n;
2034
2035 if constexpr (std::is_same_v<T, Fp<2>>) {
2036 InfInt res = 0;
2037 for (size_t u = 0; u <= n; ++u) {
2038 for (size_t w = 0; w <= n; ++w) {
2039 if (u + w == s && ell + u - w == h) {
2040 res += bin<InfInt>(n - ell, u) * bin<InfInt>(ell, w);
2041 }
2042 }
2043 }
2044 return res;
2045 } else {
2046 InfInt res = 0;
2047
2048 for (size_t u = 0; u <= n; ++u) {
2049 for (size_t v = 0; v <= n; ++v) {
2050 for (size_t w = 0; w <= n; ++w) {
2051 if (u + v + w == s && ell + u - w == h) {
2052 res += bin<InfInt>(n - ell, u) * bin<InfInt>(ell, v) * bin<InfInt>(ell - v, w) *
2053 sqm<InfInt>(T::get_size() - 1, u) * sqm<InfInt>(T::get_size() - 2, v);
2054 break; // no need to check for further matching w
2055 }
2056 }
2057 }
2058 }
2059
2060 /*
2061 // Blahut alternative
2062 for (size_t i=0; i<=n; ++i) {
2063 for (size_t j=0; j<=n; ++j) {
2064 if (i+2*j+h==s+ell) {
2065 if (ell>n || j+h-ell>n-ell || ell>j+h || i>ell || i>ell || j>ell-i) continue;
2066 sum+=bin<InfInt>(n-ell, j+h-ell)*bin<InfInt>(ell, i)*bin<InfInt>(ell-i, j)*powl(F::get_size(),
2067 j+h-ell)*powl(F::get_size()-2, i);
2068 }
2069 }
2070 }
2071 */
2072
2073 return res;
2074 }
2075 }
2076};
2077
2078template <FiniteFieldType T>
2079class UniverseCode : public LinearCode<T> {
2080 public:
2081 UniverseCode(size_t n) : LinearCode<T>(n, n, IdentityMatrix<T>(n)) {
2082 auto weight_enumerator = Polynomial<InfInt>();
2083 for (size_t i = 0; i <= n; ++i) weight_enumerator.set_coefficient(i, bin<InfInt>(n, i));
2084 this->set_weight_enumerator(std::move(weight_enumerator));
2085 }
2086
2087 UniverseCode(const LinearCode<T>& C) : LinearCode<T>(C) {
2088 if (this->n != this->k) throw std::invalid_argument("Linear code cannot be converted into universe code!");
2089 }
2090
2091 UniverseCode(const UniverseCode&) = default;
2095
2097
2098 void get_info(std::ostream& os) const override {
2099 if (os.iword(details::index) < 3) {
2100 LinearCode<T>::get_info(os);
2101 if (os.iword(details::index) > 0) os << std::endl;
2102 }
2103 if (os.iword(details::index) > 0) os << "Universe code";
2104 }
2105
2106 ZeroCode<T> get_dual() const noexcept { return ZeroCode<T>(this->n); }
2107 Vector<T> enc(const Vector<T>& u) const override { return u; }
2108 Vector<T> encinv(const Vector<T>& c) const override { return c; }
2109
2110 Vector<T> dec_BD(const Vector<T>& r) const override {
2111 this->validate_length(r);
2112#ifdef CECCO_ERASURE_SUPPORT
2113 if (LinearCode<T>::erasures_present(r))
2114 throw decoding_failure("Universe code BD decoder failed, received vector contains erasures!");
2115#endif
2116 return r;
2117 }
2118
2119 Vector<T> dec_ML(const Vector<T>& r) const override {
2120 this->validate_length(r);
2121#ifdef CECCO_ERASURE_SUPPORT
2122 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
2123#endif
2124 return r;
2125 }
2126
2127 Vector<T> dec_burst(const Vector<T>& r) const override {
2128 this->validate_length(r);
2129#ifdef CECCO_ERASURE_SUPPORT
2130 if (LinearCode<T>::erasures_present(r))
2131 throw decoding_failure("Universe code burst decoder failed, received vector contains erasures!");
2132#endif
2133 return r;
2134 }
2135
2136#ifdef CECCO_ERASURE_SUPPORT
2137 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
2138 this->validate_length(r);
2139#ifdef CECCO_ERASURE_SUPPORT
2141 throw decoding_failure("Universe code BD error/erasure decoder failed, received vector contains erasures!");
2142#endif
2143 return r;
2144 }
2145 Vector<T> dec_ML_EE(const Vector<T>& r) const override {
2146 this->validate_length(r);
2147 auto c_est = r;
2148 for (size_t i = 0; i < this->n; ++i) {
2149 if (c_est[i].is_erased()) c_est.set_component(i, T(0));
2150 }
2151 return c_est;
2152 }
2153#endif
2154};
2155
2156template <FiniteFieldType T>
2157class ZeroCode : public LinearCode<T> {
2158 public:
2159 ZeroCode(size_t n) : LinearCode<T>(n, 0, ZeroMatrix<T>(1, n)) {}
2160
2161 ZeroCode(const LinearCode<T>& C) : LinearCode<T>(C) {
2162 if (this->k != 0) throw std::invalid_argument("Linear code cannot be converted into zero code!");
2163 }
2164
2165 ZeroCode(const ZeroCode&) = default;
2166 ZeroCode(ZeroCode&&) = default;
2167 ZeroCode& operator=(const ZeroCode&) = default;
2169
2170 ZeroCode<T> get_equivalent_code_in_standard_form() const { return ZeroCode<T>(this->n); }
2171
2172 void get_info(std::ostream& os) const override {
2173 if (os.iword(details::index) < 3) {
2174 LinearCode<T>::get_info(os);
2175 if (os.iword(details::index) > 0) os << std::endl;
2176 }
2177 if (os.iword(details::index) > 0) os << "Zero code";
2178 }
2179};
2180
2181template <FiniteFieldType T>
2182class HammingCode : public LinearCode<T> {
2183 friend class SimplexCode<T>;
2184
2185 public:
2186 HammingCode(size_t s) : LinearCode<T>(Hamming_n(s), Hamming_k(s), Hamming_H(s)), s(s) {
2187 constexpr size_t q = T::get_size();
2188 Polynomial<InfInt> weight_enumerator_dual; // weight enumerator of the dual...
2189 weight_enumerator_dual.set_coefficient(0, 1); // ... code, a q-ary simplex code...
2190 weight_enumerator_dual.set_coefficient(sqm<size_t>(q, s - 1), sqm<InfInt>(q, s) - 1); // ... easy to calculate
2191 this->set_weight_enumerator(MacWilliamsIdentity<T>(weight_enumerator_dual, this->n, this->n - this->k));
2192 }
2193
2194 HammingCode(const LinearCode<T>& C) : LinearCode<T>(C), s(0) {
2195 for (size_t s_cand = 2; s_cand < std::numeric_limits<size_t>::max(); ++s_cand) {
2196 const size_t n = Hamming_n(s_cand);
2197 if (n > this->n) break;
2198 if (n != this->n || Hamming_k(s_cand) != this->k) continue;
2199
2200 std::vector<size_t> seen;
2201 seen.reserve(this->n);
2202 bool valid = true;
2203 for (size_t i = 0; i < this->n && valid; ++i) {
2204 const auto h = this->HT.get_row(i);
2205 T inv;
2206 for (size_t j = 0; j < h.get_n(); ++j)
2207 if (!h[j].is_zero()) {
2208 inv = T(1) / h[j];
2209 break;
2210 }
2211 if (inv.is_zero()) {
2212 valid = false;
2213 break;
2214 }
2215 const size_t key = (inv * h).as_integer();
2216 if (std::ranges::find(seen, key) != seen.end()) {
2217 valid = false;
2218 break;
2219 }
2220 seen.push_back(key);
2221 }
2222 if (valid) {
2223 this->s = s_cand;
2224 this->set_dmin(3);
2225 return;
2226 }
2227 }
2228 throw std::invalid_argument("Linear code cannot be converted into Hamming code!");
2229 }
2230
2231 HammingCode(const HammingCode&) = default;
2233 HammingCode& operator=(const HammingCode&) = default;
2235
2237 return HammingCode<T>(LinearCode<T>::get_equivalent_code_in_standard_form());
2238 }
2239
2240 size_t get_s() const noexcept { return s; }
2241
2242 void get_info(std::ostream& os) const override {
2243 if (os.iword(details::index) < 3) {
2244 LinearCode<T>::get_info(os);
2245 if (os.iword(details::index) > 0) os << std::endl;
2246 }
2247 if (os.iword(details::index) > 0) os << BOLD("Hamming code") " with properties: { s = " << s << " }";
2248 }
2249
2250 SimplexCode<T> get_dual() const noexcept { return SimplexCode<T>(s); }
2251
2252 Vector<T> dec_BD(const Vector<T>& r) const override {
2253#ifdef CECCO_ERASURE_SUPPORT
2254 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
2255#endif
2256 this->validate_length(r);
2257
2258 constexpr size_t q = T::get_size();
2259 const auto s = r * this->HT;
2260 if (s.is_zero()) return r;
2261 auto c_est = r;
2262 for (size_t i = 0; i < this->n; ++i) {
2263 for (size_t j = 1; j < q; ++j) {
2264 T a(j);
2265 if (s == a * this->HT.get_row(i)) {
2266 c_est.set_component(i, c_est[i] - a);
2267 return c_est;
2268 }
2269 }
2270 }
2271
2272 return c_est;
2273 }
2274
2275 Vector<T> dec_ML(const Vector<T>& r) const override {
2276#ifdef CECCO_ERASURE_SUPPORT
2277 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
2278#endif
2279
2280 return dec_BD(r);
2281 }
2282
2283#ifdef CECCO_ERASURE_SUPPORT
2284 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
2285 this->validate_length(r);
2286
2287 const auto X = this->erasure_positions(r);
2288 const size_t tau = X.size();
2289
2290 if (tau == 0) return dec_BD(r);
2291 if (tau > 2) throw decoding_failure("Hamming code BD error/erasure decoder failed!");
2292
2293 auto s = Vector<T>(this->n - this->k);
2294 for (size_t i = 0; i < this->n; ++i) {
2295 if (!r[i].is_erased()) s += r[i] * this->HT.get_row(i);
2296 }
2297
2298 auto c_est = r;
2299
2300 if (tau == 1) {
2301 if (s.is_zero()) {
2302 c_est.set_component(X[0], 0);
2303 } else {
2304 for (size_t i = 0; i < this->n - this->k; ++i) {
2305 if (!this->HT(X[0], i).is_zero()) {
2306 c_est.set_component(X[0], -s[i] / this->HT(X[0], i));
2307 break;
2308 }
2309 }
2310 }
2311 } else if (tau == 2) {
2312 const auto c0 = transpose(Matrix<T>(this->HT.get_row(X[0])));
2313 const auto c1 = transpose(Matrix<T>(this->HT.get_row(X[1])));
2314 const auto c2 = -transpose(Matrix<T>(s));
2315 const auto M = horizontal_join(horizontal_join(c0, c1), c2);
2316 const auto B = M.basis_of_nullspace();
2317 c_est.set_component(X[0], B(0, 0));
2318 c_est.set_component(X[1], B(0, 1));
2319 }
2320
2321 if (!(c_est * this->HT).is_zero()) throw decoding_failure("Hamming code BD error/erasure decoder failed!");
2322
2323 return c_est;
2324 }
2325
2326 Vector<T> dec_ML_EE(const Vector<T>& r) const override { return dec_BD_EE(r); }
2327#endif
2328
2329 private:
2330 size_t s;
2331
2332 static size_t Hamming_n(size_t s) noexcept {
2333 constexpr size_t q = T::get_size();
2334 return (sqm<size_t>(q, s) - 1) / (q - 1);
2335 }
2336
2337 static size_t Hamming_k(size_t s) noexcept { return Hamming_n(s) - s; }
2338
2339 static Matrix<T> Hamming_H(size_t s) {
2340 const size_t n = Hamming_n(s);
2341 auto H = Matrix<T>(n, s);
2342 size_t i = 0;
2343 // topmost element loop
2344 for (size_t top = 0; top < s; ++top) {
2345 const auto v = IdentityMatrix<T>(s - top - 1).rowspace();
2346 for (size_t j = 0; j < v.size(); ++j) {
2347 H.set_component(n - i - 1, top, T(1));
2348 H.set_submatrix(n - i - 1, top + 1, Matrix(v[v.size() - j - 1]));
2349 ++i;
2350 }
2351 }
2352 H.transpose();
2353 return H;
2354 }
2355};
2356
2357template <FiniteFieldType T>
2358class SimplexCode : public LinearCode<T> {
2359 public:
2360 SimplexCode(size_t s)
2361 : LinearCode<T>(HammingCode<T>::Hamming_n(s), s, HammingCode<T>::Hamming_H(s)),
2362 s(s),
2363 cols_as_integers(this->n) {
2364 constexpr size_t q = T::get_size();
2365 Polynomial<InfInt> weight_enumerator;
2366 weight_enumerator.set_coefficient(0, 1);
2367 weight_enumerator.set_coefficient(sqm<size_t>(q, s - 1), sqm<InfInt>(q, s) - 1);
2368 this->set_weight_enumerator(std::move(weight_enumerator));
2369 for (size_t i = 0; i < this->n; ++i) cols_as_integers[i] = this->G.get_col(i).as_integer();
2370 }
2371
2372 SimplexCode(const LinearCode<T>& C) : LinearCode<T>(C), s(0), cols_as_integers(this->n) {
2373 constexpr size_t q = T::get_size();
2374 for (size_t s_cand = 2; s_cand < std::numeric_limits<size_t>::max(); ++s_cand) {
2375 const size_t n = HammingCode<T>::Hamming_n(s_cand);
2376 if (n > this->n) break;
2377 if (n == this->n) {
2378 if (this->k == s_cand && this->get_dmin() == sqm<size_t>(q, s_cand - 1)) {
2379 this->s = s_cand;
2380 for (size_t i = 0; i < this->n; ++i) cols_as_integers[i] = this->G.get_col(i).as_integer();
2381 return;
2382 }
2383 }
2384 }
2385 throw std::invalid_argument("Linear code cannot be converted into Simplex code!");
2386 }
2387
2388 SimplexCode(const SimplexCode&) = default;
2390 SimplexCode& operator=(const SimplexCode&) = default;
2392
2394 return SimplexCode<T>(LinearCode<T>::get_equivalent_code_in_standard_form());
2395 }
2396
2397 size_t get_s() const noexcept { return s; }
2398
2399 void get_info(std::ostream& os) const override {
2400 if (os.iword(details::index) < 3) {
2401 LinearCode<T>::get_info(os);
2402 if (os.iword(details::index) > 0) os << std::endl;
2403 }
2404 if (os.iword(details::index) > 0) os << BOLD("Simplex code") " with properties: { s = " << s << " }";
2405 }
2406
2407 HammingCode<T> get_dual() const noexcept { return HammingCode<T>(s); }
2408 Vector<T> dec_BD(const Vector<T>& r) const override {
2409#ifdef CECCO_ERASURE_SUPPORT
2410 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
2411#endif
2412 if constexpr (T::get_size() != 2) return LinearCode<T>::dec_BD(r);
2413
2414 const auto c_est = dec_ML(r);
2415 if (dH(r, c_est) > this->get_tmax())
2416 throw decoding_failure("Simplex code BD decoder failed!");
2417 else
2418 return c_est;
2419 }
2420
2421 Vector<T> dec_ML(const Vector<T>& r) const override {
2422#ifdef CECCO_ERASURE_SUPPORT
2423 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
2424#endif
2425 if constexpr (T::get_size() != 2) return LinearCode<T>::dec_ML(r);
2426 this->validate_length(r);
2427
2428 Vector<double> y(this->n + 1, 0.0);
2429 y.set_component(0, 0.0);
2430 for (size_t i = 0; i < this->n; ++i) {
2431 const size_t j = cols_as_integers[i];
2432 y.set_component(j, r[i].is_zero() ? 1.0 : -1.0);
2433 }
2434
2435 FWHT(y);
2436
2437 size_t hit = 0;
2438 double best = -std::numeric_limits<double>::infinity();
2439 for (size_t i = 0; i < y.get_n(); ++i) {
2440 if (y[i] > best) {
2441 best = y[i];
2442 hit = i;
2443 }
2444 }
2445
2446 Vector<T> u_est;
2447 u_est.from_integer(hit, this->k);
2448
2449 return u_est * this->G;
2450 }
2451
2452#ifdef CECCO_ERASURE_SUPPORT
2453 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
2454 if constexpr (T::get_size() != 2) return LinearCode<T>::dec_BD_EE(r);
2455 this->validate_length(r);
2456
2457 size_t tau = 0;
2458 for (size_t i = 0; i < this->n; ++i) {
2459 if (r[i].is_erased()) ++tau;
2460 }
2461
2462 if (tau > this->get_dmin() - 1) throw decoding_failure("Simplex code BD error/erasure decoder failed!");
2463
2464 const Vector<T> c_est = dec_ML_EE(r);
2465
2466 size_t t = 0;
2467 for (size_t i = 0; i < this->n; ++i) {
2468 if (!r[i].is_erased() && c_est[i] != r[i]) ++t;
2469 }
2470
2471 if (2 * t + tau > this->get_dmin() - 1) throw decoding_failure("Simplex code BD error/erasure decoder failed!");
2472
2473 return c_est;
2474 }
2475
2476 Vector<T> dec_ML_EE(const Vector<T>& r) const override {
2477 if constexpr (T::get_size() != 2) return LinearCode<T>::dec_ML_EE(r);
2478 this->validate_length(r);
2479
2480 size_t tau = 0;
2481 for (size_t i = 0; i < this->n; ++i) {
2482 if (r[i].is_erased()) ++tau;
2483 }
2484 if (tau == this->n) return Vector<T>(this->n, T(0));
2485
2486 Vector<double> y(this->n + 1, 0.0);
2487 y.set_component(0, 0.0);
2488 for (size_t i = 0; i < this->n; ++i) {
2489 const size_t j = cols_as_integers[i];
2490 if (!r[i].is_erased()) y.set_component(j, r[i].is_zero() ? 1.0 : -1.0);
2491 }
2492
2493 FWHT(y);
2494
2495 size_t hit = 0;
2496 double best = -std::numeric_limits<double>::infinity();
2497 for (size_t u = 0; u < y.get_n(); ++u) {
2498 if (y[u] > best) {
2499 best = y[u];
2500 hit = u;
2501 }
2502 }
2503
2504 Vector<T> u_est;
2505 u_est.from_integer(hit, this->k);
2506
2507 return u_est * this->G;
2508 }
2509#endif
2510
2511 private:
2512 static void FWHT(Vector<double>& a) {
2513 const size_t n = a.get_n();
2514 if (!std::has_single_bit(n)) throw std::invalid_argument("FWHT requires length 2^m!");
2515 for (size_t len = 1; 2 * len <= n; len *= 2) {
2516 for (size_t i = 0; i < n; i += 2 * len) {
2517 for (size_t j = 0; j < len; ++j) {
2518 const double u = a[i + j];
2519 const double v = a[i + j + len];
2520 a.set_component(i + j, u + v);
2521 a.set_component(i + j + len, u - v);
2522 }
2523 }
2524 }
2525 }
2526
2527 size_t s;
2528 std::vector<size_t> cols_as_integers;
2529};
2530
2531template <FiniteFieldType T>
2532class RepetitionCode : public LinearCode<T> {
2533 friend class SingleParityCheckCode<T>;
2534
2535 public:
2536 RepetitionCode(size_t n) : LinearCode<T>(n, 1, Repetition_G(n)) {
2537 Polynomial<InfInt> weight_enumerator;
2538 weight_enumerator.set_coefficient(0, 1);
2539 weight_enumerator.set_coefficient(n, T::get_size() - 1);
2540 this->set_weight_enumerator(std::move(weight_enumerator));
2541 auto p = ZeroPolynomial<T>();
2542 p.set_coefficient(0, -T(1));
2543 p.set_coefficient(n, T(1));
2544 auto q = ZeroPolynomial<T>();
2545 q.set_coefficient(0, -T(1));
2546 q.set_coefficient(1, T(1));
2547 this->set_gamma(p / q);
2548 }
2549
2550 RepetitionCode(const LinearCode<T>& C) : LinearCode<T>(C) {
2551 if (this->k != 1 || this->get_dmin() != this->n)
2552 throw std::invalid_argument("Linear code cannot be converted into repetition code!");
2553 }
2554
2559
2562 return RepetitionCode<T>(LinearCode<T>::get_identical_code_in_polynomial_form());
2563 }
2564
2565 void get_info(std::ostream& os) const override {
2566 if (os.iword(details::index) < 3) {
2567 LinearCode<T>::get_info(os);
2568 if (os.iword(details::index) > 0) os << std::endl;
2569 }
2570 if (os.iword(details::index) > 0) os << "Repetition code";
2571 }
2572
2573 SingleParityCheckCode<T> get_dual() const noexcept { return SingleParityCheckCode<T>(this->n); }
2574 Vector<T> enc(const Vector<T>& u) const override { return Vector<T>(this->n, u[0]); }
2575 Vector<T> encinv(const Vector<T>& c) const override { return Vector<T>(1, c[0]); }
2576
2577 Vector<T> dec_BD(const Vector<T>& r) const override {
2578#ifdef CECCO_ERASURE_SUPPORT
2579 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
2580#endif
2581
2582 const auto c_est = dec_ML(r);
2583 constexpr size_t q = T::get_size();
2584 if (q != 2 || this->n % 2 == 0) {
2585 if (2 * dH(r, c_est) > this->get_dmin() - 1) throw decoding_failure("Repetition code BD decoder failed!");
2586 }
2587
2588 return c_est;
2589 }
2590
2591 Vector<T> dec_ML(const Vector<T>& r) const override {
2592#ifdef CECCO_ERASURE_SUPPORT
2593 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
2594#endif
2595 this->validate_length(r);
2596
2597 constexpr size_t q = T::get_size();
2598 std::array<size_t, q> counters{};
2599 for (size_t i = 0; i < this->n; ++i) ++counters[r[i].get_label()];
2600 const auto it = std::max_element(counters.cbegin(), counters.cend());
2601 std::size_t label = static_cast<std::size_t>(std::distance(counters.cbegin(), it));
2602
2603 return Vector<T>(this->n, T(label));
2604 }
2605
2606#ifdef CECCO_ERASURE_SUPPORT
2607 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
2608 this->validate_length(r);
2609
2610 size_t tau = 0;
2611 for (size_t i = 0; i < this->n; ++i) {
2612 if (r[i].is_erased()) ++tau;
2613 }
2614
2615 if (tau == 0) return dec_BD(r);
2616 if (tau > this->get_dmin() - 1) throw decoding_failure("Repetition code BD error/erasure decoder failed!");
2617
2618 constexpr size_t q = T::get_size();
2619 std::array<size_t, q> counters{};
2620 for (size_t i = 0; i < this->n; ++i) {
2621 if (!r[i].is_erased()) ++counters[r[i].get_label()];
2622 }
2623 const auto it = std::max_element(counters.cbegin(), counters.cend());
2624 std::size_t label = static_cast<std::size_t>(std::distance(counters.cbegin(), it));
2625
2626 const auto c_est = Vector<T>(this->n, T(label));
2627 size_t t = 0;
2628 for (size_t i = 0; i < this->n; ++i) {
2629 if (!r[i].is_erased() && r[i] != c_est[i]) ++t;
2630 }
2631
2632 if (q != 2 || this->n % 2 == 0) {
2633 if (2 * t + tau > this->get_dmin() - 1)
2634 throw decoding_failure("Repetition code BD error/erasure decoder failed!");
2635 }
2636
2637 return c_est;
2638 }
2639
2640 virtual Vector<T> dec_ML_EE(const Vector<T>& r) const override {
2641 this->validate_length(r);
2642
2643 size_t tau = 0;
2644 for (size_t i = 0; i < this->n; ++i) {
2645 if (r[i].is_erased()) ++tau;
2646 }
2647
2648 if (tau == 0) return dec_ML(r);
2649 if (tau == this->n) return Vector<T>(this->n, T(0));
2650
2651 constexpr size_t q = T::get_size();
2652 std::array<size_t, q> counters{};
2653 for (size_t i = 0; i < this->n; ++i) {
2654 if (!r[i].is_erased()) ++counters[r[i].get_label()];
2655 }
2656 const auto it = std::max_element(counters.cbegin(), counters.cend());
2657 std::size_t label = static_cast<std::size_t>(std::distance(counters.cbegin(), it));
2658 return Vector<T>(this->n, T(label));
2659 }
2660#endif
2661
2662 private:
2663 static Matrix<T> Repetition_G(size_t n) { return Matrix<T>(1, n, T(1)); }
2664};
2665
2666template <FiniteFieldType T>
2668 public:
2669 SingleParityCheckCode(size_t n) : LinearCode<T>(n, n - 1, RepetitionCode<T>::Repetition_G(n)) {
2670 Polynomial<InfInt> weight_enumerator_rep;
2671 weight_enumerator_rep.set_coefficient(0, 1);
2672 weight_enumerator_rep.set_coefficient(n, T::get_size() - 1);
2673 this->set_weight_enumerator(MacWilliamsIdentity<T>(weight_enumerator_rep, n, 1));
2674 auto q = ZeroPolynomial<T>();
2675 q.set_coefficient(0, -T(1));
2676 q.set_coefficient(1, T(1));
2677 this->set_gamma(q);
2678 }
2679
2681 if (this->k != this->n - 1)
2682 throw std::invalid_argument("Linear code cannot be converted into single parity check code!");
2683 const auto& HTp = this->get_HT();
2684 for (size_t i = 0; i < this->n; ++i)
2685 if (HTp(i, 0).is_zero())
2686 throw std::invalid_argument("Linear code cannot be converted into single parity check code!");
2687 }
2688
2693
2695 auto Gp = this->G;
2696 Gp.rref();
2697 return SingleParityCheckCode<T>(LinearCode<T>(this->n, this->k, Gp));
2698 }
2700 return SingleParityCheckCode<T>(LinearCode<T>::get_identical_code_in_polynomial_form());
2701 }
2702
2703 void get_info(std::ostream& os) const override {
2704 if (os.iword(details::index) < 3) {
2705 LinearCode<T>::get_info(os);
2706 if (os.iword(details::index) > 0) os << std::endl;
2707 }
2708 if (os.iword(details::index) > 0) os << "Single parity check code";
2709 }
2710
2711 RepetitionCode<T> get_dual() const noexcept { return RepetitionCode<T>(this->n); }
2712 Vector<T> dec_BD(const Vector<T>& r) const override {
2713#ifdef CECCO_ERASURE_SUPPORT
2714 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
2715#endif
2716 this->validate_length(r);
2717
2718 if (!(r * this->HT)[0].is_zero()) throw decoding_failure("Single parity check code BD decoder failed!");
2719
2720 return r;
2721 }
2722
2723 Vector<T> dec_ML(const Vector<T>& r) const override {
2724#ifdef CECCO_ERASURE_SUPPORT
2725 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
2726#endif
2727 this->validate_length(r);
2728
2729 const T s = (r * this->HT)[0];
2730 if (s.is_zero()) return r;
2731
2732 Vector<T> c_est = r;
2733 c_est.set_component(0, r[0] - s / (this->HT)(0, 0));
2734 return c_est;
2735 }
2736
2737#ifdef CECCO_ERASURE_SUPPORT
2738 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
2739 this->validate_length(r);
2740
2741 const auto X = this->erasure_positions(r);
2742 const size_t tau = X.size();
2743
2744 if (tau == 0) return dec_BD(r);
2745 if (tau > 1) throw decoding_failure("Single parity check code BD error/erasure decoder failed!");
2746
2747 T s = T(0);
2748 for (size_t i = 0; i < this->n; ++i)
2749 if (!r[i].is_erased()) s += (this->HT)(i, 0) * r[i];
2750
2751 Vector<T> c_est = r;
2752 c_est.set_component(X[0], -s / (this->HT)(X[0], 0));
2753 return c_est;
2754 }
2755
2756 Vector<T> dec_ML_EE(const Vector<T>& r) const override {
2757 this->validate_length(r);
2758
2759 const auto X = this->erasure_positions(r);
2760 const size_t tau = X.size();
2761
2762 if (tau == 0) return dec_ML(r);
2763 if (tau == this->n) return Vector<T>(this->n, T(0));
2764
2765 T s = T(0);
2766 for (size_t i = 0; i < this->n; ++i)
2767 if (!r[i].is_erased()) s += (this->HT)(i, 0) * r[i];
2768
2769 Vector<T> c_est = r;
2770 c_est.set_component(X[0], -s / (this->HT)(X[0], 0));
2771 for (size_t i = 1; i < tau; ++i) c_est.set_component(X[i], T(0));
2772 return c_est;
2773 }
2774#endif
2775};
2776
2777template <FiniteFieldType T>
2778 requires(std::is_same_v<T, Fp<2>> || std::is_same_v<T, Fp<3>>)
2779class GolayCode : public LinearCode<T> {
2780 public:
2782 if constexpr (T::get_size() == 2) {
2783 this->set_weight_enumerator(Polynomial<InfInt>(
2784 {1, 0, 0, 0, 0, 0, 0, 253, 506, 0, 0, 1288, 1288, 0, 0, 506, 253, 0, 0, 0, 0, 0, 0, 1}));
2785 this->set_gamma(Polynomial<T>({1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1}));
2786 } else {
2787 this->set_weight_enumerator(Polynomial<InfInt>({1, 0, 0, 0, 0, 132, 132, 0, 330, 110, 0, 24}));
2788 this->set_gamma(Polynomial<T>({2, 0, 1, 2, 1, 1}));
2789 }
2790 }
2791
2792 GolayCode(const LinearCode<T>& C) : LinearCode<T>(C) {
2793 // this is a non-trivial result, uses uniqueness of Steiner systems
2794 if (this->n == Golay_n() && this->k == Golay_k()) {
2795 if (std::is_same_v<T, Fp<2>> && this->get_dmin() == 7)
2796 return;
2797 else if (std::is_same_v<T, Fp<3>> && this->get_dmin() == 5)
2798 return;
2799 }
2800 throw std::invalid_argument("Linear code cannot be converted into Golay code!");
2801 }
2802
2803 GolayCode(const GolayCode&) = default;
2804 GolayCode(GolayCode&&) = default;
2805 GolayCode& operator=(const GolayCode&) = default;
2807
2814
2815 virtual void get_info(std::ostream& os) const override {
2816 if (os.iword(details::index) < 3) {
2818 if (os.iword(details::index) > 0) os << std::endl;
2819 }
2820 if (os.iword(details::index) > 0) {
2821 if constexpr (std::is_same_v<T, Fp<2>>) {
2822 os << "Binary Golay code";
2823 } else {
2824 os << "Ternary Golay code";
2825 }
2826 }
2827 }
2828
2829 private:
2830 static constexpr size_t Golay_n() {
2831 if constexpr (std::is_same_v<T, Fp<2>>)
2832 return 23;
2833 else
2834 return 11;
2835 }
2836
2837 static constexpr size_t Golay_k() {
2838 if constexpr (std::is_same_v<T, Fp<2>>)
2839 return 12;
2840 else
2841 return 6;
2842 }
2843
2844 static Matrix<T> Golay_G() {
2846 if constexpr (std::is_same_v<T, Fp<2>>)
2847 gamma = Polynomial<T>({1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1});
2848 else
2849 gamma = Polynomial<T>({2, 0, 1, 2, 1, 1});
2850
2851 const size_t n = Golay_n();
2852 const size_t k = Golay_k();
2853
2854 return ToeplitzMatrix(pad_back(pad_front(Vector<T>(gamma), n), 2 * k + gamma.degree() - 1), k, n);
2855 }
2856};
2857
2858template <FieldType T>
2859class GRSCode : public LinearCode<T> {
2860 public:
2861 GRSCode(const Vector<T>& a, const Vector<T>& d, size_t k) try : LinearCode
2862 <T>(a.get_n(), k, GRS_G(a, d, k)), a(a), d(d) {
2863 const size_t n = this->n;
2864
2865 for (size_t i = 0; i < n; ++i) {
2866 if (d[i] == T(0)) throw std::invalid_argument("GRS codes must have nonzero column multipliers!");
2867 }
2868
2869 if constexpr (FiniteFieldType<T>) {
2870 constexpr size_t q = T::get_size();
2871
2872 Polynomial<InfInt> weight_enumerator;
2873 weight_enumerator.set_coefficient(0, 1);
2874 for (size_t i = n - k + 1; i <= n; ++i) {
2875 InfInt sum = 0;
2876 for (size_t j = 0; j <= i - (n - k + 1); ++j) {
2877 InfInt s = j % 2 ? -1 : 1;
2878 sum += s * bin<InfInt>(i - 1, j) * sqm<InfInt>(q, i - j - (n - k + 1));
2879 }
2880 weight_enumerator.set_coefficient(i, bin<InfInt>(n, i) * (q - 1) * sum);
2881 }
2882
2883 this->set_weight_enumerator(std::move(weight_enumerator));
2884 } else {
2885 this->set_dmin(n - k + 1);
2886 }
2887 }
2888 catch (const std::invalid_argument& e) {
2889 throw std::invalid_argument(std::string("Cannot construct GRS code: ") + e.what());
2890 }
2891
2892 GRSCode(const GRSCode& other)
2893 : LinearCode<T>(other),
2894 a(other.a),
2895 d(other.d),
2896 G_canonical(other.G_canonical),
2897 HT_canonical(other.HT_canonical) {}
2898
2900 : LinearCode<T>(std::move(other)),
2901 a(std::move(other.a)),
2902 d(std::move(other.d)),
2903 G_canonical(std::move(other.G_canonical)),
2904 HT_canonical(std::move(other.HT_canonical)) {}
2905
2906 GRSCode& operator=(const GRSCode& other) {
2907 if (this != &other) {
2908 LinearCode<T>::operator=(other);
2909 a = other.a;
2910 d = other.d;
2911 G_canonical = other.G_canonical;
2912 HT_canonical = other.HT_canonical;
2913 }
2914 return *this;
2915 }
2916
2918 if (this != &other) {
2919 LinearCode<T>::operator=(std::move(other));
2920 a = std::move(other.a);
2921 d = std::move(other.d);
2922 G_canonical = std::move(other.G_canonical);
2923 HT_canonical = std::move(other.HT_canonical);
2924 }
2925 return *this;
2926 }
2927
2928 const Vector<T>& get_a() const noexcept { return a; }
2929 const Vector<T>& get_d() const noexcept { return d; }
2930
2932 auto Gp = this->G;
2933 Gp.rref();
2934 GRSCode res(a, d, this->k, std::move(Gp));
2935 if (this->dmin.has_value()) res.set_dmin(*(this->dmin));
2936 if constexpr (FiniteFieldType<T>)
2937 if (this->weight_enumerator.has_value()) res.set_weight_enumerator(*(this->weight_enumerator));
2938 return res;
2939 }
2940
2941 bool is_singly_extended() const {
2942 for (size_t i = 0; i < this->n; ++i) {
2943 if (a[i] == T(0)) return true;
2944 }
2945 return false;
2946 }
2947
2948 bool is_primitive() const {
2949 if constexpr (!FiniteFieldType<T>) {
2950 return false;
2951 } else {
2952 const size_t n = this->n;
2953 if (n != T::get_size() - 1) return false;
2954 for (size_t i = 0; i < n; ++i) {
2955 if (a[i] == T(0)) return false;
2956 }
2957 return true;
2958 }
2959 }
2960
2961 bool is_narrow_sense() const {
2962 for (size_t i = 0; i < this->n; ++i) {
2963 if (d[i] != T(1)) return false;
2964 }
2965 return true;
2966 }
2967
2968 bool is_normalized() const { return a == d; }
2969
2970 virtual void get_info(std::ostream& os) const override {
2971 if (os.iword(details::index) < 3) {
2972 LinearCode<T>::get_info(os);
2973 if (os.iword(details::index) > 0) os << std::endl;
2974 }
2975 if (os.iword(details::index) > 0) {
2976 os << BOLD("GRS code") " with properties: { a = " << a << ", d = " << d;
2978 os << " singly-extended";
2979 }
2980 if (is_primitive()) {
2981 os << " primitive";
2982 }
2983 if (is_narrow_sense()) {
2984 os << " narrow-sense";
2985 }
2986 if (is_normalized()) {
2987 os << " normalized";
2988 }
2989 os << " }";
2990 }
2991 }
2992
2993 Vector<T> dec_BD(const Vector<T>& r) const override { return dec_WBA(r); }
2994 Vector<T> dec_WBA(const Vector<T>& r) const override { return dec_WBA_impl(r, {}); }
2995 Vector<T> dec_BMA(const Vector<T>& r) const override { return dec_BMA_impl(r, {}); }
2996
2997#ifdef CECCO_ERASURE_SUPPORT
2998 Vector<T> dec_BD_EE(const Vector<T>& r) const override { return dec_WBA_EE(r); }
2999
3000 Vector<T> dec_WBA_EE(const Vector<T>& r) const override { return dec_WBA_impl(r, this->erasure_positions(r)); }
3001
3002 Vector<T> dec_BMA_EE(const Vector<T>& r) const override { return dec_BMA_impl(r, this->erasure_positions(r)); }
3003#endif
3004
3005 protected:
3006 GRSCode(const Vector<T>& a, const Vector<T>& d, size_t k, Matrix<T> G)
3007 : LinearCode<T>(a.get_n(), k, std::move(G)), a(a), d(d) {}
3008
3009 private:
3010 static Matrix<T> GRS_G(const Vector<T>& a, const Vector<T>& d, size_t k) {
3011 if (a.get_n() != d.get_n())
3012 throw std::invalid_argument("code locators a and column multipliers d must have the same length");
3013 return VandermondeMatrix<T>(a, k) * DiagonalMatrix<T>(d);
3014 }
3015
3016 Vector<T> dec_WBA_impl(const Vector<T>& r, const std::vector<size_t>& X) const {
3017 this->validate_length(r);
3018
3019 const size_t tau = X.size();
3020 if (tau > this->get_dmin() - 1)
3021 throw decoding_failure("GRS code WBA error/erasure decoder failed (too many erasures)!");
3022
3023 const size_t n = this->n;
3024 const size_t k = this->k;
3025
3026 const auto ap = delete_components(a, X);
3027 const auto dp = delete_components(d, X);
3028 const size_t np = n - tau;
3029 const size_t tmaxp = (np - k) / 2;
3030 const auto rp = delete_components(r, X);
3031
3032 Vector<T> rp_norm(np);
3033 for (size_t i = 0; i < np; ++i) rp_norm.set_component(i, rp[i] / dp[i]);
3034
3035 const auto M0 = transpose(VandermondeMatrix<T>(ap, np - tmaxp));
3036 const auto M1 = DiagonalMatrix(rp_norm) * transpose(VandermondeMatrix<T>(ap, np - tmaxp - k + 1));
3037 const auto M = horizontal_join(M0, M1);
3038
3039 const auto B = M.basis_of_kernel();
3040
3041 size_t i = 0;
3042 Polynomial<T> Q0;
3043 for (size_t j = 0; j <= np - tmaxp - 1; ++j, ++i) Q0.set_coefficient(j, B(0, i));
3044 Polynomial<T> Q1;
3045 for (size_t j = 0; j <= np - tmaxp - k; ++j, ++i) Q1.set_coefficient(j, B(0, i));
3046
3047 const auto temp = poly_long_div(-Q0, Q1);
3048 const auto quotient = temp.first;
3049 const auto remainder = temp.second;
3050 if (!remainder.is_zero() || quotient.degree() >= k)
3051 throw decoding_failure("GRS code WBA error/erasure decoder failed (true error beyond BD radius)!");
3052
3053 const auto u_est = pad_back(Vector<T>(quotient), k);
3054 G_canonical.call_once([this] {
3055 if (G_canonical.has_value()) return;
3056 G_canonical.emplace(VandermondeMatrix<T>(a, this->k) * DiagonalMatrix<T>(d));
3057 });
3058 const auto c_est = u_est * G_canonical.value();
3059 if (dH(r, c_est) > tmaxp)
3060 throw decoding_failure("GRS code WBA error/erasure decoder failed (identified error beyond BD radius)!");
3061
3062 return c_est;
3063 }
3064
3065 Vector<T> dec_BMA_impl(const Vector<T>& r, const std::vector<size_t>& X) const {
3066 this->validate_length(r);
3067
3068 const size_t n = this->n;
3069 const size_t redundancy = n - this->k;
3070 const size_t tau = X.size();
3071 if (tau > redundancy) throw decoding_failure("GRS code BMA error/erasure decoder failed (too many erasures)!");
3072
3073 Vector<T> rp = r;
3074 for (size_t i = 0; i < tau; ++i) rp.set_component(X[i], T(0));
3075
3076 HT_canonical.call_once([this, n, redundancy] {
3077 if (HT_canonical.has_value()) return;
3078
3079 auto dells = VandermondeMatrix<T>(a, n).invert().get_col(n - 1);
3080 for (size_t i = 0; i < n; ++i) dells.set_component(i, dells[i] / d[i]);
3081 const auto D = DiagonalMatrix<T>(dells);
3082 const auto V = VandermondeMatrix<T>(a, redundancy);
3083 HT_canonical.emplace(transpose(V * D));
3084 });
3085 const auto& HT = HT_canonical.value();
3086
3087 // syndrome
3088 const auto s = rp * HT;
3089 if (s.is_zero() && tau == 0) return r;
3090 const auto sx = Polynomial<T>(s);
3091
3092 // erasure locator
3093 auto Psi = Monomial<T>(0, T(1));
3094 for (size_t i = 0; i < tau; ++i) Psi *= Polynomial<T>({-a[X[i]], T(1)});
3095
3096 // updated syndrome
3097 auto sxp = Polynomial<T>(T(0));
3098 for (size_t i = 0; i < redundancy - tau; ++i) {
3099 T coeff(0);
3100 for (size_t j = 0; j <= tau; ++j) coeff += Psi[j] * s[i + j];
3101 sxp.set_coefficient(i, coeff);
3102 }
3103
3104 // error locator (Berlekamp-Massey)
3105 auto Lambda_e = Monomial<T>(0, T(1));
3106 size_t t = 0;
3107 auto B_ref = Monomial<T>(0, T(1));
3108 size_t t_ref = 0;
3109 T discrepancy_ref(1);
3110 size_t last_update = 0;
3111
3112 for (size_t i = 0; i < redundancy - tau; ++i) {
3113 T discrepancy(0);
3114 const size_t j0 = (t > i ? t - i : 0);
3115 for (size_t j = j0; j <= t; ++j) discrepancy += Lambda_e[j] * sxp[i + j - t];
3116 const auto ratio = discrepancy / discrepancy_ref;
3117
3118 if (!discrepancy.is_zero()) {
3119 const auto B = Lambda_e;
3120 const size_t shift = i - last_update + 1;
3121
3122 if (shift + t_ref <= t) {
3123 Lambda_e -= ratio * Monomial<T>(t - shift - t_ref, T(1)) * B_ref;
3124 } else {
3125 const auto temp = t;
3126 Lambda_e = Monomial<T>(shift + t_ref - t, T(1)) * Lambda_e - ratio * B_ref;
3127 t = shift + t_ref;
3128 B_ref = B;
3129 t_ref = temp;
3130 discrepancy_ref = discrepancy;
3131 last_update = i + 1;
3132 }
3133 }
3134 }
3135
3136 if (2 * t + tau > redundancy)
3137 throw decoding_failure("GRS code BMA error/erasure decoder failed (identified error beyond BD radius)!");
3138
3139 // error/erasure locator
3140 auto Lambda = Psi * Lambda_e;
3141
3142 // Chien search
3143 std::vector<size_t> E;
3144 E.reserve(tau + t);
3145 for (size_t i = 0; i < n; ++i)
3146 if (Lambda(a[i]).is_zero()) E.push_back(i);
3147
3148 if (E.size() != tau + t)
3149 throw decoding_failure("GRS code BMA error/erasure decoder failed (true error beyond BD radius)!");
3150
3151 // Forney
3152 auto Omega = Polynomial<T>(T(0));
3153 for (size_t j = 0; j < tau + t; ++j) {
3154 T coeff(0);
3155 for (size_t m = j + 1; m <= tau + t; ++m) coeff += Lambda[m] * sx[m - j - 1];
3156 Omega.set_coefficient(j, coeff);
3157 }
3158
3159 Lambda.differentiate(1);
3160
3161 Vector<T> c_est = rp;
3162 for (size_t i = 0; i < E.size(); ++i) {
3163 const auto locator = a[E[i]];
3164 c_est.set_component(E[i], c_est[E[i]] - Omega(locator) / (HT(E[i], 0) * Lambda(locator)));
3165 }
3166
3167 return c_est;
3168 }
3169
3170 Vector<T> a;
3171 Vector<T> d;
3172 mutable details::OnceCache<Matrix<T>> G_canonical;
3173 mutable details::OnceCache<Matrix<T>> HT_canonical;
3174};
3175
3176template <FiniteFieldType T>
3177class RSCode : public GRSCode<T> {
3178 public:
3179 RSCode(const T& alpha, size_t b, size_t k) try : RSCode(RS_a_and_D(alpha, b), k, alpha, b) {
3180 } catch (const std::invalid_argument& e) {
3181 throw std::invalid_argument(std::string("Cannot construct RS code: ") + e.what());
3182 }
3183
3184 RSCode(const RSCode&) = default;
3185 RSCode(RSCode&&) = default;
3186 RSCode& operator=(const RSCode&) = default;
3187 RSCode& operator=(RSCode&&) = default;
3188
3189 T get_alpha() const noexcept { return alpha; }
3190 size_t get_b() const noexcept { return b; }
3191
3193 auto Gp = this->G;
3194 Gp.rref();
3195 RSCode<T> res(std::make_pair(this->get_a(), this->get_d()), this->k, alpha, b, std::move(Gp));
3196 if (this->dmin.has_value()) res.set_dmin(*(this->dmin));
3197 if (this->weight_enumerator.has_value()) res.set_weight_enumerator(*(this->weight_enumerator));
3198 return res;
3199 }
3200
3202 RSCode<T> res(std::make_pair(this->get_a(), this->get_d()), this->k, alpha, b,
3203 this->get_G_in_polynomial_form());
3204 if (this->dmin.has_value()) res.set_dmin(*(this->dmin));
3205 if (this->weight_enumerator.has_value()) res.set_weight_enumerator(*(this->weight_enumerator));
3206 if (this->minimal_trellis.has_value()) res.minimal_trellis = this->minimal_trellis;
3207 if (this->codewords.has_value()) res.codewords = this->codewords;
3208 return res;
3209 }
3210
3211 void get_info(std::ostream& os) const override {
3212 if (os.iword(details::index) < 3) {
3213 GRSCode<T>::get_info(os);
3214 if (os.iword(details::index) > 0) os << std::endl;
3215 }
3216 if (os.iword(details::index) > 0) {
3217 os << "RS code: { alpha = " << alpha << ", b = " << b;
3218 if (this->is_primitive()) os << ", primitive";
3219 if (this->is_narrow_sense()) os << ", narrow-sense";
3220 if (this->is_normalized()) os << ", normalized";
3221 os << " }";
3222 }
3223 }
3224
3225 private:
3226 static std::pair<Vector<T>, Vector<T>> RS_a_and_D(const T& alpha, size_t b) {
3227 if (alpha == T(0)) throw std::invalid_argument("alpha must not be zero");
3228 const size_t n = alpha.get_multiplicative_order();
3229 const T alpha_1mb = sqm<T>(alpha, n + 1 - b % n);
3230 Vector<T> a(n), d(n);
3231 T a_pow = T(1), d_pow = T(1);
3232 for (size_t i = 0; i < n; ++i) {
3233 a.set_component(i, a_pow);
3234 d.set_component(i, d_pow);
3235 a_pow = a_pow * alpha;
3236 d_pow = d_pow * alpha_1mb;
3237 }
3238 return {std::move(a), std::move(d)};
3239 }
3240
3241 static Polynomial<T> RS_gamma(const Vector<T>& a, size_t b, size_t k) {
3242 const size_t n = a.get_n();
3243 const size_t start = b % n; // index of alpha^b in a
3244 auto gamma = ZeroPolynomial<T>();
3245 gamma.set_coefficient(0, T(1));
3246 for (size_t j = 0; j < n - k; ++j) {
3247 auto factor = ZeroPolynomial<T>();
3248 factor.set_coefficient(0, -a[(start + j) % n]);
3249 factor.set_coefficient(1, T(1));
3250 gamma = gamma * factor;
3251 }
3252 return gamma;
3253 }
3254
3255 RSCode(std::pair<Vector<T>, Vector<T>> params, size_t k, const T& alpha, size_t b)
3256 : GRSCode<T>(params.first, params.second, k), alpha(alpha), b(b) {
3257 this->set_gamma(RS_gamma(params.first, b, k));
3258 }
3259
3260 RSCode(std::pair<Vector<T>, Vector<T>> params, size_t k, const T& alpha, size_t b, Matrix<T> G)
3261 : GRSCode<T>(params.first, params.second, k, std::move(G)), alpha(alpha), b(b) {
3262 this->set_gamma(RS_gamma(params.first, b, k));
3263 }
3264
3265 T alpha;
3266 size_t b;
3267};
3268
3269class CordaroWagnerCode : public LinearCode<Fp<2>> {
3270 public:
3271 CordaroWagnerCode(size_t r, int8_t m) : LinearCode<Fp<2>>(3 * r + m, 2, CordaroWagner_G(r, m)), r(r), m(m) {
3272 if (r < 1) throw std::invalid_argument("Cordaro-Wagner codes must have r > 0!");
3273 if (m != -1 && m != 0 && m != 1)
3274 throw std::invalid_argument("Cordaro-Wagner codes must have m either -1, 0, or 1!");
3275
3276 auto weight_enumerator = ZeroPolynomial<InfInt>();
3277 weight_enumerator.add_to_coefficient(0, 1);
3278 weight_enumerator.add_to_coefficient(2 * r, 1);
3279 weight_enumerator.add_to_coefficient(2 * r + m, 2);
3280 this->set_weight_enumerator(std::move(weight_enumerator));
3281 }
3282
3283 CordaroWagnerCode(const LinearCode<Fp<2>>& C) : LinearCode<Fp<2>>(C), r(0), m(0) {
3284 if (this->k != 2) throw std::invalid_argument("Linear code cannot be converted into Cordaro-Wagner code!");
3285
3286 r = std::floor(this->n / 3.0 + 1.0 / 2.0);
3287 m = this->n - 3 * r;
3288
3289 const auto c1 = this->G.get_row(0);
3290 const auto c2 = this->G.get_row(1);
3291 std::array<size_t, 3> actual = {c1.wH(), c2.wH(), (c1 + c2).wH()};
3292 std::array<size_t, 3> expected = {2 * r, this->n - r, this->n - r}; // n - r = 2r + m
3293 std::ranges::sort(actual);
3294 std::ranges::sort(expected);
3295 if (actual != expected)
3296 throw std::invalid_argument("Linear code cannot be converted into Cordaro-Wagner code!");
3297 }
3298
3303
3305 return CordaroWagnerCode(LinearCode<Fp<2>>::get_equivalent_code_in_standard_form());
3306 }
3307
3308 size_t get_r() const noexcept { return r; }
3309 int8_t get_m() const noexcept { return m; }
3310
3311 virtual void get_info(std::ostream& os) const override {
3312 if (os.iword(details::index) < 3) {
3313 LinearCode<Fp<2>>::get_info(os);
3314 if (os.iword(details::index) > 0) os << std::endl;
3315 }
3316 if (os.iword(details::index) > 0)
3317 os << BOLD("Cordaro-Wagner code") " with properties: { r = " << r << ", m = " << static_cast<int>(m)
3318 << " }";
3319 }
3320
3321 Vector<Fp<2>> dec_BD(const Vector<Fp<2>>& r) const override {
3322#ifdef CECCO_ERASURE_SUPPORT
3323 if (LinearCode<Fp<2>>::erasures_present(r)) return dec_BD_EE(r);
3324#endif
3325 this->validate_length(r);
3326
3327 const size_t t = this->get_tmax();
3328 for (auto it = this->cbegin(); it != this->cend(); ++it) {
3329 if (dH(*it, r) <= t) return *it;
3330 }
3331 throw decoding_failure("Cordaro-Wagner code BD decoder failed!");
3332 }
3333
3334 Vector<Fp<2>> dec_ML(const Vector<Fp<2>>& r) const override {
3335#ifdef CECCO_ERASURE_SUPPORT
3336 if (LinearCode<Fp<2>>::erasures_present(r)) return dec_ML_EE(r);
3337#endif
3338 this->validate_length(r);
3339
3340 Vector<Fp<2>> best;
3341 size_t best_t = std::numeric_limits<size_t>::max();
3342
3343 for (auto it = this->cbegin(); it != this->cend(); ++it) {
3344 const size_t t = dH(*it, r);
3345 if (t < best_t) {
3346 best = *it;
3347 best_t = t;
3348 }
3349 }
3350 return best;
3351 }
3352
3353#ifdef CECCO_ERASURE_SUPPORT
3354 Vector<Fp<2>> dec_BD_EE(const Vector<Fp<2>>& r) const override {
3355 this->validate_length(r);
3356
3357 const size_t dmin = this->get_dmin();
3358
3359 size_t tau = 0;
3360 for (size_t i = 0; i < this->n; ++i) {
3361 if (r[i].is_erased()) ++tau;
3362 }
3363
3364 if (tau > dmin - 1) throw decoding_failure("Cordaro-Wagner code BD error/erasure decoder failed!");
3365
3366 for (auto it = this->cbegin(); it != this->cend(); ++it) {
3367 size_t t = 0;
3368 for (size_t i = 0; i < this->n; ++i) {
3369 if (!r[i].is_erased() && r[i] != (*it)[i]) ++t;
3370 }
3371
3372 if (2 * t + tau <= dmin - 1) return *it;
3373 }
3374
3375 throw decoding_failure("Cordaro-Wagner code BD error/erasure decoder failed!");
3376 }
3377
3378 Vector<Fp<2>> dec_ML_EE(const Vector<Fp<2>>& r) const override {
3379 this->validate_length(r);
3380
3381 size_t tau = 0;
3382 for (size_t i = 0; i < this->n; ++i) {
3383 if (r[i].is_erased()) ++tau;
3384 }
3385 if (tau == this->n) return Vector<Fp<2>>(this->n, Fp<2>(0));
3386
3387 Vector<Fp<2>> best;
3389
3390 for (auto it = this->cbegin(); it != this->cend(); ++it) {
3391 size_t t = 0;
3392 for (size_t i = 0; i < this->n; ++i) {
3393 if (!r[i].is_erased() && r[i] != (*it)[i]) ++t;
3394 }
3395
3396 if (t < best_t) {
3397 best_t = t;
3398 best = *it;
3399 if (best_t == 0) return best;
3400 }
3401 }
3402
3403 return best;
3404 }
3405#endif
3406
3407 private:
3408 static Matrix<Fp<2>> CordaroWagner_G(size_t r, int8_t m) {
3409 const size_t n = 3 * r + m;
3410 Matrix<Fp<2>> G(2, n);
3411
3412 const Matrix<Fp<2>> type1_column(2, 1, {Fp<2>(1), Fp<2>(0)});
3413 for (size_t s = 0; s < r; ++s) G.set_submatrix(0, s, type1_column); // r of them
3414
3415 const Matrix<Fp<2>> type2_column(2, 1, {Fp<2>(0), Fp<2>(1)});
3416 for (size_t s = r; s < 2 * r + m; ++s) G.set_submatrix(0, s, type2_column); // r+m of them
3417
3418 const Matrix<Fp<2>> type3_column(2, 1, {Fp<2>(1), Fp<2>(1)});
3419 for (size_t s = 2 * r + m; s < 3 * r + m; ++s) G.set_submatrix(0, s, type3_column); // r of them
3420
3421 // => weight distribution is 1 + 2*x^(2r) + x^(2r+m)
3422 return G;
3423 }
3424
3425 size_t r;
3426 int8_t m;
3427};
3428
3429namespace details {
3430
3431template <FieldType T>
3432Matrix<T> LDC_G(const Matrix<T>& G_U, const Matrix<T>& G_V) {
3433 const size_t n = G_U.get_n();
3434 if (G_V.get_n() != n) throw std::invalid_argument("Codes must have same length for LDC construction!");
3435
3436 const size_t kU = G_U.get_m();
3437 const size_t kV = G_V.get_m();
3438
3439 Matrix<T> G(kU + kV, 2 * n);
3440 G.set_submatrix(0, 0, G_U);
3441 G.set_submatrix(0, n, G_U);
3442 G.set_submatrix(kU, n, G_V);
3443 return G;
3444}
3445
3446} // namespace details
3447
3448template <class BU, class BV>
3449 requires(std::derived_from<BU, LinearCode<typename BU::FIELD>> &&
3450 std::derived_from<BV, LinearCode<typename BV::FIELD>> &&
3451 std::same_as<typename BU::FIELD, typename BV::FIELD>)
3452class LDCCode : public LinearCode<typename BU::FIELD> {
3453 public:
3454 using T = typename BU::FIELD;
3455
3456 LDCCode(const BU& U, const BV& V) try : LinearCode
3457 <T>(2 * U.get_n(), U.get_k() + V.get_k(), details::LDC_G(U.get_G(), V.get_G())), U(U), V(V) {}
3458 catch (const std::invalid_argument& e) {
3459 throw std::invalid_argument(std::string("Trying to perform invalid length-doubling construction: ") + e.what());
3460 }
3461
3462 LDCCode(const LDCCode&) = default;
3463 LDCCode(LDCCode&&) = default;
3464 LDCCode& operator=(const LDCCode&) = default;
3465 LDCCode& operator=(LDCCode&&) = default;
3466
3467 const BU& get_U() const noexcept { return U; }
3468 const BV& get_V() const noexcept { return V; }
3469
3470 virtual size_t get_dmin() const override {
3471 if constexpr (std::is_same_v<T, Fp<2>>) this->set_dmin(std::min({2 * U.get_dmin(), V.get_dmin()}));
3472 return this->LinearCode<T>::get_dmin();
3473 }
3474
3475 virtual void get_info(std::ostream& os) const override {
3476 if (os.iword(details::index) < 3) {
3477 LinearCode<T>::get_info(os);
3478 if (os.iword(details::index) > 0) os << std::endl;
3479 }
3480 if (os.iword(details::index) > 0) {
3481 const auto old = os.iword(details::index);
3482 os << BOLD("LDC code") " with properties: { ";
3483 os << "U = " << showbasic;
3484 U.LinearCode<T>::get_info(os);
3485 os << ", V = ";
3486 V.LinearCode<T>::get_info(os);
3487 os << " }";
3488 os.iword(details::index) = old;
3489 }
3490 }
3491
3492 Vector<T> dec_recursive(const Vector<T>& r) const override {
3493#ifdef CECCO_ERASURE_SUPPORT
3494 if (LinearCode<T>::erasures_present(r)) return dec_recursive_EE(r);
3495#endif
3496 this->validate_length(r);
3497
3498 auto rl = r.get_subvector(0, U.get_n());
3499 auto rr = r.get_subvector(U.get_n(), U.get_n());
3500
3501 // U code
3502 const Vector<T> cl_hat_1 = dec_wrapper(U, rl);
3503
3504 // V code
3505 const Vector<T> cr_hat = dec_wrapper(V, rr - rl);
3506 // ... then U code
3507 const Vector<T> cl_hat_2 = dec_wrapper(U, rr - cr_hat);
3508
3509 const auto c_est_1 = concatenate(cl_hat_1, cl_hat_1 + cr_hat);
3510 const auto c_est_2 = concatenate(cl_hat_2, cl_hat_2 + cr_hat);
3511 if (dH(r, c_est_1) < dH(r, c_est_2))
3512 return c_est_1;
3513 else
3514 return c_est_2;
3515 }
3516
3517#ifdef CECCO_ERASURE_SUPPORT
3519 this->validate_length(r);
3520
3521 size_t tau = 0;
3522 for (size_t i = 0; i < this->n; ++i) {
3523 if (r[i].is_erased()) ++tau;
3524 }
3525
3526 if (tau == 0) return dec_recursive(r);
3527 if (tau == this->n) return Vector<T>(this->n, T(0));
3528
3529 auto rl = r.get_subvector(0, U.get_n());
3530 auto rr = r.get_subvector(U.get_n(), U.get_n());
3531
3532 // U code
3533 const Vector<T> cl_hat_1 = dec_wrapper_EE(U, rl);
3534
3535 // V code
3536 const Vector<T> cr_hat = dec_wrapper_EE(V, rr - rl);
3537 // ... then U code
3538 const Vector<T> cl_hat_2 = dec_wrapper_EE(U, rr - cr_hat);
3539
3540 const auto c_est_1 = concatenate(cl_hat_1, cl_hat_1 + cr_hat);
3541 const auto c_est_2 = concatenate(cl_hat_2, cl_hat_2 + cr_hat);
3542
3543 size_t t_1 = 0;
3544 size_t t_2 = 0;
3545 for (size_t i = 0; i < this->n; ++i) {
3546 if (!r[i].is_erased()) {
3547 if (r[i] != c_est_1[i]) ++t_1;
3548 if (r[i] != c_est_2[i]) ++t_2;
3549 }
3550 }
3551
3552 if (t_1 < t_2)
3553 return c_est_1;
3554 else
3555 return c_est_2;
3556 }
3557#endif
3558
3559 private:
3560 BU U;
3561 BV V;
3562
3563 Vector<T> dec_wrapper(const LinearCode<T>& C, const Vector<T>& r) const { return C.dec_ML(r); }
3564
3565 Vector<T> dec_wrapper(const LDCCode& C, const Vector<T>& r) const { return C.dec_recursive(r); }
3566
3567#ifdef CECCO_ERASURE_SUPPORT
3568 Vector<T> dec_wrapper_EE(const LinearCode<T>& C, const Vector<T>& r) const { return C.dec_ML_EE(r); }
3569
3570 Vector<T> dec_wrapper_EE(const LDCCode& C, const Vector<T>& r) const { return C.dec_recursive_EE(r); }
3571#endif
3572};
3573
3574class RMCode : public LinearCode<Fp<2>> {
3575 public:
3576 using T = Fp<2>;
3577
3578 RMCode(size_t r, size_t m) try : LinearCode
3579 <T>(sqm(2, m), RM_k(r, m), RM_G(r, m)), r(r), m(m) { this->set_dmin(sqm(2, m - r)); }
3580 catch (const std::invalid_argument& e) {
3581 throw std::invalid_argument(std::string("Trying to construct invalid RM code: ") + e.what());
3582 }
3583
3584 RMCode(const RMCode&) = default;
3585 RMCode(RMCode&&) = default;
3586 RMCode& operator=(const RMCode&) = default;
3587 RMCode& operator=(RMCode&&) = default;
3588
3589 size_t get_r() const noexcept { return r; }
3590 size_t get_m() const noexcept { return m; }
3591
3592 void get_info(std::ostream& os) const override {
3593 if (os.iword(details::index) < 3) {
3594 LinearCode<T>::get_info(os);
3595 if (os.iword(details::index) > 0) os << std::endl;
3596 }
3597 if (os.iword(details::index) > 0) {
3598 os << BOLD("RM code") " with properties: { r = " << r << ", m = " << m << " }";
3599 }
3600 }
3601
3602 // Documentation note: there is no dec_ML, falls back to LinearCode since dec_recursive is only approximative
3603 // ML!
3604
3605 Vector<T> dec_recursive(const Vector<T>& r) const override {
3606 this->validate_length(r);
3607 return dec_wrapper(this->r, m, r);
3608 }
3609
3610#ifdef CECCO_ERASURE_SUPPORT
3612 this->validate_length(r);
3613
3614 size_t tau = 0;
3615 for (size_t i = 0; i < this->n; ++i) {
3616 if (r[i].is_erased()) ++tau;
3617 }
3618
3619 if (tau == 0) return dec_recursive(r);
3620 if (tau == this->n) return Vector<T>(this->n, T(0));
3621
3622 return dec_wrapper_EE(this->r, m, r);
3623 }
3624#endif
3625
3626 private:
3627 size_t r;
3628 size_t m;
3629
3630 static size_t RM_k(size_t r, size_t m) {
3631 size_t k = 0;
3632 for (size_t i = 0; i <= r; ++i) k += bin<size_t>(m, i);
3633 return k;
3634 }
3635
3636 static Matrix<T> RM_G(size_t r, size_t m) {
3637 if (r > m) throw std::invalid_argument("RM codes require 0 <= r <= m");
3638 const size_t n = sqm(2, m);
3639
3640 if (r == 0) return Matrix<T>(1, n, T(1));
3641 if (r == m) return IdentityMatrix<T>(n);
3642
3643 const auto GU = RM_G(r, m - 1);
3644 const auto GV = RM_G(r - 1, m - 1);
3645 return details::LDC_G(GU, GV);
3646 }
3647
3648 static Vector<T> dec_wrapper(size_t r, size_t m, const Vector<T>& v) {
3649 const size_t n = sqm(2, m);
3650
3651 // UC, decoding means returning v
3652 if (r == m) return v;
3653
3654 // RepC, majority decision
3655 if (r == 0) {
3656 constexpr size_t q = T::get_size();
3657 std::array<size_t, q> counters{};
3658 for (size_t i = 0; i < n; ++i) ++counters[v[i].get_label()];
3659 const auto it = std::max_element(counters.cbegin(), counters.cend());
3660 return Vector<T>(n, T(static_cast<size_t>(std::distance(counters.cbegin(), it))));
3661 }
3662
3663 const size_t np = n / 2;
3664 auto vl = v.get_subvector(0, np);
3665 auto vr = v.get_subvector(np, np);
3666
3667 // U code
3668 const Vector<T> cl_hat_1 = dec_wrapper(r, m - 1, vl);
3669
3670 // V code
3671 const Vector<T> cr_hat = dec_wrapper(r - 1, m - 1, vr - vl);
3672 // ... then U code
3673 const Vector<T> cl_hat_2 = dec_wrapper(r, m - 1, vr - cr_hat);
3674
3675 if (dH(vl, cl_hat_1) + dH(vr, cl_hat_1 + cr_hat) < dH(vl, cl_hat_2) + dH(vr, cl_hat_2 + cr_hat))
3676 return concatenate(cl_hat_1, cl_hat_1 + cr_hat);
3677 else
3678 return concatenate(cl_hat_2, cl_hat_2 + cr_hat);
3679 }
3680
3681#ifdef CECCO_ERASURE_SUPPORT
3682 static Vector<T> dec_wrapper_EE(size_t r, size_t m, const Vector<T>& v) {
3683 const size_t n = sqm(2, m);
3684
3685 if (r == m) {
3686 auto c = v;
3687 for (size_t i = 0; i < n; ++i)
3688 if (c[i].is_erased()) c.set_component(i, T(0));
3689 return c;
3690 }
3691
3692 if (r == 0) {
3693 constexpr size_t q = T::get_size();
3694 std::array<size_t, q> counters{};
3695 for (size_t i = 0; i < n; ++i)
3696 if (!v[i].is_erased()) ++counters[v[i].get_label()];
3697 const auto it = std::max_element(counters.cbegin(), counters.cend());
3698 return Vector<T>(n, T(static_cast<size_t>(std::distance(counters.cbegin(), it))));
3699 }
3700
3701 const size_t np = n / 2;
3702 auto vl = v.get_subvector(0, np);
3703 auto vr = v.get_subvector(np, np);
3704
3705 // U code
3706 const Vector<T> cl_hat_1 = dec_wrapper_EE(r, m - 1, vl);
3707
3708 // V code...
3709 const Vector<T> cr_hat = dec_wrapper_EE(r - 1, m - 1, vr - vl);
3710 // ... then U code
3711 const Vector<T> cl_hat_2 = dec_wrapper_EE(r, m - 1, vr - cr_hat);
3712
3713 const auto c_est_1 = concatenate(cl_hat_1, cl_hat_1 + cr_hat);
3714 const auto c_est_2 = concatenate(cl_hat_2, cl_hat_2 + cr_hat);
3715
3716 size_t t_1 = 0;
3717 size_t t_2 = 0;
3718 for (size_t i = 0; i < n; ++i) {
3719 if (!v[i].is_erased()) {
3720 if (v[i] != c_est_1[i]) ++t_1;
3721 if (v[i] != c_est_2[i]) ++t_2;
3722 }
3723 }
3724
3725 if (t_1 < t_2)
3726 return c_est_1;
3727 else
3728 return c_est_2;
3729 }
3730#endif
3731};
3732
3733template <class B>
3735class SubfieldSubcode : public LinearCode<typename B::FIELD::BASE_FIELD> {
3736 using SUPER = typename B::FIELD;
3737 using SUB = typename SUPER::BASE_FIELD;
3738
3739 public:
3741 } catch (const std::invalid_argument& e) {
3742 throw std::invalid_argument(
3743 std::string("Trying to construct a subfield subcode from a super code that is not over a superfield: ") +
3744 e.what());
3745 }
3746
3751
3752 const B& get_SuperCode() const noexcept { return SuperCode; }
3753
3755#ifdef CECCO_ERASURE_SUPPORT
3757#endif
3758 return project(SuperCode.dec_BD(Vector<SUPER>(r)));
3759 }
3760
3762#ifdef CECCO_ERASURE_SUPPORT
3764#endif
3765 return project(SuperCode.dec_ML(Vector<SUPER>(r)));
3766 }
3767
3768#ifdef CECCO_ERASURE_SUPPORT
3771 }
3772
3775 }
3776#endif
3777
3778 virtual void get_info(std::ostream& os) const override {
3779 if (os.iword(details::index) < 3) {
3781 if (os.iword(details::index) > 0) os << std::endl;
3782 }
3783 if (os.iword(details::index) > 0) {
3784 const auto old = os.iword(details::index);
3785 os << BOLD("Subfield-subcode") " with properties: {" << std::endl;
3786 os << "Gamma = " << std::endl << Gamma << ", " << std::endl;
3787 os << "Supercode = " << showbasic;
3789 os << " " << showspecial << SuperCode << std::endl;
3790 os << "}";
3791 os.iword(details::index) = old;
3792 }
3793 }
3794
3795 private:
3796 static Vector<SUB> project(const Vector<SUPER>& c) {
3797 try {
3798 return Vector<SUB>(c);
3799 } catch (const std::invalid_argument& e) {
3800 throw decoding_failure(std::string("Subfield subcode supercode decoder failed: ") + e.what());
3801 }
3802 }
3803
3808
3809 static std::pair<size_t, Matrix<SUPER>> SSC_parameters(const B& SuperCode) {
3810 const size_t n = SuperCode.get_n();
3811 const size_t kp = SuperCode.get_k();
3812 const size_t m = SUPER(0).template as_vector<SUB>().get_n();
3813 const auto Gp = SuperCode.get_G();
3814
3815 auto T = [m](const SUPER& Gij) -> Matrix<SUB> {
3816 const auto v = pad_back(pad_front(Gij.template as_vector<SUB>().reverse(), 2 * m - 1), 3 * m - 2);
3817 return ToeplitzMatrix<SUB>(v, m, 2 * m - 1);
3818 };
3819
3820 auto R = IdentityMatrix<SUB>(2 * m - 1);
3821 for (size_t j = 2 * m - 1; j-- > m;) {
3822 const auto I = IdentityMatrix<SUB>(j - m);
3823 const auto E = Matrix<SUB>(unit_vector<SUB>(m, 0));
3824 const auto C = CompanionMatrix<SUB>(Vector(SUPER::get_modulus())).transpose();
3825 const auto Rj = diagonal_join(I, vertical_join(E, C));
3826 R *= Rj;
3827 }
3828
3829 Matrix<SUB> Gt(kp * m, n * (m - 1));
3830 for (size_t i = 0; i < kp; ++i) {
3831 for (size_t j = 0; j < n; ++j) Gt.set_submatrix(i * m, j * (m - 1), (T(Gp(i, j)) * R).delete_column(0));
3832 }
3833 const auto Gammat = Gt.transpose().basis_of_kernel();
3834 const size_t k = Gammat.rank();
3835 if (k == 0) return std::make_pair(0, Matrix<SUPER>(k, kp));
3836
3837 Matrix<SUPER> Gamma(k, kp);
3838 for (size_t i = 0; i < k; ++i) {
3839 for (size_t j = 0; j < kp; ++j)
3841 }
3842
3843 return std::make_pair(k, Gamma);
3844 }
3845
3846 B SuperCode;
3848};
3849
3850template <class B>
3852
3853template <class B>
3855
3856template <class B>
3857 requires std::derived_from<B, GRSCode<typename B::FIELD>>
3859 using Base = SubfieldSubcode<B>;
3860 using SUPER = typename B::FIELD;
3861 using SUB = typename SUPER::BASE_FIELD;
3862
3863 public:
3865 } catch (const std::invalid_argument& e) {
3866 throw std::invalid_argument(std::string("Cannot construct alternant code: ") + e.what());
3867 }
3868
3871 try : Base(GRSCode<SUPER>(a, d, k_from_delta(a.get_n(), delta))), delta(delta) {
3872 } catch (const std::invalid_argument& e) {
3873 throw std::invalid_argument(std::string("Cannot construct alternant code: ") + e.what());
3874 }
3875
3876 AlternantCode(const AlternantCode&) = default;
3880
3881 const Vector<SUPER>& get_a() const noexcept { return this->get_SuperCode().get_a(); }
3882 const Vector<SUPER>& get_d() const noexcept { return this->get_SuperCode().get_d(); }
3883 virtual size_t get_delta() const noexcept { return delta; }
3884
3885 virtual void get_info(std::ostream& os) const override {
3886 if (os.iword(details::index) < 3) {
3887 Base::get_info(os);
3888 if (os.iword(details::index) > 0) os << std::endl;
3889 }
3890 if (os.iword(details::index) > 0) {
3891 os << BOLD("Alternant code") " with properties: { delta = " << get_delta();
3892 if (this->get_SuperCode().is_primitive()) os << ", primitive";
3893 if (this->get_SuperCode().is_narrow_sense()) os << ", narrow-sense";
3894 if (this->get_SuperCode().is_normalized()) os << ", normalized";
3895 os << " }";
3896 }
3897 }
3898
3899 protected:
3901 if (delta == 0 || delta > n) throw std::invalid_argument("delta must satisfy 1 <= delta <= n");
3902 return n - delta + 1;
3903 }
3904
3905 private:
3906 size_t delta;
3907};
3908
3909template <class B>
3910 requires std::derived_from<B, RSCode<typename B::FIELD>>
3911class BCHCode : public AlternantCode<B> {
3912 using Base = AlternantCode<B>;
3913 using SUPER = typename B::FIELD;
3914 using SUB = typename SUPER::BASE_FIELD;
3915
3916 public:
3917 BCHCode(const B& rscode) try : Base(rscode) {
3918 } catch (const std::invalid_argument& e) {
3919 throw std::invalid_argument(std::string("Cannot construct BCH code: ") + e.what());
3920 }
3921
3925 } catch (const std::invalid_argument& e) {
3926 throw std::invalid_argument(std::string("Cannot construct BCH code: ") + e.what());
3927 }
3928
3929 BCHCode(const BCHCode&) = default;
3930 BCHCode(BCHCode&&) = default;
3931 BCHCode& operator=(const BCHCode&) = default;
3932 BCHCode& operator=(BCHCode&&) = default;
3933
3934 SUPER get_alpha() const noexcept { return this->get_SuperCode().get_alpha(); }
3935 size_t get_b() const noexcept { return this->get_SuperCode().get_b(); }
3936
3937 virtual void get_info(std::ostream& os) const override {
3938 if (os.iword(details::index) < 3) {
3939 Base::get_info(os);
3940 if (os.iword(details::index) > 0) os << std::endl;
3941 }
3942 if (os.iword(details::index) > 0) os << BOLD("BCH code");
3943 }
3944};
3945
3946template <FiniteFieldType SUPER>
3948 using Base = AlternantCode<GRSCode<SUPER>>;
3949 using SUB = typename SUPER::BASE_FIELD;
3950
3951 public:
3952 GoppaCode(const Vector<SUPER>& a, size_t delta) try : GoppaCode(a, Goppa_polynomial(delta)) {
3953 } catch (const std::invalid_argument& e) {
3954 throw std::invalid_argument(std::string("Cannot construct Goppa code: ") + e.what());
3955 }
3956
3957 GoppaCode(const Vector<SUPER>& a, Polynomial<SUPER> g) try
3958 : Base(a, Goppa_multipliers(a, g), g.degree() + 1),
3959 g(std::move(g)),
3960 squarefree(GCD(this->g, derivative(this->g, 1)).degree() == 0) {
3961 } catch (const std::invalid_argument& e) {
3962 throw std::invalid_argument(std::string("Cannot construct Goppa code: ") + e.what());
3963 }
3964
3965 GoppaCode(const GoppaCode&) = default;
3966 GoppaCode(GoppaCode&&) = default;
3967 GoppaCode& operator=(const GoppaCode&) = default;
3969
3970 const Polynomial<SUPER>& get_g() const noexcept { return g; }
3971 bool is_squarefree() const noexcept { return squarefree; }
3972
3973 size_t get_delta() const noexcept override {
3974 if constexpr (std::is_same_v<SUB, Fp<2>>)
3975 if (squarefree) return 2 * g.degree() + 1;
3976 return Base::get_delta();
3977 }
3978
3979 void get_info(std::ostream& os) const override {
3980 if (os.iword(details::index) < 3) {
3981 Base::get_info(os);
3982 if (os.iword(details::index) > 0) os << std::endl;
3983 }
3984 if (os.iword(details::index) > 0) {
3985 os << BOLD("Goppa code") " with properties: { g = " << g;
3986 if (squarefree) os << ", square-free";
3987 os << " }";
3988 }
3989 }
3990
3993 {
3994 this->validate_length(r);
3995
3996 if (!squarefree) throw std::invalid_argument("Patterson decoder requires a square-free Goppa polynomial");
3997
3998 const size_t n = this->n;
3999 const auto& a = this->get_a();
4000 const size_t t = g.degree();
4001
4002 if (t == 0) throw std::invalid_argument("Patterson decoder requires a nonconstant Goppa polynomial");
4003
4005 const auto& inv = *Patterson_inv_cache;
4006
4007 const auto one = Monomial<SUPER>(0, SUPER(1));
4008 const auto x = Monomial<SUPER>(1, SUPER(1));
4009 const auto zero = ZeroPolynomial<SUPER>();
4010
4011 auto S = zero;
4012 for (size_t i = 0; i < n; ++i)
4013 if (r[i] == SUB(1)) S = (S + inv[i]) % g;
4014 if (S.is_zero()) return r;
4015
4017 const auto d = GCD(S, g, &u);
4018 if (d.is_zero() || d.degree() != 0)
4019 throw decoding_failure("Patterson decoder failed (syndrome not invertible modulo g)");
4020 const auto h = (((SUPER(1) / d[0]) * u) + x) % g;
4021
4023 if (h.is_zero()) {
4024 sigma = one;
4025 } else {
4026 auto R = h;
4027 const size_t squarings = std::bit_width(SUPER::get_size() - 1) * t - 1;
4028 for (size_t i = 0; i < squarings; ++i) R = (R * R) % g;
4029
4030 auto r0 = g, r1 = R;
4031 auto t0 = zero, t1 = one;
4032 while (!r1.is_zero() && r1.degree() > t / 2) {
4033 const auto [q, rem] = poly_long_div(r0, r1);
4034 const auto next_t = t0 - q * t1;
4035 r0 = std::move(r1);
4036 r1 = rem;
4037 t0 = std::move(t1);
4038 t1 = next_t;
4039 }
4040 if (r1.is_zero()) throw decoding_failure("Patterson decoder failed (half-GCD)");
4041 sigma = r1 * r1 + x * (t1 * t1);
4042 }
4043
4044 // Chien search
4045 std::vector<size_t> E;
4046 E.reserve(t);
4047 for (size_t i = 0; i < n; ++i)
4048 if (sigma(a[i]).is_zero()) E.push_back(i);
4049
4050 if (E.size() > t) throw decoding_failure("Patterson decoder failed (too many errors)");
4051
4052 Vector<SUB> c_est = r;
4053 for (size_t i = 0; i < E.size(); ++i) c_est.set_component(E[i], r[E[i]] + SUB(1));
4054
4055 return c_est;
4056 }
4057
4058 private:
4060 bool squarefree;
4061 mutable details::OnceCache<std::vector<Polynomial<SUPER>>> Patterson_inv_cache;
4062
4063 void calculate_Patterson_inv_cache() const {
4064 Patterson_inv_cache.call_once([this] {
4065 if (Patterson_inv_cache.has_value()) return;
4066 const size_t n = this->get_n();
4067 const auto& a = this->get_a();
4068 std::vector<Polynomial<SUPER>> inv(n);
4069 for (size_t i = 0; i < n; ++i) {
4070 Polynomial<SUPER> u;
4071 const auto d = GCD(Polynomial<SUPER>({a[i], SUPER(1)}), g, &u);
4072 if (d.is_zero() || d.degree() != 0)
4073 throw std::invalid_argument("Goppa locator a[" + std::to_string(i) + "] is a root of g");
4074 inv[i] = ((SUPER(1) / d[0]) * u) % g;
4075 }
4076 Patterson_inv_cache.emplace(std::move(inv));
4077 });
4078 }
4079
4080 // docu note: these are G multipliers (in literature typically: H multipliers)
4081 static Vector<SUPER> Goppa_multipliers(const Vector<SUPER>& a, const Polynomial<SUPER>& h) {
4082 const size_t n = a.get_n();
4083 Vector<SUPER> res(n);
4084
4085 for (size_t i = 0; i < n; ++i) {
4086 const SUPER ha = h(a[i]);
4087 if (ha.is_zero()) throw std::invalid_argument("Goppa polynomial vanishes at a code locator");
4088
4089 SUPER denominator(1);
4090 for (size_t j = 0; j < n; ++j) {
4091 if (j == i) continue;
4092 const SUPER diff = a[i] - a[j]; // are guaranteed to be pairwise distinct
4093 denominator *= diff;
4094 }
4095 res.set_component(i, ha / denominator);
4096 }
4097
4098 return res;
4099 }
4100
4101 static Polynomial<SUPER> Goppa_polynomial(size_t delta) {
4102 if (delta < 2) throw std::invalid_argument("delta must be at least 2 for a nonconstant Goppa polynomial");
4103 return find_irreducible<SUPER>(delta - 1);
4104 }
4105};
4106
4107template <FieldType T, class B>
4109class ExtendedCode : public LinearCode<T> {
4110 public:
4111 ExtendedCode(const B& BaseCode, size_t i, const Vector<T>& v) try : LinearCode
4112 <T>(BaseCode.get_n() + 1, BaseCode.get_k() == 0 ? v.wH() : BaseCode.get_k(),
4113 Extended_G(BaseCode.get_G(), i, v)),
4114 BaseCode(BaseCode), i(i), v(v), parity(false) {
4115 if (v == Extended_v(this->BaseCode.get_G())) parity = true;
4116 }
4117 catch (const std::invalid_argument& e) {
4118 throw std::invalid_argument(std::string("Trying to extend a code with invalid extension parameters: ") +
4119 e.what());
4120 }
4121
4122 ExtendedCode(B&& BaseCode, size_t i, const Vector<T>& v) try : LinearCode
4123 <T>(BaseCode.get_n() + 1, BaseCode.get_k() == 0 ? v.wH() : BaseCode.get_k(),
4124 Extended_G(BaseCode.get_G(), i, v)),
4125 BaseCode(std::move(BaseCode)), i(i), v(v), parity(v == Extended_v(this->BaseCode.get_G())) {}
4126 catch (const std::invalid_argument& e) {
4127 throw std::invalid_argument(std::string("Trying to extend a code with invalid extension parameters: ") +
4128 e.what());
4129 }
4130
4131 explicit ExtendedCode(const B& BaseCode)
4135 i(0),
4136 v(this->G.get_col(i)),
4137 parity(true) {}
4138
4139 ExtendedCode(const ExtendedCode&) = default;
4143
4144 size_t get_i() const noexcept { return i; }
4145 const B& get_BaseCode() const noexcept { return BaseCode; }
4146
4147 const Polynomial<InfInt>& get_weight_enumerator() const override {
4148 if constexpr (!FiniteFieldType<T>) {
4149 throw std::logic_error("Cannot calculate weight enumerator of code over infinite field!");
4150 } else {
4151 // In non-binary case, zero row sum is not the same as even Hamming distance so this really only
4152 // works for the binary Fp<2> case! Otherwise we fall back to linear code weight weight enumerator
4153 // calculation.
4154 if constexpr (std::is_same_v<T, Fp<2>>) {
4155 if (parity) {
4156 auto weight_enumerator = BaseCode.get_weight_enumerator();
4157 for (size_t w = 0; w <= weight_enumerator.degree(); ++w) {
4158 if (w % 2 && weight_enumerator[w] != 0) {
4159 weight_enumerator.add_to_coefficient(w + 1, weight_enumerator[w]);
4160 weight_enumerator.set_coefficient(w, 0);
4161 }
4162 }
4163 this->set_weight_enumerator(std::move(weight_enumerator));
4164 }
4165 }
4166 return LinearCode<T>::get_weight_enumerator();
4167 }
4168 }
4169
4170 virtual void get_info(std::ostream& os) const override {
4171 if (os.iword(details::index) < 3) {
4172 LinearCode<T>::get_info(os);
4173 if (os.iword(details::index) > 0) os << std::endl;
4174 }
4175 if (os.iword(details::index) > 0) {
4176 const auto old = os.iword(details::index);
4177 os << BOLD("Extended code") " with properties: { i = " << i;
4178 os << ", v = " << v;
4179 if (parity) os << ", even parity";
4180 os << ", BaseCode = " << showbasic;
4181 BaseCode.LinearCode<T>::get_info(os);
4182 os << " " << showspecial << BaseCode;
4183 os << " }";
4184 os.iword(details::index) = old;
4185 }
4186 }
4187
4188 virtual Vector<T> dec_BD(const Vector<T>& r) const override {
4189#ifdef CECCO_ERASURE_SUPPORT
4190 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
4191#endif
4192 return LinearCode<T>::dec_BD(r);
4193 }
4194
4195 virtual Vector<T> dec_ML(const Vector<T>& r) const override {
4196#ifdef CECCO_ERASURE_SUPPORT
4197 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
4198#endif
4199 return LinearCode<T>::dec_ML(r);
4200 }
4201
4202#ifdef CECCO_ERASURE_SUPPORT
4203 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
4204 if (!parity || !r[i].is_erased()) return LinearCode<T>::dec_BD_EE(r);
4205 this->validate_length(r);
4206
4207 const auto r_base = concatenate(r.get_subvector(0, i), r.get_subvector(i + 1, this->n - i - 1));
4208
4209 const auto cp_est = BaseCode.dec_ML_EE(r_base); // sic!
4210
4211 T p(0);
4212 for (size_t j = 0; j < cp_est.get_n(); ++j) p += cp_est[j];
4213
4214 Vector<T> c_est(this->n);
4218
4219 size_t tau = 0;
4220 size_t t = 0;
4221 for (size_t j = 0; j < this->n; ++j) {
4222 if (r[j].is_erased())
4223 ++tau;
4224 else if (r[j] != c_est[j])
4225 ++t;
4226 }
4227
4228 if (2 * t + tau > this->get_dmin() - 1)
4229 throw decoding_failure("Extended code BD error/erasure decoder failed!");
4230
4231 return c_est;
4232 }
4233
4234 Vector<T> dec_ML_EE(const Vector<T>& r) const override {
4235 if (!parity || !r[i].is_erased()) return LinearCode<T>::dec_ML_EE(r);
4236 this->validate_length(r);
4237
4238 const auto r_base = concatenate(r.get_subvector(0, i), r.get_subvector(i + 1, this->n - i - 1));
4239
4240 const auto cp_est = BaseCode.dec_ML_EE(r_base);
4241
4242 T p(0);
4243 for (size_t j = 0; j < cp_est.get_n(); ++j) p += cp_est[j];
4244
4245 Vector<T> c_est(this->n);
4249 return c_est;
4250 }
4251#endif
4252
4253 private:
4254 static Matrix<T> Extended_G(const Matrix<T>& Gp, size_t i, const Vector<T>& v) {
4255 const size_t n = Gp.get_n();
4256 const size_t k = Gp.get_m();
4257
4258 if (i > n) throw std::invalid_argument("Extension index invalid");
4259 if (v.get_n() != k) throw std::invalid_argument(std::string("Length of v must be ") + std::to_string(k));
4260
4261 Matrix<T> G(k, n + 1);
4262 for (size_t j = 0; j < i; ++j) G.set_submatrix(0, j, Gp.get_submatrix(0, j, k, 1));
4263 G.set_submatrix(0, i, transpose(Matrix<T>(v)));
4264 for (size_t j = i; j < n; ++j) G.set_submatrix(0, j + 1, Gp.get_submatrix(0, j, k, 1));
4265 return G;
4266 }
4267
4268 static Vector<T> Extended_v(const Matrix<T>& Gp) {
4269 const size_t n = Gp.get_n();
4270 const size_t k = Gp.get_m();
4271
4272 Vector<T> v(k);
4273 for (size_t j = 0; j < n; ++j) v += Gp.get_col(j);
4274 v *= -T(1);
4275 return v;
4276 }
4277
4278 B BaseCode;
4279 size_t i;
4280 Vector<T> v;
4281 bool parity;
4282};
4283
4284template <FieldType T, class B>
4286class AugmentedCode : public LinearCode<T> {
4287 public:
4288 AugmentedCode(const B& BaseCode, size_t j, const Vector<T>& w) try : LinearCode
4289 <T>(BaseCode.get_n(), BaseCode.get_k() + 1, Augmented_G(BaseCode.get_G(), j, w)), BaseCode(BaseCode), j(j),
4290 w(w) {}
4291 catch (const std::invalid_argument& e) {
4292 throw std::invalid_argument(std::string("Trying to augment a code by an invalid w: ") + e.what());
4293 }
4294
4295 AugmentedCode(B&& BaseCode, size_t j, const Vector<T>& w) try : LinearCode
4296 <T>(BaseCode.get_n(), BaseCode.get_k() + 1, Augmented_G(BaseCode.get_G(), j, w)), BaseCode(std::move(BaseCode)),
4297 j(j), w(w) {}
4298 catch (const std::invalid_argument& e) {
4299 throw std::invalid_argument(std::string("Trying to augment a code by an invalid w: ") + e.what());
4300 }
4301
4302 AugmentedCode(const AugmentedCode&) = default;
4306
4307 const B& get_BaseCode() const noexcept { return BaseCode; }
4308 size_t get_j() const noexcept { return j; }
4309 const Vector<T>& get_w() const noexcept { return w; }
4310
4311 virtual void get_info(std::ostream& os) const override {
4312 if (os.iword(details::index) < 3) {
4313 LinearCode<T>::get_info(os);
4314 if (os.iword(details::index) > 0) os << std::endl;
4315 }
4316 if (os.iword(details::index) > 0) {
4317 const auto old = os.iword(details::index);
4318 os << BOLD("Augmented code") " with properties: { w = " << w;
4319 os << ", BaseCode = " << showbasic;
4320 BaseCode.LinearCode<T>::get_info(os);
4321 os << " " << showspecial << BaseCode;
4322 os << " }";
4323 os.iword(details::index) = old;
4324 }
4325 }
4326
4327 Vector<T> dec_BD(const Vector<T>& r) const override {
4328#ifdef CECCO_ERASURE_SUPPORT
4329 if (LinearCode<T>::erasures_present(r)) return dec_BD_EE(r);
4330#endif
4331 if constexpr (!FiniteFieldType<T>) {
4332 return LinearCode<T>::dec_BD(r);
4333 } else {
4334 this->validate_length(r);
4335
4336 const size_t t = this->get_tmax();
4337
4338 for (size_t i = 0; i < T::get_size(); ++i) {
4339 const T alpha = T(i);
4340
4341 try {
4342 Vector<T> cp_est = BaseCode.dec_BD(r - alpha * w);
4343 Vector<T> c_est = cp_est + alpha * w;
4344 if (dH(r, c_est) <= t) return c_est;
4345 } catch (const decoding_failure&) {
4346 continue;
4347 }
4348 }
4349
4350 throw decoding_failure("Augmented code BD decoder failed!");
4351 }
4352 }
4353
4354 Vector<T> dec_ML(const Vector<T>& r) const override {
4355#ifdef CECCO_ERASURE_SUPPORT
4356 if (LinearCode<T>::erasures_present(r)) return dec_ML_EE(r);
4357#endif
4358 if constexpr (!FiniteFieldType<T>) {
4359 return LinearCode<T>::dec_ML(r);
4360 } else {
4361 this->validate_length(r);
4362
4363 const size_t tmax = this->get_tmax();
4364
4365 Vector<T> best = BaseCode.dec_ML(r); // r - 0*w
4366 size_t best_t = dH(r, best);
4367
4368 if (best_t <= tmax) return best;
4369
4370 for (size_t i = 1; i < T::get_size(); ++i) {
4371 const T alpha = T(i);
4372
4373 Vector<T> cp_est = BaseCode.dec_ML(r - alpha * w);
4374 Vector<T> c_est = cp_est + alpha * w;
4375
4376 const size_t t = dH(r, c_est);
4377
4378 if (t <= tmax) return c_est;
4379
4380 if (t < best_t) {
4381 best_t = t;
4382 best = std::move(c_est);
4383 }
4384 }
4385
4386 return best;
4387 }
4388 }
4389
4390#ifdef CECCO_ERASURE_SUPPORT
4391 Vector<T> dec_BD_EE(const Vector<T>& r) const override {
4392 if constexpr (!FiniteFieldType<T>) {
4393 return LinearCode<T>::dec_BD_EE(r);
4394 } else {
4395 this->validate_length(r);
4396
4397 const size_t dmin = this->get_dmin();
4398
4399 size_t tau = 0;
4400 for (size_t j = 0; j < this->n; ++j)
4401 if (r[j].is_erased()) ++tau;
4402
4403 if (tau > dmin - 1) throw decoding_failure("Augmented code BD error/erasure decoder failed!");
4404
4405 for (size_t i = 0; i < T::get_size(); ++i) {
4406 const T alpha = T(i);
4407
4408 try {
4409 const auto cp_est = BaseCode.dec_BD_EE(r - alpha * w);
4410 const auto c_est = cp_est + alpha * w;
4411
4412 size_t t = 0;
4413 for (size_t j = 0; j < this->n; ++j) {
4414 if (!r[j].is_erased() && r[j] != c_est[j]) ++t;
4415 }
4416
4417 if (2 * t + tau <= dmin - 1) return c_est;
4418
4419 } catch (const decoding_failure&) {
4420 continue;
4421 }
4422 }
4423
4424 throw decoding_failure("Augmented code BD error/erasure decoder failed!");
4425 }
4426 }
4427
4428 Vector<T> dec_ML_EE(const Vector<T>& r) const override {
4429 if constexpr (!FiniteFieldType<T>) {
4430 return LinearCode<T>::dec_ML_EE(r);
4431 } else {
4432 this->validate_length(r);
4433
4434 Vector<T> best = BaseCode.dec_ML_EE(r); // r - 0*w
4435 size_t best_t = 0;
4436 for (size_t j = 0; j < this->n; ++j) {
4437 if (!r[j].is_erased() && r[j] != best[j]) ++best_t;
4438 }
4439 if (best_t == 0) return best;
4440
4441 for (size_t i = 1; i < T::get_size(); ++i) {
4442 const T alpha = T(i);
4443
4444 const auto cp_est = BaseCode.dec_ML_EE(r - alpha * w);
4445 const auto c_est = cp_est + alpha * w;
4446
4447 size_t t = 0;
4448 for (size_t j = 0; j < this->n; ++j) {
4449 if (!r[j].is_erased() && r[j] != c_est[j]) ++t;
4450 }
4451
4452 if (t == 0) return c_est;
4453
4454 if (t < best_t) {
4455 best_t = t;
4456 best = std::move(c_est);
4457 }
4458 }
4459
4460 return best;
4461 }
4462 }
4463#endif
4464
4465 private:
4466 static Matrix<T> Augmented_G(const Matrix<T>& Gp, size_t j, const Vector<T>& w) {
4467 const size_t n = Gp.get_n();
4468 const size_t k = Gp.get_m();
4469
4470 if (w.get_n() != n) throw std::invalid_argument(std::string("Length of w must be ") + std::to_string(n));
4471
4472 if (Gp.get_m() == 1 && Gp.rank() == 0) return Matrix<T>(w);
4473
4474 if (j == 0) {
4475 const auto G = vertical_join(Matrix(w), Gp);
4476 return G;
4477 } else if (j == k) {
4478 const auto G = vertical_join(Gp, Matrix(w));
4479 return G;
4480 } else {
4481 const auto G =
4482 vertical_join(vertical_join(Gp.get_submatrix(0, 0, j, n), Matrix(w)), Gp.get_submatrix(j, 0, k - j, n));
4483 return G;
4484 }
4485 }
4486
4487 B BaseCode;
4488 size_t j;
4489 Vector<T> w;
4490};
4491
4492template <FieldType T>
4493auto dual(const T& C) {
4494 return C.get_dual();
4495}
4496
4497namespace details {
4498bool validate(const std::vector<size_t>& v, std::size_t n) {
4499 std::vector<bool> seen(n, false);
4500 for (auto it = v.begin(); it != v.end(); ++it) {
4501 if (*it >= n || seen[*it]) return false;
4502 seen[*it] = true;
4503 }
4504 return true;
4505}
4506} // namespace details
4507
4508template <class B>
4510
4511template <class C>
4512using field_t = typename base_t<C>::FIELD;
4513
4514template <class B>
4515auto extend(B&& base, size_t i, const Vector<field_t<B>>& v) {
4516 using D = base_t<B>;
4517 using T = field_t<B>;
4518 return ExtendedCode<T, D>(std::forward<B>(base), i, v);
4519}
4520
4521template <class B>
4522auto extend(B&& base) {
4523 using D = base_t<B>;
4524 using T = field_t<B>;
4525 return ExtendedCode<T, D>(std::forward<B>(base));
4526}
4527
4528// docu note: unextend maintains special structure, puncture does not
4529template <FieldType T, class B>
4531 return C.get_BaseCode();
4532}
4533
4534template <class B>
4535auto augment(B&& base, size_t j, const Vector<field_t<B>>& w) {
4536 using D = base_t<B>;
4537 using T = field_t<B>;
4538 return AugmentedCode<T, D>(std::forward<B>(base), j, w);
4539}
4540
4541// docu note: unaugment maintains special structure, expurgate does not
4542template <FieldType T, class B>
4544 return C.get_BaseCode();
4545}
4546
4547template <class B>
4548auto lengthen(B&& base, size_t j, const Vector<field_t<B>>& w, size_t i, const Vector<field_t<B>>& v) {
4549 using D = base_t<B>;
4550 using T = field_t<B>;
4551 return ExtendedCode<T, AugmentedCode<T, D>>(AugmentedCode<T, D>(std::forward<B>(base), j, w), i, v);
4552}
4553
4554template <class B>
4555auto lengthen(B&& base, size_t j, const Vector<field_t<B>>& w) {
4556 using D = base_t<B>;
4557 using T = field_t<B>;
4558 return ExtendedCode<T, AugmentedCode<T, D>>(AugmentedCode<T, D>(std::forward<B>(base), j, w));
4559}
4560
4561// docu note: unlengthen maintains special structure, shorten does not
4562template <FieldType T, class D>
4564 return C.get_BaseCode().get_BaseCode();
4565}
4566
4567template <FieldType T>
4568LinearCode<T> puncture(const LinearCode<T>& C, const std::vector<size_t>& v) {
4569 if (!details::validate(v, C.get_n())) throw std::invalid_argument("Invalid pattern for puncturing linear code!");
4570
4571 auto G = C.get_G();
4572 G.delete_columns(v);
4573 size_t rank;
4574 G.rref(&rank);
4575 G = G.get_submatrix(0, 0, rank, G.get_n());
4576 return LinearCode<T>(C.get_n() - v.size(), rank, std::move(G));
4577}
4578
4579template <FieldType T>
4580auto puncture(const EmptyCode<T>& C, const std::vector<size_t>& v) {
4581 if (v.empty()) return C;
4582 throw std::invalid_argument("Cannot puncture an empty code!");
4583}
4584
4585template <FieldType T>
4586auto puncture(const ZeroCode<T>& C, const std::vector<size_t>& v) {
4587 if (!details::validate(v, C.get_n())) throw std::invalid_argument("Invalid pattern for puncturing zero code!");
4588 return ZeroCode<T>(C.get_n() - v.size());
4589}
4590
4591template <FieldType T>
4592auto puncture(const UniverseCode<T>& C, const std::vector<size_t>& v) {
4593 if (!details::validate(v, C.get_n())) throw std::invalid_argument("Invalid pattern for puncturing universe code!");
4594 return UniverseCode<T>(C.get_n() - v.size());
4595}
4596
4597template <FieldType T>
4598auto puncture(const RepetitionCode<T>& C, const std::vector<size_t>& v) {
4599 if (!details::validate(v, C.get_n()))
4600 throw std::invalid_argument("Invalid pattern for puncturing repetition code!");
4601 return RepetitionCode<T>(C.get_n() - v.size());
4602}
4603
4604template <class C>
4607 return puncture(std::forward<C>(code), std::vector<size_t>{i});
4608}
4609
4610template <FieldType T>
4612 if (!details::validate(v, C.get_k())) throw std::invalid_argument("Invalid pattern for expurgating linear code!");
4613 if (C.get_k() == 0) return C;
4614
4615 auto G = C.get_G();
4616 G.delete_rows(v);
4617 return LinearCode<T>(C.get_n(), G.get_m(), std::move(G));
4618}
4619
4620template <FieldType T>
4621auto expurgate(const EmptyCode<T>& C, const std::vector<size_t>& v) {
4622 if (v.empty()) return C;
4623 throw std::invalid_argument("Cannot expurgate empty code!");
4624}
4625
4626template <FieldType T>
4627auto expurgate(const ZeroCode<T>& C, const std::vector<size_t>& v) {
4628 if (!details::validate(v, C.get_k()) || v.size() > 1 || v[0] != 0)
4629 throw std::invalid_argument("Invalid pattern for expurgating zero code!");
4630 return EmptyCode<T>(C.get_n());
4631}
4632
4633template <FieldType T>
4634auto expurgate(const RepetitionCode<T>& C, const std::vector<size_t>& v) {
4635 if (!details::validate(v, C.get_k()) || v.size() > 1 || v[0] != 0)
4636 throw std::invalid_argument("Invalid pattern for expurgating repetition code!");
4637 return EmptyCode<T>(C.get_n());
4638}
4639
4640template <class C>
4643 return expurgate(std::forward<C>(code), std::vector<size_t>{j});
4644}
4645
4646template <class B>
4648 return puncture(expurgate(std::forward<B>(base), j), i);
4649}
4650
4651template <FieldType L, FieldType R>
4652bool operator==(const LinearCode<L>& lhs, const LinearCode<R>& rhs) {
4653 return lhs.is_identical(rhs);
4654}
4655
4656template <FieldType L, FieldType R>
4657bool operator!=(const LinearCode<L>& lhs, const LinearCode<R>& rhs) {
4658 return !(lhs == rhs);
4659}
4660
4661template <FieldType L, FieldType R>
4662bool identical(const LinearCode<L>& lhs, const LinearCode<R>& rhs) {
4663 return lhs.is_identical(rhs);
4664}
4665
4666template <FieldType L, FieldType R>
4667bool equivalent(const LinearCode<L>& lhs, const LinearCode<R>& rhs) {
4668 return lhs.is_equivalent(rhs);
4669}
4670
4671template <FieldType T>
4673 rhs.get_info(os);
4674 return os;
4675}
4676
4677template <FieldType T>
4678class Enc {
4679 public:
4680 Enc(const Code<T>& C) : C(C) {}
4681
4682 Vector<T> operator()(const Vector<T>& in) const { return C.enc(in); }
4683
4684 private:
4685 const Code<T>& C;
4686};
4687
4709
4710template <FieldType T>
4711class Dec {
4712 public:
4713 explicit Dec(const Code<T>& C, method_t method = method_t::ML, size_t cache_limit = 10000)
4714 : C(C), method(method), cache_limit(cache_limit) {
4715 switch (method) {
4716 case method_t::BD:
4717 dec = [this](const Vector<T>& r) { return this->C.dec_BD(r); };
4718 break;
4720 dec = [this](const Vector<T>& r) { return this->C.dec_boosted_BD(r); };
4721 break;
4722 case method_t::ML:
4723 dec = [this](const Vector<T>& r) { return this->C.dec_ML(r); };
4724 break;
4725 case method_t::Viterbi:
4726 dec = [this](const Vector<T>& r) { return this->C.dec_Viterbi(r); };
4727 break;
4729 dec = [this](const Vector<T>& r) { return this->C.dec_recursive(r); };
4730 break;
4731 case method_t::Meggitt:
4732 dec = [this](const Vector<T>& r) { return this->C.dec_Meggitt(r); };
4733 break;
4734 case method_t::WBA:
4735 dec = [this](const Vector<T>& r) { return this->C.dec_WBA(r); };
4736 break;
4737 case method_t::BMA:
4738 dec = [this](const Vector<T>& r) { return this->C.dec_BMA(r); };
4739 break;
4740 case method_t::ML_soft:
4742 case method_t::BCJR:
4743 break;
4744#ifdef CECCO_ERASURE_SUPPORT
4745 case method_t::WBA_EE:
4746 dec = [this](const Vector<T>& r) { return this->C.dec_WBA_EE(r); };
4747 break;
4748 case method_t::BMA_EE:
4749 dec = [this](const Vector<T>& r) { return this->C.dec_BMA_EE(r); };
4750 break;
4751 case method_t::BD_EE:
4752 dec = [this](const Vector<T>& r) { return this->C.dec_BD_EE(r); };
4753 break;
4754 case method_t::ML_EE:
4755 dec = [this](const Vector<T>& r) { return this->C.dec_ML_EE(r); };
4756 break;
4757 case method_t::Viterbi_EE:
4758 dec = [this](const Vector<T>& r) { return this->C.dec_Viterbi_EE(r); };
4759 break;
4760 case method_t::recursive_EE:
4761 dec = [this](const Vector<T>& r) { return this->C.dec_recursive_EE(r); };
4762 break;
4763#endif
4764 default:
4765 break;
4766 }
4767 }
4768
4769 Vector<T> operator()(const Vector<T>& in) const {
4770 if (!dec) throw std::logic_error("Selected decoding method does not support hard-decision input!");
4771 return dec(in);
4772 }
4773
4774 Vector<T> operator()(const Vector<double>& in) const {
4775 if (method == method_t::Viterbi || method == method_t::Viterbi_soft) return C.dec_Viterbi_soft(in);
4776 if (method == method_t::BCJR) {
4777 const auto llrs = C.dec_BCJR(in);
4778 Vector<T> c_est(llrs.get_n());
4779 for (size_t i = 0; i < llrs.get_n(); ++i)
4780 if (llrs[i] < 0.0) c_est.set_component(i, T(1));
4781 return c_est;
4782 }
4783 return C.dec_ML_soft(in, cache_limit);
4784 }
4785
4786 private:
4787 const Code<T>& C;
4788 std::function<Vector<T>(const Vector<T>&)> dec;
4789 method_t method;
4790 size_t cache_limit;
4791};
4792
4793template <FiniteFieldType T>
4794class Encinv {
4795 public:
4796 Encinv(const Code<T>& C) : C(C) {}
4797
4798 Vector<T> operator()(const Vector<T>& in) const { return C.encinv(in); }
4799
4800 private:
4801 const Code<T>& C;
4802};
4803
4804} // namespace CECCO
4805
4806#endif
const Vector< T > & get_w() const noexcept
Definition codes.hpp:4309
AugmentedCode(B &&BaseCode, size_t j, const Vector< T > &w)
Definition codes.hpp:4295
AugmentedCode(AugmentedCode &&)=default
AugmentedCode & operator=(AugmentedCode &&)=default
AugmentedCode(const AugmentedCode &)=default
const B & get_BaseCode() const noexcept
Definition codes.hpp:4307
virtual void get_info(std::ostream &os) const override
Definition codes.hpp:4311
size_t get_j() const noexcept
Definition codes.hpp:4308
AugmentedCode(const B &BaseCode, size_t j, const Vector< T > &w)
Definition codes.hpp:4288
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:4327
AugmentedCode & operator=(const AugmentedCode &)=default
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:4354
virtual Vector< T > dec_WBA(const Vector< T > &r) const
Definition codes.hpp:182
virtual Vector< T > encinv(const Vector< T > &c) const
Definition codes.hpp:149
virtual Vector< T > dec_boosted_BD(const Vector< T > &r) const
Definition codes.hpp:155
Code(Code &&)=default
Code & operator=(const Code &other)
Definition codes.hpp:136
Code(const Code &other)
Definition codes.hpp:130
virtual Vector< T > dec_ML_soft(const Vector< double > &llrs, size_t cache_size) const
Definition codes.hpp:164
Code(size_t n)
Definition codes.hpp:128
virtual Vector< T > dec_Viterbi_soft(const Vector< double > &llrs, const std::string &filename="") const
Definition codes.hpp:167
virtual Vector< T > dec_BMA(const Vector< T > &r) const
Definition codes.hpp:185
virtual Vector< T > dec_Meggitt(const Vector< T > &r) const
Definition codes.hpp:179
virtual Vector< double > dec_BCJR(const Vector< double > &llrs, const std::string &filename="") const
Definition codes.hpp:170
size_t n
Definition codes.hpp:210
virtual void get_info(std::ostream &os) const
Definition codes.hpp:147
virtual Vector< T > dec_BD(const Vector< T > &r) const
Definition codes.hpp:152
Code & operator=(Code &&)=default
virtual Vector< T > dec_recursive(const Vector< T > &r) const
Definition codes.hpp:176
virtual Vector< T > dec_burst(const Vector< T > &r) const
Definition codes.hpp:173
virtual ~Code()=default
virtual Vector< T > dec_ML(const Vector< T > &r) const
Definition codes.hpp:158
virtual Vector< T > dec_Viterbi(const Vector< T > &r, const std::string &filename="") const
Definition codes.hpp:161
size_t get_n() const noexcept
Definition codes.hpp:145
virtual Vector< T > enc(const Vector< T > &u) const
Definition codes.hpp:148
Vector< T > value_type
Definition codes.hpp:267
std::forward_iterator_tag iterator_category
Definition codes.hpp:266
const Vector< T > * pointer
Definition codes.hpp:269
std::ptrdiff_t difference_type
Definition codes.hpp:268
friend bool operator==(const CodewordIterator &a, const CodewordIterator &b)
Definition codes.hpp:261
CodewordIterator & operator++()
Definition codes.hpp:286
const Vector< T > & reference
Definition codes.hpp:270
CodewordIterator(const LinearCode< T > &C, InfInt s)
Definition codes.hpp:272
friend bool operator!=(const CodewordIterator &a, const CodewordIterator &b)
Definition codes.hpp:262
const Vector< T > & operator*() const noexcept
Definition codes.hpp:284
CordaroWagnerCode(const LinearCode< Fp< 2 > > &C)
Definition codes.hpp:3283
CordaroWagnerCode(const CordaroWagnerCode &)=default
CordaroWagnerCode & operator=(const CordaroWagnerCode &)=default
size_t get_r() const noexcept
Definition codes.hpp:3308
virtual void get_info(std::ostream &os) const override
Definition codes.hpp:3311
int8_t get_m() const noexcept
Definition codes.hpp:3309
Vector< Fp< 2 > > dec_ML(const Vector< Fp< 2 > > &r) const override
Definition codes.hpp:3334
CordaroWagnerCode & operator=(CordaroWagnerCode &&)=default
CordaroWagnerCode(size_t r, int8_t m)
Definition codes.hpp:3271
CordaroWagnerCode get_equivalent_code_in_standard_form() const
Definition codes.hpp:3304
Vector< Fp< 2 > > dec_BD(const Vector< Fp< 2 > > &r) const override
Definition codes.hpp:3321
CordaroWagnerCode(CordaroWagnerCode &&)=default
Vector< T > operator()(const Vector< T > &in) const
Definition codes.hpp:4769
Vector< T > operator()(const Vector< double > &in) const
Definition codes.hpp:4774
Dec(const Code< T > &C, method_t method=method_t::ML, size_t cache_limit=10000)
Definition codes.hpp:4713
EmptyCode(const EmptyCode &)=default
EmptyCode(size_t n)
Definition codes.hpp:225
EmptyCode & operator=(EmptyCode &&)=default
void get_info(std::ostream &os) const override
Definition codes.hpp:232
EmptyCode & operator=(const EmptyCode &)=default
EmptyCode(EmptyCode &&)=default
Encinv(const Code< T > &C)
Definition codes.hpp:4796
Vector< T > operator()(const Vector< T > &in) const
Definition codes.hpp:4798
const Polynomial< InfInt > & get_weight_enumerator() const override
Definition codes.hpp:4147
ExtendedCode & operator=(ExtendedCode &&)=default
ExtendedCode(B &&BaseCode, size_t i, const Vector< T > &v)
Definition codes.hpp:4122
Vector< T > dec_BD_EE(const Vector< T > &r) const override
Definition codes.hpp:4203
const B & get_BaseCode() const noexcept
Definition codes.hpp:4145
ExtendedCode(const B &BaseCode)
Definition codes.hpp:4131
ExtendedCode(const ExtendedCode &)=default
virtual Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:4188
size_t get_i() const noexcept
Definition codes.hpp:4144
ExtendedCode(ExtendedCode &&)=default
ExtendedCode & operator=(const ExtendedCode &)=default
ExtendedCode(const B &BaseCode, size_t i, const Vector< T > &v)
Definition codes.hpp:4111
virtual Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:4195
virtual void get_info(std::ostream &os) const override
Definition codes.hpp:4170
Prime field 𝔽_p ≅ ℤ/pℤ
Definition fields.hpp:1647
GRSCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2931
GRSCode(GRSCode &&other)
Definition codes.hpp:2899
const Vector< T > & get_a() const noexcept
Definition codes.hpp:2928
GRSCode(const Vector< T > &a, const Vector< T > &d, size_t k, Matrix< T > G)
Definition codes.hpp:3006
bool is_singly_extended() const
Definition codes.hpp:2941
GRSCode(const Vector< T > &a, const Vector< T > &d, size_t k)
Definition codes.hpp:2861
bool is_narrow_sense() const
Definition codes.hpp:2961
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2993
const Vector< T > & get_d() const noexcept
Definition codes.hpp:2929
Vector< T > dec_WBA(const Vector< T > &r) const override
Definition codes.hpp:2994
GRSCode & operator=(const GRSCode &other)
Definition codes.hpp:2906
virtual void get_info(std::ostream &os) const override
Definition codes.hpp:2970
bool is_normalized() const
Definition codes.hpp:2968
GRSCode & operator=(GRSCode &&other)
Definition codes.hpp:2917
Vector< T > dec_BMA(const Vector< T > &r) const override
Definition codes.hpp:2995
GRSCode(const GRSCode &other)
Definition codes.hpp:2892
bool is_primitive() const
Definition codes.hpp:2948
GolayCode & operator=(GolayCode &&)=default
GolayCode(const GolayCode &)=default
GolayCode & operator=(const GolayCode &)=default
GolayCode(const LinearCode< T > &C)
Definition codes.hpp:2792
GolayCode(GolayCode &&)=default
GoppaCode(const Vector< SUPER > &a, Polynomial< SUPER > g)
Definition codes.hpp:3957
GoppaCode(const GoppaCode &)=default
size_t get_delta() const noexcept override
Definition codes.hpp:3973
Vector< SUB > dec_Patterson(const Vector< SUB > &r) const
Definition codes.hpp:3991
GoppaCode(GoppaCode &&)=default
GoppaCode & operator=(const GoppaCode &)=default
bool is_squarefree() const noexcept
Definition codes.hpp:3971
GoppaCode & operator=(GoppaCode &&)=default
const Polynomial< SUPER > & get_g() const noexcept
Definition codes.hpp:3970
void get_info(std::ostream &os) const override
Definition codes.hpp:3979
GoppaCode(const Vector< SUPER > &a, size_t delta)
Definition codes.hpp:3952
void get_info(std::ostream &os) const override
Definition codes.hpp:2242
HammingCode(HammingCode &&)=default
HammingCode(const HammingCode &)=default
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2252
HammingCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2236
HammingCode & operator=(const HammingCode &)=default
HammingCode & operator=(HammingCode &&)=default
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:2275
HammingCode(const LinearCode< T > &C)
Definition codes.hpp:2194
size_t get_s() const noexcept
Definition codes.hpp:2240
HammingCode(size_t s)
Definition codes.hpp:2186
SimplexCode< T > get_dual() const noexcept
Definition codes.hpp:2250
LDCCode & operator=(LDCCode &&)=default
const BU & get_U() const noexcept
Definition codes.hpp:3467
LDCCode(LDCCode &&)=default
virtual size_t get_dmin() const override
Definition codes.hpp:3470
virtual void get_info(std::ostream &os) const override
Definition codes.hpp:3475
const BV & get_V() const noexcept
Definition codes.hpp:3468
LDCCode(const BU &U, const BV &V)
Definition codes.hpp:3456
LDCCode & operator=(const LDCCode &)=default
Vector< T > dec_recursive(const Vector< T > &r) const override
Definition codes.hpp:3492
LDCCode(const LDCCode &)=default
Matrix< T > HT
Definition codes.hpp:1835
double get_R() const noexcept
Definition codes.hpp:487
LinearCode(const LinearCode &other)
Definition codes.hpp:388
LinearCode(size_t n, size_t k, const Matrix< T > &X)
Definition codes.hpp:328
Matrix< T > G
Definition codes.hpp:1834
details::OnceCache< bool > polynomial
Definition codes.hpp:1849
LinearCode(size_t k, Polynomial< T > gamma)
Definition codes.hpp:378
std::vector< size_t > infoset
Definition codes.hpp:1837
size_t get_k() const noexcept
Definition codes.hpp:486
InfInt get_size() const
Definition codes.hpp:489
LinearCode & operator=(const LinearCode &other)
Definition codes.hpp:434
details::OnceCache< Polynomial< InfInt > > weight_enumerator
Definition codes.hpp:1839
details::OnceCache< std::vector< bool > > tainted_burst
Definition codes.hpp:1843
LinearCode(LinearCode &&other)
Definition codes.hpp:411
details::OnceCache< Trellis< T > > minimal_trellis
Definition codes.hpp:1851
details::OnceCache< std::vector< bool > > tainted
Definition codes.hpp:1842
details::OnceCache< std::vector< Vector< T > > > standard_array
Definition codes.hpp:1841
details::OnceCache< size_t > dmin
Definition codes.hpp:1838
details::OnceCache< std::vector< Vector< T > > > codewords
Definition codes.hpp:1840
LinearCode & operator=(LinearCode &&other)
Definition codes.hpp:460
Matrix< T > MI
Definition codes.hpp:1836
details::OnceCache< Polynomial< T > > gamma
Definition codes.hpp:1850
Dense m × n matrix over a CECCO::ComponentType.
Definition matrices.hpp:174
Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a CECCO::ComponentType.
Fp< 2 > T
Definition codes.hpp:3576
void get_info(std::ostream &os) const override
Definition codes.hpp:3592
RMCode & operator=(const RMCode &)=default
RMCode(RMCode &&)=default
size_t get_r() const noexcept
Definition codes.hpp:3589
size_t get_m() const noexcept
Definition codes.hpp:3590
RMCode & operator=(RMCode &&)=default
RMCode(const RMCode &)=default
RMCode(size_t r, size_t m)
Definition codes.hpp:3578
Vector< T > dec_recursive(const Vector< T > &r) const override
Definition codes.hpp:3605
RSCode(const T &alpha, size_t b, size_t k)
Definition codes.hpp:3179
RSCode< T > get_identical_code_in_polynomial_form() const
Definition codes.hpp:3201
RSCode & operator=(RSCode &&)=default
T get_alpha() const noexcept
Definition codes.hpp:3189
void get_info(std::ostream &os) const override
Definition codes.hpp:3211
RSCode(RSCode &&)=default
RSCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:3192
RSCode(const RSCode &)=default
size_t get_b() const noexcept
Definition codes.hpp:3190
RSCode & operator=(const RSCode &)=default
RepetitionCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2560
RepetitionCode< T > get_identical_code_in_polynomial_form() const
Definition codes.hpp:2561
RepetitionCode & operator=(RepetitionCode &&)=default
RepetitionCode & operator=(const RepetitionCode &)=default
RepetitionCode(const RepetitionCode &)=default
Vector< T > encinv(const Vector< T > &c) const override
Definition codes.hpp:2575
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:2591
SingleParityCheckCode< T > get_dual() const noexcept
Definition codes.hpp:2573
void get_info(std::ostream &os) const override
Definition codes.hpp:2565
RepetitionCode(RepetitionCode &&)=default
RepetitionCode(size_t n)
Definition codes.hpp:2536
Vector< T > enc(const Vector< T > &u) const override
Definition codes.hpp:2574
RepetitionCode(const LinearCode< T > &C)
Definition codes.hpp:2550
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2577
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:2421
SimplexCode(const LinearCode< T > &C)
Definition codes.hpp:2372
size_t get_s() const noexcept
Definition codes.hpp:2397
SimplexCode(SimplexCode &&)=default
SimplexCode(const SimplexCode &)=default
SimplexCode & operator=(const SimplexCode &)=default
SimplexCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2393
void get_info(std::ostream &os) const override
Definition codes.hpp:2399
HammingCode< T > get_dual() const noexcept
Definition codes.hpp:2407
SimplexCode(size_t s)
Definition codes.hpp:2360
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2408
SimplexCode & operator=(SimplexCode &&)=default
SingleParityCheckCode(SingleParityCheckCode &&)=default
SingleParityCheckCode(const SingleParityCheckCode &)=default
SingleParityCheckCode & operator=(const SingleParityCheckCode &)=default
SingleParityCheckCode & operator=(SingleParityCheckCode &&)=default
void get_info(std::ostream &os) const override
Definition codes.hpp:2703
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2712
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:2723
SingleParityCheckCode(const LinearCode< T > &C)
Definition codes.hpp:2680
SingleParityCheckCode< T > get_identical_code_in_polynomial_form() const
Definition codes.hpp:2699
RepetitionCode< T > get_dual() const noexcept
Definition codes.hpp:2711
SingleParityCheckCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2694
UniverseCode(size_t n)
Definition codes.hpp:2081
UniverseCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2096
Vector< T > dec_burst(const Vector< T > &r) const override
Definition codes.hpp:2127
UniverseCode & operator=(const UniverseCode &)=default
UniverseCode(const UniverseCode &)=default
Vector< T > encinv(const Vector< T > &c) const override
Definition codes.hpp:2108
Vector< T > dec_ML(const Vector< T > &r) const override
Definition codes.hpp:2119
void get_info(std::ostream &os) const override
Definition codes.hpp:2098
UniverseCode(const LinearCode< T > &C)
Definition codes.hpp:2087
UniverseCode & operator=(UniverseCode &&)=default
Vector< T > enc(const Vector< T > &u) const override
Definition codes.hpp:2107
UniverseCode(UniverseCode &&)=default
Vector< T > dec_BD(const Vector< T > &r) const override
Definition codes.hpp:2110
ZeroCode< T > get_dual() const noexcept
Definition codes.hpp:2106
Vector v = (v₀, v₁, …, vₙ₋₁) over a CECCO::ComponentType.
Definition vectors.hpp:115
ZeroCode(const LinearCode< T > &C)
Definition codes.hpp:2161
ZeroCode & operator=(const ZeroCode &)=default
ZeroCode(ZeroCode &&)=default
void get_info(std::ostream &os) const override
Definition codes.hpp:2172
ZeroCode & operator=(ZeroCode &&)=default
ZeroCode< T > get_equivalent_code_in_standard_form() const
Definition codes.hpp:2170
ZeroCode(size_t n)
Definition codes.hpp:2159
ZeroCode(const ZeroCode &)=default
const char * what() const noexcept override
Definition codes.hpp:313
decoding_failure(const std::string &message)
Definition codes.hpp:312
Thread-safe single-value cache.
Definition helpers.hpp:512
#define BOLD(x)
Definition codes.hpp:57
Contains implementation details not to be exposed to the user. Functions and classes here may change ...
Definition blocks.hpp:59
Matrix< T > LDC_G(const Matrix< T > &G_U, const Matrix< T > &G_V)
Definition codes.hpp:3432
const int index
Definition codes.hpp:101
bool validate(const std::vector< size_t > &v, std::size_t n)
Definition codes.hpp:4498
Provides a framework for error correcting codes.
Definition blocks.hpp:57
std::ostream & showall(std::ostream &os)
Definition codes.hpp:115
std::mt19937 & gen()
Current thread's random number generator.
Definition helpers.hpp:127
auto unlengthen(const ExtendedCode< T, AugmentedCode< T, D > > &C)
Definition codes.hpp:4563
auto puncture(const EmptyCode< T > &C, const std::vector< size_t > &v)
Definition codes.hpp:4580
std::ostream & showspecial(std::ostream &os)
Definition codes.hpp:120
auto lengthen(B &&base, size_t j, const Vector< field_t< B > > &w)
Definition codes.hpp:4555
SubfieldSubcode(const B &) -> SubfieldSubcode< B >
std::ostream & showmost(std::ostream &os)
Definition codes.hpp:110
std::ostream & showbasic(std::ostream &os)
Definition codes.hpp:105
auto puncture(const ZeroCode< T > &C, const std::vector< size_t > &v)
Definition codes.hpp:4586
auto extend(B &&base)
Definition codes.hpp:4522
auto augment(B &&base, size_t j, const Vector< field_t< B > > &w)
Definition codes.hpp:4535
Polynomial< InfInt > MacWilliamsIdentity(const Polynomial< InfInt > &A, size_t n, size_t k)
Definition codes.hpp:62
LinearCode< T > puncture(const LinearCode< T > &C, const std::vector< size_t > &v)
Definition codes.hpp:4568
SubfieldSubcode(B &&) -> SubfieldSubcode< B >
auto dual(const T &C)
Definition codes.hpp:4493
auto extend(B &&base, size_t i, const Vector< field_t< B > > &v)
Definition codes.hpp:4515
auto puncture(const UniverseCode< T > &C, const std::vector< size_t > &v)
Definition codes.hpp:4592
B unextend(const ExtendedCode< T, B > &C)
Definition codes.hpp:4530
B unaugment(const AugmentedCode< T, B > &C)
Definition codes.hpp:4543
auto lengthen(B &&base, size_t j, const Vector< field_t< B > > &w, size_t i, const Vector< field_t< B > > &v)
Definition codes.hpp:4548
auto puncture(const RepetitionCode< T > &C, const std::vector< size_t > &v)
Definition codes.hpp:4598
Trellis with field-labelled edges between consecutive layers.