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
vectors.hpp
Go to the documentation of this file.
1/**
2 * @file vectors.hpp
3 * @brief Vector arithmetic library
4 * @author Christian Senger <senger@inue.uni-stuttgart.de>
5 * @version 2.1.11
6 * @date 2026
7 *
8 * @copyright
9 * Copyright (c) 2026, Christian Senger <senger@inue.uni-stuttgart.de>
10 *
11 * Licensed for noncommercial use only, including academic teaching, research, and personal non-profit purposes.
12 * Commercial use is prohibited without a separate commercial license. See the [LICENSE](../../LICENSE) file in the
13 * repository root for full terms and how to request a commercial license.
14 *
15 * @section Description
16 *
17 * Vector arithmetic for error-control coding over any @ref CECCO::ComponentType (finite fields,
18 * floating-point, complex, signed integers including `InfInt`). Supports safe cross-field
19 * conversions via @ref CECCO::SubfieldOf / @ref CECCO::ExtensionOf / @ref CECCO::largest_common_subfield_t,
20 * bidirectional Vector ↔ Matrix integration, and lazy O(1) caching of Hamming weight / burst length.
21 *
22 * @section Usage_Example
23 *
24 * @code{.cpp}
25 * // Basic vector operations
26 * Vector<int> u = {1, 2, 3, 4};
27 * Vector<int> v(4, 5); // Vector of length 4, all components = 5
28 * auto w = u + v; // Element-wise addition
29 * int dot = inner_product(u, v); // Inner product
30 *
31 * // Finite field vectors
32 * using F4 = Ext<Fp<2>, MOD{1, 1, 1}>;
33 * Vector<F4> x = {0, 1, 2, 3};
34 * size_t weight = x.wH(); // Hamming weight
35 * size_t distance = dH(x, Vector<F4>(4)); // Hamming distance to zero vector
36 *
37 * // Cross-field upcast (𝔽_2 ⊆ 𝔽_4 via construction tower)
38 * Vector<Fp<2>> y = {1, 0, 1, 1};
39 * Vector<F4> z(y);
40 * @endcode
41 *
42 * @see @ref fields.hpp, @ref matrices.hpp, @ref field_concepts_traits.hpp
43 */
44
45#ifndef VECTORS_HPP
46#define VECTORS_HPP
47
48#include <initializer_list>
49#include <numeric>
50
51#include "matrices.hpp"
52
53/*
54//transitive
55#include <algorithm>
56#include <complex>
57#include <concepts>
58#include <functional>
59#include <iostream>
60#include <iterator>
61#include <limits>
62#include <random>
63#include <ranges>
64#include <set>
65#include <stdexcept>
66#include <string>
67#include <type_traits>
68#include <unordered_map>
69#include <utility>
70#include <vector>
71
72#include "InfInt.hpp"
73#include "field_concepts_traits.hpp"
74#include "helpers.hpp"
75*/
76
77namespace CECCO {
78
79template <ComponentType T>
80class Vector;
81template <CoefficientType T>
82class Polynomial;
83template <ComponentType T>
84class Matrix;
85
86template <ComponentType T>
87T inner_product(const Vector<T>& lhs, const Vector<T>& rhs);
88template <ComponentType T>
89Vector<T> Schur_product(const Vector<T>& lhs, const Vector<T>& rhs);
90template <ComponentType T>
91Vector<T> unit_vector(size_t length, size_t i);
92template <ComponentType T>
93std::ostream& operator<<(std::ostream& os, const Vector<T>& rhs);
94double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs);
95
96/**
97 * @brief Vector v = (v₀, v₁, …, vₙ₋₁) over a @ref CECCO::ComponentType
98 *
99 * @tparam T Component type satisfying @ref CECCO::ComponentType (finite field, `double`,
100 * `std::complex<double>`, or signed integer including `InfInt`)
101 *
102 * Components are stored contiguously; length is fixed except via the explicit resizers
103 * (@ref pad_front, @ref pad_back, @ref append, @ref prepend, @ref delete_components, …).
104 * Dimension mismatches in arithmetic throw `std::invalid_argument`. Hamming weight is
105 * computed lazily and cached; the cache is invalidated on any mutation. Methods that
106 * compare against zero (@ref wH, @ref supp, @ref burst_length, …) are gated by
107 * `requires ReliablyComparableType<T>`.
108 *
109 * Cross-field constructors and assignment operators between two finite fields of the same
110 * characteristic route through @ref CECCO::details::largest_common_subfield_t, so vectors
111 * over fields from disjoint construction towers can interoperate.
112 *
113 * @section Usage_Example
114 *
115 * @code{.cpp}
116 * using F4 = Ext<Fp<2>, MOD{1, 1, 1}>;
117 * Vector<F4> v = {0, 1, 2, 3};
118 * size_t weight = v.wH(); // Hamming weight (cached)
119 * size_t burst = v.burst_length();
120 * auto M = v.as_matrix<Fp<2>>(); // 2 × 4 matrix over 𝔽₂ (𝔽₂ ⊆ 𝔽₄)
121 * @endcode
122 */
123template <ComponentType T>
124class Vector {
125 template <ReliablyComparableType U>
126 friend constexpr bool operator==(const Vector<U>& lhs, const Vector<U>& rhs);
127 friend T inner_product<>(const Vector<T>& lhs, const Vector<T>& rhs);
128 friend Vector<T> Schur_product<>(const Vector<T>& lhs, const Vector<T>& rhs);
129 friend Vector unit_vector<>(size_t length, size_t i);
130 friend std::ostream& operator<< <>(std::ostream& os, const Vector& rhs);
131 friend double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs);
132 friend class Matrix<T>;
133 template <CoefficientType U>
134 friend class Polynomial;
135
136 public:
137 // Cache configuration for this class
138 enum CacheIds { Weight = 0 };
139
140 /** @name Constructors
141 * @{
142 */
143
144 /// @brief Default constructor: empty vector (length 0)
145 constexpr Vector() noexcept = default;
146
147 /// @brief Length-`n` vector with zero-initialised components
148 explicit Vector(size_t n) : data(n, T(0)) {
149 if constexpr (ReliablyComparableType<T>) cache.template set<Weight>(0);
150 }
151
152 /// @brief Length-`n` vector with every component equal to `l`
153 Vector(size_t n, const T& l);
154
155 /// @brief From an initializer list of components, in order
156 Vector(const std::initializer_list<T>& l) : data(l) {}
157
158 Vector(const Vector& other);
159 constexpr Vector(Vector&& other) noexcept;
160
161 /**
162 * @brief From a matrix over a subfield: each column becomes one T component
163 *
164 * @tparam S Subfield of T (in the construction tower)
165 * @throws std::invalid_argument if `mat.get_m()` does not match `[T:S]` (the extension
166 * degree of T over S)
167 */
168 template <FiniteFieldType S>
169 Vector(const Matrix<S>& mat)
171
172 /**
173 * @brief Cross-field conversion between two finite fields of the same characteristic
174 *
175 * @tparam S Source field type (`Vector<S>`); must share characteristic with T
176 *
177 * Converts component by component via T's cross-field constructor, which routes through
178 * @ref CECCO::details::largest_common_subfield_t and so handles disjoint construction towers.
179 * Propagates `std::invalid_argument` if any component is not representable in T.
180 */
181 template <FiniteFieldType S>
183 Vector(const Vector<S>& other);
184
185 /**
186 * @brief From polynomial coefficients: component `i` becomes `poly[i]`
187 *
188 * Resulting length is `poly.degree() + 1`, or 1 for the zero polynomial. For cross-field
189 * conversion, convert the polynomial first.
190 *
191 * @note Member template with U fixed to T, so that instantiating a vector over polynomial
192 * components does not form the unsupported type `Polynomial<Polynomial<...>>`.
193 */
194 template <CoefficientType U>
196 Vector(const Polynomial<U>& poly);
197
198 /** @} */
199
200 /** @name Assignment Operators
201 * @{
202 */
203
204 constexpr Vector& operator=(const Vector& rhs);
205 constexpr Vector& operator=(Vector&& rhs) noexcept;
206
207 /// @brief Set every component to `rhs`
208 constexpr Vector& operator=(const T& rhs);
209
210 /// @brief Cross-field assignment (same semantics as the cross-field constructor)
211 template <FiniteFieldType S>
214
215 /** @} */
216
217 /** @name Unary Arithmetic Operations
218 * @{
219 */
220
221 /// @brief Unary `+` (lvalue): returns a copy
222 constexpr Vector operator+() const& { return *this; }
223 /// @brief Unary `+` (rvalue): returns the rvalue itself
224 constexpr Vector operator+() && noexcept { return std::move(*this); }
225
226 /// @brief Unary `−` (lvalue): returns a new vector with each component negated
227 constexpr Vector operator-() const&;
228 /// @brief Unary `−` (rvalue): negates components in place
229 constexpr Vector operator-() &&;
230
231 /** @} */
232
233 /** @name Compound Assignment Operations
234 * @{
235 */
236
237 /**
238 * @brief Component-wise addition `this[i] += rhs[i]`
239 *
240 * @throws std::invalid_argument if `this->get_n() != rhs.get_n()`
241 */
242 Vector& operator+=(const Vector& rhs);
243
244 /**
245 * @brief Component-wise subtraction `this[i] -= rhs[i]`
246 *
247 * @throws std::invalid_argument if `this->get_n() != rhs.get_n()`
248 */
249 Vector& operator-=(const Vector& rhs);
250
251 /// @brief Multiply every component by the scalar `s`
252 constexpr Vector& operator*=(const T& s);
253
254 /**
255 * @brief Divide every component by the scalar `s`
256 *
257 * @throws std::invalid_argument if `s == T(0)`
258 * @note Round-trip `(v / s) * s == v` is only guaranteed when T satisfies @ref CECCO::FieldType
259 * (otherwise integer rounding may corrupt components).
260 */
261 Vector& operator/=(const T& s)
263
264 /** @} */
265
266 /** @name Randomization
267 * @{
268 */
269
270 /**
271 * @brief Replace components with random values appropriate for T
272 *
273 * Distribution per component: finite-field types draw uniformly from the field; signed
274 * integers from [−100, 100]; `double` and the parts of `std::complex<double>` from [−1, 1].
275 *
276 * @note Unavailable for polynomial components: randomizing a polynomial needs a degree,
277 * see @ref CECCO::Polynomial::randomize.
278 */
281
282 /// @brief Like @ref randomize but every component is guaranteed non-zero
285
286 /**
287 * @brief Fill with pairwise distinct field elements (shuffled labels 0, …, q − 1)
288 *
289 * @throws std::invalid_argument if the vector length exceeds the field cardinality q
290 */
293
294 /** @} */
295
296 /** @name Information and Properties
297 * @{
298 */
299
300 /// @brief Vector length (number of components)
301 constexpr size_t get_n() const noexcept { return data.size(); }
302
303 /// @brief True iff length is 0 (distinct from a non-empty all-zero vector)
304 constexpr bool is_empty() const noexcept { return data.empty(); }
305
306 /// @brief True iff every component equals T(0); also true for the empty vector
307 constexpr bool is_zero() const
309
310 /// @brief True iff no two components are equal
311 constexpr bool is_pairwise_distinct() const
313
314 /**
315 * @brief Sorted indices of non-zero, non-erased components (empty vector for an input with only zeros and erasures)
316 *
317 * @throws std::invalid_argument if `*this` is empty (length 0)
318 */
321 {
322 if (data.empty()) throw std::invalid_argument("Support of an empty (length zero) vector is undefined!");
323 const T zero(0);
325 for (size_t i = 0; i < data.size(); ++i) {
326#ifdef CECCO_ERASURE_SUPPORT
327 if constexpr (FieldType<T>)
328 if (data[i].is_erased()) continue;
329#endif
330 if (data[i] != zero) supp.push_back(i);
331 }
332 return supp;
333 }
334
335 /// @brief Hamming weight: number of non-zero, non-erased components; cached on first call
336 size_t wH() const
338 {
339 return cache.template get_or_compute<Weight>([this] { return calculate_weight(); });
340 }
341
342 /// @brief Burst length R − L + 1, where L, R are the first and last non-zero indices; 0 for an all-zero or empty
343 /// vector
344 constexpr size_t burst_length() const
346
347 /// @brief Cyclic burst length: shortest circular arc covering all non-zero positions; 0 for an all-zero or empty
348 /// vector
349 constexpr size_t cyclic_burst_length() const
351
352 /** @} */
353
354 /** @name Component Access and Manipulation
355 * @{
356 */
357
358 /**
359 * @brief Set component `i` by perfect-forwarding `c`
360 *
361 * @throws std::invalid_argument if `i` is out of bounds
362 */
363 template <typename U>
364 Vector& set_component(size_t i, U&& c)
366
367 /**
368 * @brief Read access to component `i`
369 *
370 * @throws std::invalid_argument if `i` is out of bounds
371 */
372 const T& operator[](size_t i) const;
373
374 /**
375 * @brief Extract the contiguous subvector `[i, i + w)`
376 *
377 * @throws std::invalid_argument if `[i, i + w)` exceeds the vector
378 */
379 Vector get_subvector(size_t i, size_t w) const&;
380
381 /// @brief In-place rvalue overload of @ref get_subvector (truncates to `[i, i + w)`)
382 Vector& get_subvector(size_t i, size_t w) &&;
383
384 /**
385 * @brief Overwrite components `[i, i + v.get_n())` with `v`
386 *
387 * @throws std::invalid_argument if the replacement region exceeds the vector
388 */
389 Vector& set_subvector(const Vector& v, size_t i);
390
391 /// @brief Rvalue overload of @ref set_subvector (move-assigns components from `v`)
392 Vector& set_subvector(Vector&& v, size_t i);
393
394 /// @brief Append `rhs`: result becomes `[*this | rhs]`
395 Vector& append(const Vector& rhs);
396 /// @brief Rvalue overload of @ref append (moves components from `rhs`)
397 Vector& append(Vector&& rhs);
398
399 /// @brief Append after converting `rhs` to `Vector<T>`
400 template <typename U>
401 Vector& append(U&& rhs)
403
404 /// @brief Prepend `rhs`: result becomes `[rhs | *this]`
405 Vector& prepend(const Vector& rhs);
406 /// @brief Rvalue overload of @ref prepend (moves components from `rhs`)
407 Vector& prepend(Vector&& rhs);
408
409 /// @brief Prepend after converting `lhs` to `Vector<T>`
410 template <typename U>
411 Vector& prepend(U&& lhs)
413
414 /**
415 * @brief Delete the components whose indices appear in `v` and compact
416 *
417 * @param v Indices (deduplicated internally)
418 * @throws std::invalid_argument if any index in `v` is out of bounds
419 */
420 Vector& delete_components(const std::vector<size_t>& v);
421
422 /**
423 * @brief Delete component `i` (single-index convenience for @ref delete_components)
424 *
425 * @throws std::invalid_argument if `i` is out of bounds
426 */
427 Vector& delete_component(size_t i) { return delete_components({i}); }
428
429#ifdef CECCO_ERASURE_SUPPORT
430
431 /**
432 * @brief Mark every component whose index appears in `v` as erased
433 *
434 * @param v Indices (deduplicated internally)
435 * @throws std::invalid_argument if any index in `v` is out of bounds
436 *
437 * @warning Erased components must not participate in field arithmetic, see
438 * @ref CECCO_ERASURE_SUPPORT.
439 */
442
443 /// @brief Erase component `i` (single-index convenience for @ref erase_components)
449
450 /**
451 * @brief Clear the erasure flag on every component whose index appears in `v` (resets to T(0))
452 *
453 * @param v Indices (deduplicated internally)
454 * @throws std::invalid_argument if any index in `v` is out of bounds
455 */
458
459 /// @brief Un-erase component `i` (single-index convenience for @ref unerase_components)
465
466#endif
467
468 /// @brief Prepend zeros so the vector has length at least `n` (no-op if already ≥ `n`)
469 Vector& pad_front(size_t n);
470
471 /// @brief Append zeros so the vector has length at least `n` (no-op if already ≥ `n`)
472 Vector& pad_back(size_t n);
473
474 /** @} */
475
476 /** @name Transformations
477 * @{
478 */
479
480 /** @brief Circular left shift by `i` positions: `j ↦ (j − i) mod n`. */
481 constexpr Vector& rotate_left(size_t i);
482
483 /** @brief Circular right shift by `i` positions: `j ↦ (j + i) mod n`. */
484 constexpr Vector& rotate_right(size_t i);
485
486 /** @brief Reverse component order: `i ↦ n − 1 − i`. */
487 constexpr Vector& reverse();
488
489 /** @brief Set every component to `value`. */
490 constexpr Vector& fill(const T& value);
491
492 /** @} */
493
494 /** @name Finite Field Specific Operations
495 * @{
496 */
497
498 /**
499 * @brief Interpret as a base-q integer, where q = T::get_size()
500 *
501 * Component `data[n − 1 − i]` is the i-th digit (least-significant first), so the value
502 * is Σ data[n − 1 − i] · qⁱ.
503 *
504 * @note May overflow `size_t` for large vectors over large fields.
505 * @throws std::invalid_argument if any component is erased.
506 */
507 size_t as_integer() const
509
510 /**
511 * @brief Replace `*this` with the length-`n` base-q encoding of `value`
512 *
513 * Left inverse of @ref as_integer: `v.from_integer(v.as_integer(), v.get_n()) == v`.
514 *
515 * @throws std::out_of_range if `value` does not fit into n base-q digits
516 */
517 Vector& from_integer(size_t value, size_t n)
519
520 /**
521 * @brief Expand each component into its S-coefficient column
522 *
523 * @tparam S Subfield of T
524 * @return [T:S] × `get_n()` matrix whose column j is `data[j].as_vector<S>()`
525 *
526 * @note Under @ref CECCO_ERASURE_SUPPORT, an erased component j produces an entirely
527 * erased column j.
528 */
529 template <FiniteFieldType S>
530 Matrix<S> as_matrix() const
532
533 /**
534 * @brief Reshape into an `m × (n/m)` matrix, row-major
535 *
536 * Inverse of @ref Matrix::to_vector: index k of this vector lands at `(k/cols, k%cols)`
537 * with `cols = n/m`.
538 *
539 * @throws std::invalid_argument if `m` does not divide the vector length
540 */
541 Matrix<T> to_matrix(size_t m) const;
542
543 /** @} */
544
545 private:
546 std::vector<T> data;
547
548 /// @brief Cache for Hamming weight (invalidated by mutating operations)
549 mutable details::Cache<details::CacheEntry<Weight, size_t>> cache;
550
551 constexpr size_t calculate_weight() const
553};
554
555/// @brief Deduction guide: a vector constructed from a polynomial deduces the coefficient type
556template <CoefficientType T>
558
559template <ComponentType T>
560Vector<T>::Vector(size_t n, const T& l) : data(n) {
561 fill(l);
562}
563
564template <ComponentType T>
565Vector<T>::Vector(const Vector<T>& other) : data(other.data), cache(other.cache) {}
566
567template <ComponentType T>
568constexpr Vector<T>::Vector(Vector<T>&& other) noexcept : data(std::move(other.data)), cache(std::move(other.cache)) {
569 other.cache.invalidate();
570}
571
572template <ComponentType T>
573template <FiniteFieldType S>
574Vector<T>::Vector(const Matrix<S>& mat)
576{
577 const auto m = T().template as_vector<S>().get_n();
578 if (m != mat.get_m())
579 throw std::invalid_argument("trying to construct base field vector from subfield matrix of incompatible size");
580 data.resize(mat.get_n());
581 for (size_t i = 0; i < mat.get_n(); ++i) {
582 data[i] = T(mat.get_col(i));
583 }
584}
585
586template <ComponentType T>
587template <FiniteFieldType S>
591 for (size_t i = 0; i < other.get_n(); ++i) {
592 data[i] = T(other[i]);
593 }
594}
595
596template <ComponentType T>
597template <CoefficientType U>
598 requires std::same_as<U, T>
600 if (poly.is_zero()) {
601 data.assign(1, T(0));
602 return;
603 }
604 const size_t d = poly.degree();
605 data.resize(d + 1);
606 for (size_t i = 0; i <= d; ++i) {
607 data[i] = poly[i];
608 }
609}
610
611template <ComponentType T>
612constexpr Vector<T>& Vector<T>::operator=(const Vector<T>& rhs) {
613 if (this == &rhs) return *this;
614 data = rhs.data;
615 cache = rhs.cache;
616 return *this;
617}
618
619template <ComponentType T>
620constexpr Vector<T>& Vector<T>::operator=(Vector<T>&& rhs) noexcept {
621 if (this == &rhs) return *this;
622 data = std::move(rhs.data);
623 cache = std::move(rhs.cache);
624 rhs.cache.invalidate();
625 return *this;
626}
627
628template <ComponentType T>
629constexpr Vector<T>& Vector<T>::operator=(const T& rhs) {
630 return fill(rhs);
631}
632
633template <ComponentType T>
634template <FiniteFieldType S>
639 for (size_t i = 0; i < other.get_n(); ++i) {
640 data[i] = T(other[i]);
641 }
642 return *this;
643}
644
645template <ComponentType T>
646constexpr Vector<T> Vector<T>::operator-() const& {
647 Vector res(*this);
648 std::ranges::for_each(res.data, [](T& v) { v = -v; });
649 return res; // move elision
650}
651
652template <ComponentType T>
653constexpr Vector<T> Vector<T>::operator-() && {
654 std::ranges::for_each(data, [](T& v) { v = -v; });
655 return std::move(*this);
656}
657
658template <ComponentType T>
659Vector<T>& Vector<T>::operator+=(const Vector<T>& rhs) {
660 if (data.size() != rhs.data.size()) throw std::invalid_argument("trying to add vectors of different lengths");
661 cache.invalidate();
662 std::transform(data.begin(), data.end(), rhs.data.begin(), data.begin(), std::plus<T>{});
663 return *this;
664}
665
666template <ComponentType T>
667Vector<T>& Vector<T>::operator-=(const Vector<T>& rhs) {
668 if (data.size() != rhs.data.size())
669 throw std::invalid_argument(
670 "trying to subtract vectors of different "
671 "lengths");
672 cache.invalidate();
673 std::transform(data.begin(), data.end(), rhs.data.begin(), data.begin(), std::minus<T>{});
674 return *this;
675}
676
677template <ComponentType T>
678constexpr Vector<T>& Vector<T>::operator*=(const T& s) {
679 if (s == T(0)) {
680 fill(T(0));
681 } else {
682 std::ranges::for_each(data, [&s](T& v) { v *= s; });
683 }
684
685 return *this;
686}
687
688template <ComponentType T>
689Vector<T>& Vector<T>::operator/=(const T& s)
691{
692 if (s == T(0)) throw std::invalid_argument("trying to divide components of vector by zero");
693 if constexpr (FieldType<T>) {
694 operator*=(T(1) / s);
695 } else {
697 std::ranges::for_each(data, [&s](T& v) { v /= s; });
698 }
699 return *this;
700}
701
702template <ComponentType T>
703Vector<T>& Vector<T>::append(const Vector& rhs) {
704 if (this == &rhs) {
705 Vector tmp(rhs);
706 return append(std::move(tmp));
707 }
708
709 data.reserve(data.size() + rhs.data.size());
710 data.insert(data.end(), rhs.data.begin(), rhs.data.end());
711 cache.invalidate();
712 return *this;
713}
714
715template <ComponentType T>
716Vector<T>& Vector<T>::append(Vector&& rhs) {
717 if (this == &rhs) return *this;
718
719 data.reserve(data.size() + rhs.data.size());
720 data.insert(data.end(), std::make_move_iterator(rhs.data.begin()), std::make_move_iterator(rhs.data.end()));
721 cache.invalidate();
722 return *this;
723}
724
725template <ComponentType T>
726Vector<T>& Vector<T>::prepend(const Vector& lhs) {
727 if (this == &lhs) {
728 Vector tmp(lhs);
729 return prepend(std::move(tmp));
730 }
731
732 data.reserve(data.size() + lhs.data.size());
733 data.insert(data.begin(), lhs.data.begin(), lhs.data.end());
734 cache.invalidate();
735 return *this;
736}
737
738template <ComponentType T>
739Vector<T>& Vector<T>::prepend(Vector&& lhs) {
740 if (this == &lhs) return *this;
741
742 data.reserve(data.size() + lhs.data.size());
743 data.insert(data.begin(), std::make_move_iterator(lhs.data.begin()), std::make_move_iterator(lhs.data.end()));
744 cache.invalidate();
745 return *this;
746}
747
748template <ComponentType T>
749template <typename U>
750Vector<T>& Vector<T>::append(U&& rhs)
751 requires std::constructible_from<Vector<T>, U>
752{
753 if constexpr (std::same_as<std::remove_cvref_t<U>, Vector<T>>) {
754 if constexpr (std::is_lvalue_reference_v<U&&>)
755 return append(static_cast<const Vector&>(rhs));
756 else
757 return append(std::move(rhs));
758 } else {
759 Vector<T> tmp(std::forward<U>(rhs));
760 return append(std::move(tmp));
761 }
762}
763
764template <ComponentType T>
765template <typename U>
768{
769 if constexpr (std::same_as<std::remove_cvref_t<U>, Vector<T>>) {
770 if constexpr (std::is_lvalue_reference_v<U&&>)
771 return prepend(static_cast<const Vector&>(lhs));
772 else
773 return prepend(std::move(lhs));
774 } else {
775 Vector<T> tmp(std::forward<U>(lhs));
776 return prepend(std::move(tmp));
777 }
778}
779
780template <ComponentType T>
782 if (v.empty()) return *this;
783
784 std::set<size_t> indices(v.begin(), v.end());
785 for (const size_t i : indices) {
786 if (i >= data.size()) throw std::invalid_argument("trying to delete non-existent component");
787 }
788
791
792 for (size_t i = 0; i < data.size(); ++i) {
794 }
795
796 data = std::move(new_data);
798 return *this;
799}
800
801#ifdef CECCO_ERASURE_SUPPORT
802
803template <ComponentType T>
806{
807 if (v.empty()) return *this;
808
809 std::set<size_t> indices(v.begin(), v.end());
810 for (auto it = indices.cbegin(); it != indices.cend(); ++it) {
811 if (*it >= data.size()) throw std::invalid_argument("trying to erase non-existent component");
812 }
813
815 for (auto it = indices.cbegin(); it != indices.cend(); ++it) data[*it].erase();
816
817 return *this;
818}
819
820template <ComponentType T>
823{
824 if (v.empty()) return *this;
825
826 std::set<size_t> indices(v.begin(), v.end());
827 for (auto it = indices.cbegin(); it != indices.cend(); ++it) {
828 if (*it >= data.size()) throw std::invalid_argument("trying to un-erase non-existent component");
829 }
830
832 for (auto it = indices.cbegin(); it != indices.cend(); ++it) data[*it].unerase();
833
834 return *this;
835}
836
837#endif
838
839template <ComponentType T>
841 Vector<T> res(lhs);
843 return res;
844}
845
846template <ComponentType T>
848 Vector<T> res(std::move(lhs));
850 return res;
851}
852
853template <ComponentType T>
855 if (n <= data.size()) return *this;
856
857 std::vector<T> new_data(n, T(0));
858 std::copy(data.begin(), data.end(), new_data.begin() + (n - data.size()));
859 data = std::move(new_data);
860 return *this; // padding with zeros leaves the Hamming weight unchanged
861}
862
863template <ComponentType T>
865 if (n <= data.size()) return *this;
866
867 data.resize(n, T(0));
868 return *this; // padding with zeros leaves the Hamming weight unchanged
869}
870
871template <ComponentType T>
872constexpr Vector<T>& Vector<T>::fill(const T& value) {
874 std::fill(data.begin(), data.end(), value);
875 if constexpr (ReliablyComparableType<T>) {
876 if (value == T(0))
877 cache.template set<Weight>(0);
878 else
879 cache.template set<Weight>(data.size());
880 }
881
882 return *this;
883}
884
885template <ComponentType T>
887 if (data.size() > 1) std::rotate(data.begin(), data.begin() + i % data.size(), data.end());
888 return *this;
889}
890
891template <ComponentType T>
893 if (data.size() > 1) std::rotate(data.rbegin(), data.rbegin() + i % data.size(), data.rend());
894 return *this;
895}
896
897template <ComponentType T>
898constexpr Vector<T>& Vector<T>::reverse() {
899 std::reverse(data.begin(), data.end());
900 return *this;
901}
902
903template <ComponentType T>
904template <typename U>
907{
908 if (i >= data.size()) throw std::invalid_argument("trying to access non-existent element");
909
910 T& old_value = data[i];
911
912 T new_value(std::forward<U>(c));
913 if constexpr (ReliablyComparableType<T>) {
914 if (old_value == new_value) return *this;
915 }
917
919
920 return *this;
921}
922
923template <ComponentType T>
924const T& Vector<T>::operator[](size_t i) const {
925 if (i >= data.size()) throw std::invalid_argument("trying to access non-existent element");
926 return data[i];
927}
928
929template <ComponentType T>
931 if (i + w > data.size())
932 throw std::invalid_argument(
933 "trying to extract a subvector with incompatible "
934 "length");
935 Vector res;
936 res.data.assign(data.begin() + i, data.begin() + i + w);
937 return res;
938}
939
940template <ComponentType T>
942 if (i + w > data.size())
943 throw std::invalid_argument(
944 "trying to extract a subvector with incompatible "
945 "length");
946 data.resize(i + w);
947 data.erase(data.begin(), data.begin() + i);
949 return *this;
950}
951
952template <ComponentType T>
954 if (i + v.get_n() > data.size())
955 throw std::invalid_argument(
956 "trying to replace subvector with "
957 "vector of incompatible length");
958 if (this == &v) return *this; // only reachable with i == 0: overwriting the vector with itself
960 std::copy(v.data.begin(), v.data.end(), data.begin() + i);
961 return *this;
962}
963
964template <ComponentType T>
966 if (i + v.get_n() > data.size())
967 throw std::invalid_argument(
968 "trying to replace subvector with "
969 "vector of incompatible length");
970 if (this == &v) return *this; // only reachable with i == 0: overwriting the vector with itself
972 std::move(v.data.begin(), v.data.end(), data.begin() + i);
973 return *this;
974}
975
976template <ComponentType T>
977constexpr bool Vector<T>::is_zero() const
979{
980 const T zero(0);
981 return std::all_of(data.cbegin(), data.cend(), [&zero](const T& x) { return x == zero; });
982}
983
984template <ComponentType T>
985constexpr bool Vector<T>::is_pairwise_distinct() const
987{
988 for (size_t i = 0; i < data.size(); ++i) {
989 for (size_t j = i + 1; j < data.size(); ++j) {
990 if (data[i] == data[j]) return false;
991 }
992 }
993
994 return true;
995}
996
997template <ComponentType T>
998constexpr size_t Vector<T>::calculate_weight() const
1000{
1001 size_t res = data.size() - std::count(data.cbegin(), data.cend(), T(0));
1002
1003#ifdef CECCO_ERASURE_SUPPORT
1004 if constexpr (FieldType<T>) res -= std::count_if(data.cbegin(), data.cend(), [](T x) { return x.is_erased(); });
1005#endif
1006
1007 return res;
1008}
1009
1010template <ComponentType T>
1011constexpr size_t Vector<T>::burst_length() const
1013{
1014 const T zero(0);
1015
1016 auto first_nonzero = std::find_if(data.begin(), data.end(), [&zero](const T& x) { return x != zero; });
1017
1018 if (first_nonzero == data.end()) return 0; // All zeros
1019
1020 auto last_nonzero = std::find_if(data.rbegin(), data.rend(), [&zero](const T& x) { return x != zero; });
1021
1024
1025 return R - L + 1;
1026}
1027
1028template <ComponentType T>
1031{
1032 if (data.empty()) return 0;
1033
1034 // Handle all-zero vector
1035 if (burst_length() == 0) return 0;
1036
1037 const T zero(0);
1038 size_t n = data.size();
1039 size_t max_zero_run = 0;
1041
1042 for (size_t i = 0; i < n; ++i) {
1043 if (data[i] == zero) {
1046 } else {
1047 current_zero_run = 0;
1048 }
1049 }
1050
1051 // a zero run may wrap around from the end of the vector to its start
1052 if (data[0] == zero && data[n - 1] == zero) {
1054 for (size_t i = 0; i < n && data[i] == zero; ++i) {
1056 }
1057
1059 for (size_t i = n; i > 0 && data[i - 1] == zero; --i) {
1061 }
1062
1063 if (zeros_from_start + zeros_from_end < n) { // Avoid double counting all-zero case
1065 }
1066 }
1067
1068 return n - max_zero_run;
1069}
1070
1071template <ComponentType T>
1074{
1075 cache.invalidate();
1076 if constexpr (FieldType<T>) {
1078 } else if constexpr (std::same_as<T, double>) {
1079 std::uniform_real_distribution<double> dist(-1.0, 1.0);
1080 std::ranges::for_each(data, [&](double& val) { val = dist(gen()); });
1081 } else if constexpr (std::same_as<T, std::complex<double>>) {
1082 std::uniform_real_distribution<double> dist(-1.0, 1.0);
1084 [&](std::complex<double>& val) { val = std::complex<double>(dist(gen()), dist(gen())); });
1085 } else if constexpr (SignedIntType<T>) {
1086 std::uniform_int_distribution<long long> dist(-100, 100);
1087 std::ranges::for_each(data, [&](T& val) { val = T(dist(gen())); });
1088 }
1089 return *this;
1090}
1091
1092template <ComponentType T>
1095{
1096 cache.invalidate();
1097 const T zero(0);
1098 for (size_t i = 0; i < data.size(); ++i) {
1099 data[i] = zero;
1101 }
1102 if constexpr (ReliablyComparableType<T>) cache.template set<Weight>(data.size());
1103 return *this;
1104}
1105
1106template <ComponentType T>
1109{
1110 constexpr size_t q = T::get_size();
1111 const size_t n = data.size();
1112 if (n > q)
1113 throw std::invalid_argument("Cannot generate pairwise distinct vector: length " + std::to_string(n) +
1114 " exceeds field size " + std::to_string(q));
1115 cache.invalidate();
1116 // partial Fisher-Yates over the label range, tracking only displaced entries: O(n) instead of O(q)
1118 const auto label_at = [&](size_t idx) {
1119 const auto it = displaced.find(idx);
1120 return it != displaced.end() ? it->second : idx;
1121 };
1122 bool zero_drawn = false;
1123 for (size_t i = 0; i < n; ++i) {
1124 const size_t j = std::uniform_int_distribution<size_t>(i, q - 1)(gen());
1125 const size_t label = label_at(j);
1126 displaced[j] = label_at(i);
1127 if (label == 0) zero_drawn = true;
1128 data[i] = T(label);
1129 }
1130 // labels are pairwise distinct, so the zero element occurs at most once
1131 cache.template set<Weight>(zero_drawn ? n - 1 : n);
1132 return *this;
1133}
1134
1135template <ComponentType T>
1138{
1139 constexpr size_t q = T::get_size();
1140 size_t result = 0;
1141 for (size_t i = 0; i < data.size(); ++i) {
1142 const auto& digit = data[i];
1143#ifdef CECCO_ERASURE_SUPPORT
1144 if (digit.is_erased()) throw std::invalid_argument("Cannot convert vector with erased component to integer");
1145#endif
1146 if (result > (std::numeric_limits<size_t>::max() - digit.get_label()) / q)
1147 throw std::overflow_error("Cannot convert vector to integer, value exceeds the size_t range!");
1148 result = result * q + digit.get_label();
1149 }
1150 return result;
1151}
1152
1153template <ComponentType T>
1156{
1157 constexpr size_t q = T::get_size();
1158
1159 Vector<T> v(n);
1160 for (size_t i = 0; i < n; ++i) {
1161 v.data[n - 1 - i] = T(value % q);
1162 value /= q;
1163 }
1164 v.cache.invalidate(); // raw writes bypass set_component's cache handling
1165 if (value != 0)
1166 throw std::out_of_range("Cannot convert integer value to base " + std::to_string(T::get_size()) +
1167 " vector of length " + std::to_string(n) + "!");
1168 *this = std::move(v);
1169 return *this;
1170}
1171
1172template <ComponentType T>
1173template <FiniteFieldType S>
1176{
1177 const auto m = T().template as_vector<S>().get_n();
1178 Matrix<S> res(m, data.size());
1179
1180 for (size_t i = 0; i < data.size(); ++i) {
1181#ifdef CECCO_ERASURE_SUPPORT
1182 if (data[i].is_erased()) {
1183 for (size_t j = 0; j < m; ++j) {
1185 }
1186 } else {
1187 Matrix<S> temp(data[i].template as_vector<S>());
1188 temp.transpose();
1189 res.set_submatrix(0, i, temp);
1190 }
1191#else
1192 Matrix<S> temp(data[i].template as_vector<S>());
1193 temp.transpose();
1194 res.set_submatrix(0, i, temp);
1195#endif
1196 }
1197
1198 return res;
1199}
1200
1201template <ComponentType T>
1203 if (m == 0) throw std::invalid_argument("Trying to convert vector into a matrix with zero rows");
1204 if (get_n() % m != 0)
1205 throw std::invalid_argument(std::string("Cannot convert vector into a matrix with ") + std::to_string(m) +
1206 std::string(" rows, number of rows m (") + std::to_string(m) +
1207 std::string(") is not a divisor of vector length n (") + std::to_string(get_n()) +
1208 ")!");
1209
1210 Matrix<T> M(m, get_n() / m);
1211 std::copy(data.begin(), data.end(), M.data.begin());
1212 M.type = details::Generic; // raw writes bypass set_component's tag tracking
1214 return M;
1215}
1216
1217/* free functions wrt. Vector */
1218
1219/*
1220 * vector + vector
1221 */
1222
1223template <ComponentType T>
1224constexpr Vector<T> operator+(const Vector<T>& lhs, const Vector<T>& rhs) {
1225 Vector res(lhs);
1226 res += rhs;
1227 return res;
1228}
1229
1230template <ComponentType T>
1231constexpr Vector<T> operator+(Vector<T>&& lhs, const Vector<T>& rhs) {
1232 Vector res(std::move(lhs));
1233 res += rhs;
1234 return res;
1235}
1236
1237template <ComponentType T>
1238constexpr Vector<T> operator+(const Vector<T>& lhs, Vector<T>&& rhs) {
1239 Vector res(std::move(rhs));
1240 res += lhs;
1241 return res;
1242}
1243
1244template <ComponentType T>
1245constexpr Vector<T> operator+(Vector<T>&& lhs, Vector<T>&& rhs) {
1246 Vector res(std::move(lhs));
1247 res += rhs;
1248 return res;
1249}
1250
1251/*
1252 * vector - vector
1253 */
1254
1255template <ComponentType T>
1256constexpr Vector<T> operator-(const Vector<T>& lhs, const Vector<T>& rhs) {
1257 Vector res(lhs);
1258 res -= rhs;
1259 return res;
1260}
1261
1262template <ComponentType T>
1263constexpr Vector<T> operator-(Vector<T>&& lhs, const Vector<T>& rhs) {
1264 Vector res(std::move(lhs));
1265 res -= rhs;
1266 return res;
1267}
1268
1269template <ComponentType T>
1270constexpr Vector<T> operator-(const Vector<T>& lhs, Vector<T>&& rhs) {
1271 Vector res(-std::move(rhs));
1272 res += lhs;
1273 return res;
1274}
1275
1276template <ComponentType T>
1277constexpr Vector<T> operator-(Vector<T>&& lhs, Vector<T>&& rhs) {
1278 Vector res(std::move(lhs));
1279 res -= rhs;
1280 return res;
1281}
1282
1283/*
1284 * vector * T
1285 */
1286
1287template <ComponentType T>
1288constexpr Vector<T> operator*(const Vector<T>& lhs, const T& rhs) {
1289 Vector res(lhs);
1290 res *= rhs;
1291 return res;
1292}
1293
1294template <ComponentType T>
1295constexpr Vector<T> operator*(Vector<T>&& lhs, const T& rhs) {
1296 Vector res(std::move(lhs));
1297 res *= rhs;
1298 return res;
1299}
1300
1301/*
1302 * T * vector
1303 */
1304
1305template <ComponentType T>
1306constexpr Vector<T> operator*(const T& lhs, const Vector<T>& rhs) {
1307 Vector res(rhs);
1308 res *= lhs;
1309 return res;
1310}
1311
1312template <ComponentType T>
1313constexpr Vector<T> operator*(const T& lhs, Vector<T>&& rhs) {
1314 Vector res(std::move(rhs));
1315 res *= lhs;
1316 return res;
1317}
1318
1319/*
1320 * vector / T
1321 */
1322
1323template <CoefficientType T>
1324constexpr Vector<T> operator/(const Vector<T>& lhs, const T& rhs) {
1325 Vector res(lhs);
1326 res /= rhs;
1327 return res;
1328}
1329
1330template <CoefficientType T>
1331constexpr Vector<T> operator/(Vector<T>&& lhs, const T& rhs) {
1332 Vector res(std::move(lhs));
1333 res /= rhs;
1334 return res;
1335}
1336
1337/// @brief Discrete linear convolution (= coefficients of `Polynomial(v) * Polynomial(w)`); the
1338/// result always has length `v.get_n() + w.get_n() - 1`
1339/// @throws std::invalid_argument if either input is empty
1340template <CoefficientType T>
1341Vector<T> convolve(const Vector<T>& v, const Vector<T>& w) {
1342 if (v.get_n() == 0 || w.get_n() == 0) throw std::invalid_argument("Cannot convolve empty vector(s)!");
1344 res.pad_back(v.get_n() + w.get_n() - 1);
1345 return res;
1346}
1347
1348template <CoefficientType T>
1350 Vector<T> result = v;
1351 result.randomize();
1352 return result;
1353}
1354
1355template <CoefficientType T>
1357 Vector<T> result = std::move(v);
1358 result.randomize();
1359 return result;
1360}
1361
1362template <ComponentType T>
1363Vector<T> set_component(const Vector<T>& v, size_t i, const T& c) {
1364 Vector<T> res(v);
1365 res.set_component(i, c);
1366 return res;
1367}
1368
1369template <ComponentType T>
1371 Vector<T> res(std::move(v));
1372 res.set_component(i, c);
1373 return res;
1374}
1375
1376template <ComponentType T>
1378 return v.get_subvector(start, end);
1379}
1380
1381template <ComponentType T>
1383 return std::move(v).get_subvector(start, end);
1384}
1385
1386template <ComponentType T>
1387constexpr Vector<T> set_subvector(Vector<T> v, size_t start, const Vector<T>& w) {
1388 return v.set_subvector(w, start);
1389}
1390
1391template <ComponentType T>
1392constexpr Vector<T> set_subvector(Vector<T>&& v, size_t start, const Vector<T>& w) {
1393 return std::move(v).set_subvector(w, start);
1394}
1395
1396template <ComponentType T>
1397constexpr Vector<T> concatenate(const Vector<T>& lhs, const Vector<T>& rhs) {
1398 Vector res(lhs);
1399 res.append(rhs);
1400 return res;
1401}
1402
1403template <ComponentType T>
1404constexpr Vector<T> concatenate(Vector<T>&& lhs, const Vector<T>& rhs) {
1405 Vector res(std::move(lhs));
1406 res.append(rhs);
1407 return res;
1408}
1409
1410template <ComponentType T>
1411constexpr Vector<T> concatenate(const Vector<T>& lhs, Vector<T>&& rhs) {
1412 Vector res(std::move(rhs));
1413 res.prepend(lhs);
1414 return res;
1415}
1416
1417template <ComponentType T>
1418constexpr Vector<T> concatenate(Vector<T>&& lhs, Vector<T>&& rhs) {
1419 Vector res(std::move(lhs));
1420 res.append(rhs);
1421 return res;
1422}
1423
1424template <ComponentType T>
1426 Vector res(lhs);
1428 return res;
1429}
1430
1431template <ComponentType T>
1433 Vector res(std::move(lhs));
1435 return res;
1436}
1437
1438#ifdef CECCO_ERASURE_SUPPORT
1439
1440template <ComponentType T>
1442 Vector res(lhs);
1444 return res;
1445}
1446
1447template <ComponentType T>
1449 Vector res(std::move(lhs));
1451 return res;
1452}
1453
1454template <ComponentType T>
1456 Vector res(lhs);
1458 return res;
1459}
1460
1461template <ComponentType T>
1463 Vector res(std::move(lhs));
1465 return res;
1466}
1467
1468template <ComponentType T>
1470 Vector res(lhs);
1472 return res;
1473}
1474
1475template <ComponentType T>
1477 Vector res(std::move(lhs));
1479 return res;
1480}
1481
1482template <ComponentType T>
1484 Vector res(lhs);
1486 return res;
1487}
1488
1489template <ComponentType T>
1491 Vector res(std::move(lhs));
1493 return res;
1494}
1495
1496#endif
1497
1498template <ComponentType T>
1499constexpr Vector<T> pad_front(const Vector<T>& v, size_t n) {
1500 Vector res(v);
1501 res.pad_front(n);
1502 return res;
1503}
1504
1505template <ComponentType T>
1506constexpr Vector<T> pad_front(Vector<T>&& v, size_t n) {
1507 Vector res(std::move(v));
1508 res.pad_front(n);
1509 return res;
1510}
1511
1512template <ComponentType T>
1513constexpr Vector<T> pad_back(const Vector<T>& v, size_t n) {
1514 Vector res(v);
1515 res.pad_back(n);
1516 return res;
1517}
1518
1519template <ComponentType T>
1520constexpr Vector<T> pad_back(Vector<T>&& v, size_t n) {
1521 Vector res(std::move(v));
1522 res.pad_back(n);
1523 return res;
1524}
1525
1526template <ComponentType T>
1527constexpr Vector<T> rotate_left(const Vector<T>& v, size_t i) {
1528 Vector res(v);
1529 res.rotate_left(i);
1530 return res;
1531}
1532
1533template <ComponentType T>
1535 Vector res(std::move(v));
1536 res.rotate_left(i);
1537 return res;
1538}
1539
1540template <ComponentType T>
1541constexpr Vector<T> rotate_right(const Vector<T>& v, size_t i) {
1542 Vector res(v);
1544 return res;
1545}
1546
1547template <ComponentType T>
1549 Vector res(std::move(v));
1551 return res;
1552}
1553
1554template <ComponentType T>
1555constexpr Vector<T> reverse(const Vector<T>& v) {
1556 Vector res(v);
1557 res.reverse();
1558 return res;
1559}
1560
1561template <ComponentType T>
1562constexpr Vector<T> reverse(Vector<T>&& v) {
1563 Vector res(std::move(v));
1564 res.reverse();
1565 return res;
1566}
1567
1568template <ComponentType T>
1569constexpr Vector<T> fill(const Vector<T>& v, const T& value) {
1570 Vector<T> res(v);
1571 res.fill(value);
1572 return res;
1573}
1574
1575template <ComponentType T>
1576constexpr Vector<T> fill(Vector<T>&& v, const T& value) {
1577 Vector<T> res(std::move(v));
1578 res.fill(value);
1579 return res;
1580}
1581
1582/**
1583 * @brief Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i]
1584 *
1585 * For complex inputs this is the standard product (not the conjugate inner product).
1586 *
1587 * @throws std::invalid_argument if lengths differ
1588 */
1589template <ComponentType T>
1590T inner_product(const Vector<T>& lhs, const Vector<T>& rhs) {
1591 if (lhs.get_n() != rhs.get_n())
1592 throw std::invalid_argument(
1593 "trying to calculate inner product of "
1594 "vectors of different lengths");
1595 return std::inner_product(lhs.data.cbegin(), lhs.data.cend(), rhs.data.begin(), T(0));
1596}
1597
1598/**
1599 * @brief Schur (component-wise) product
1600 * @throws std::invalid_argument if lengths differ
1601 */
1602template <ComponentType T>
1604 if (lhs.get_n() != rhs.get_n())
1605 throw std::invalid_argument(
1606 "trying to calculate Schur product of "
1607 "vectors of different lengths");
1608 Vector<T> res(lhs.get_n());
1609 for (size_t i = 0; i < res.get_n(); ++i) res.data[i] = lhs.data[i] * rhs.data[i];
1611 return res;
1612}
1613
1614template <ReliablyComparableType T>
1615constexpr bool operator==(const Vector<T>& lhs, const Vector<T>& rhs) {
1616 return lhs.data == rhs.data;
1617}
1618
1619template <ReliablyComparableType T>
1620constexpr bool operator!=(const Vector<T>& lhs, const Vector<T>& rhs) {
1621 return !(lhs == rhs);
1622}
1623
1624/**
1625 * @brief Length-`length` vector with `T(1)` at index `i` and zeros elsewhere
1626 * @throws std::invalid_argument if `i >= length`
1627 */
1628template <ComponentType T>
1630 if (i >= length) throw std::invalid_argument("trying to create invalid unit vector");
1631 Vector<T> res(length);
1632 res.set_component(i, T(1));
1633 if constexpr (ReliablyComparableType<T>) res.cache.template set<Vector<T>::Weight>(1);
1634 return res;
1635}
1636
1637/** @brief Stream output as `( c₀, c₁, …, cₙ₋₁ )`. */
1638template <ComponentType T>
1640 os << "( ";
1641 const char* sep = "";
1642 for (size_t i = 0; i < rhs.data.size(); ++i) {
1643 os << sep << rhs.data[i];
1644 sep = ", ";
1645 }
1646 os << " )";
1647 return os;
1648}
1649
1650/** @name Error control coding-related functions
1651 * @{
1652 */
1653
1654/** @brief Hamming weight; see @ref Vector::wH for semantics. */
1655template <ReliablyComparableType T>
1656constexpr size_t wH(const Vector<T>& v) {
1657 return v.wH();
1658}
1659
1660/** @brief Support; see @ref Vector::supp for semantics. */
1661template <ReliablyComparableType T>
1663 return v.supp();
1664}
1665
1666/**
1667 * @brief Hamming distance dₕ(lhs, rhs) = wₕ(lhs − rhs)
1668 *
1669 * Number of positions in which `lhs` and `rhs` differ. Under @ref CECCO_ERASURE_SUPPORT,
1670 * positions where either side is erased are not counted (subtraction propagates the
1671 * erasure marker, and @ref wH skips erased components).
1672 *
1673 * @throws std::invalid_argument if lengths differ
1674 */
1675template <ReliablyComparableType T>
1676size_t dH(const Vector<T>& lhs, const Vector<T>& rhs) {
1677 if (lhs.get_n() != rhs.get_n())
1678 throw std::invalid_argument(
1679 "trying to calculate Hamming distance between vectors of different "
1680 "lengths");
1681 return (lhs - rhs).wH();
1682}
1683
1684// rvalue overloads of dH share semantics with the const&,const& version above.
1685template <ReliablyComparableType T>
1686size_t dH(Vector<T>&& lhs, const Vector<T>& rhs) {
1687 if (lhs.get_n() != rhs.get_n())
1688 throw std::invalid_argument(
1689 "trying to calculate Hamming distance between vectors of different "
1690 "lengths");
1691 return (std::move(lhs) - rhs).wH();
1692}
1693
1694template <ReliablyComparableType T>
1695size_t dH(const Vector<T>& lhs, Vector<T>&& rhs) {
1696 if (lhs.get_n() != rhs.get_n())
1697 throw std::invalid_argument(
1698 "trying to calculate Hamming distance between vectors of different "
1699 "lengths");
1700 return (lhs - std::move(rhs)).wH();
1701}
1702template <ReliablyComparableType T>
1704 if (lhs.get_n() != rhs.get_n())
1705 throw std::invalid_argument(
1706 "trying to calculate Hamming distance between vectors of different "
1707 "lengths");
1708 return (std::move(lhs) - std::move(rhs)).wH();
1709}
1710
1711/** @brief Burst length; see @ref Vector::burst_length for semantics. */
1712template <ReliablyComparableType T>
1713constexpr size_t burst_length(const Vector<T>& v) {
1714 return v.burst_length();
1715}
1716
1717/** @brief Cyclic burst length; see @ref Vector::cyclic_burst_length for semantics. */
1718template <ReliablyComparableType T>
1720 return v.cyclic_burst_length();
1721}
1722
1723/**
1724 * @brief Euclidean distance ‖lhs − rhs‖₂ = √(Σᵢ |lhs[i] − rhs[i]|²) for complex vectors
1725 * @throws std::invalid_argument if lengths differ
1726 */
1727inline double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs) {
1728 if (lhs.get_n() != rhs.get_n())
1729 throw std::invalid_argument(
1730 "trying to calculate euclidean distance between vectors of different "
1731 "lengths");
1732
1734 lhs.data.begin(), lhs.data.end(), rhs.data.begin(), 0.0, std::plus<double>{},
1735 [](const std::complex<double>& l, const std::complex<double>& r) { return std::norm(l - r); });
1736
1737 return std::sqrt(sum_of_squares);
1738}
1739
1740/** @} */
1741
1742} // namespace CECCO
1743
1744#endif
Dense m × n matrix over a CECCO::ComponentType.
Definition matrices.hpp:175
Univariate polynomial p(x) = a₀ + a₁x + … + aₙxⁿ over a CECCO::CoefficientType.
Vector v = (v₀, v₁, …, vₙ₋₁) over a CECCO::ComponentType.
Definition vectors.hpp:124
const T & operator[](size_t i) const
Read access to component i.
Definition vectors.hpp:924
Vector(size_t n)
Length-n vector with zero-initialised components.
Definition vectors.hpp:148
constexpr Vector & reverse()
Reverse component order: i ↦ n − 1 − i.
Definition vectors.hpp:898
Vector & delete_components(const std::vector< size_t > &v)
Delete the components whose indices appear in v and compact.
Definition vectors.hpp:781
constexpr bool is_pairwise_distinct() const
True iff no two components are equal.
Definition vectors.hpp:985
friend Vector< T > Schur_product(const Vector< T > &lhs, const Vector< T > &rhs)
Schur (component-wise) product.
Definition vectors.hpp:1603
Matrix< T > to_matrix(size_t m) const
Reshape into an m × (n/m) matrix, row-major.
Definition vectors.hpp:1202
Matrix< S > as_matrix() const
Expand each component into its S-coefficient column.
Definition vectors.hpp:1174
constexpr Vector & rotate_right(size_t i)
Circular right shift by i positions: j ↦ (j + i) mod n.
Definition vectors.hpp:892
constexpr Vector & fill(const T &value)
Set every component to value.
Definition vectors.hpp:872
std::vector< size_t > supp() const
Sorted indices of non-zero, non-erased components (empty vector for an input with only zeros and eras...
Definition vectors.hpp:319
constexpr Vector & rotate_left(size_t i)
Circular left shift by i positions: j ↦ (j − i) mod n.
Definition vectors.hpp:886
constexpr bool is_zero() const
True iff every component equals T(0); also true for the empty vector.
Definition vectors.hpp:977
Vector & set_component(size_t i, U &&c)
Set component i by perfect-forwarding c.
constexpr Vector & operator=(const T &rhs)
Set every component to rhs.
Definition vectors.hpp:629
constexpr Vector(Vector &&other) noexcept
Definition vectors.hpp:568
constexpr Vector operator+() const &
Unary + (lvalue): returns a copy.
Definition vectors.hpp:222
Vector & randomize_pairwise_distinct()
Fill with pairwise distinct field elements (shuffled labels 0, …, q − 1).
Definition vectors.hpp:1107
Vector & set_subvector(const Vector &v, size_t i)
Overwrite components [i, i + v.get_n()) with v.
Definition vectors.hpp:953
Vector & prepend(Vector &&rhs)
Rvalue overload of prepend (moves components from rhs).
Definition vectors.hpp:739
Vector & append(const Vector &rhs)
Append rhs: result becomes [*this | rhs].
Definition vectors.hpp:703
constexpr Vector & operator=(const Vector &rhs)
Definition vectors.hpp:612
Vector & append(Vector &&rhs)
Rvalue overload of append (moves components from rhs).
Definition vectors.hpp:716
Vector get_subvector(size_t i, size_t w) const &
Extract the contiguous subvector [i, i + w).
Definition vectors.hpp:930
Vector & operator+=(const Vector &rhs)
Component-wise addition this[i] += rhs[i].
Definition vectors.hpp:659
Vector & pad_back(size_t n)
Append zeros so the vector has length at least n (no-op if already ≥ n).
Definition vectors.hpp:864
Vector & randomize_nonzero()
Like randomize but every component is guaranteed non-zero.
Definition vectors.hpp:1093
Vector & operator-=(const Vector &rhs)
Component-wise subtraction this[i] -= rhs[i].
Definition vectors.hpp:667
Vector & delete_component(size_t i)
Delete component i (single-index convenience for delete_components).
Definition vectors.hpp:427
constexpr Vector & operator*=(const T &s)
Multiply every component by the scalar s.
Definition vectors.hpp:678
constexpr Vector operator-() const &
Unary − (lvalue): returns a new vector with each component negated.
Definition vectors.hpp:646
constexpr Vector & operator=(Vector &&rhs) noexcept
Definition vectors.hpp:620
constexpr bool is_empty() const noexcept
True iff length is 0 (distinct from a non-empty all-zero vector).
Definition vectors.hpp:304
Vector & from_integer(size_t value, size_t n)
Replace *this with the length-n base-q encoding of value.
Definition vectors.hpp:1154
Vector & get_subvector(size_t i, size_t w) &&
In-place rvalue overload of get_subvector (truncates to [i, i + w)).
Definition vectors.hpp:941
Vector & prepend(U &&lhs)
Prepend after converting lhs to Vector<T>.
constexpr size_t get_n() const noexcept
Vector length (number of components).
Definition vectors.hpp:301
Vector(const Vector &other)
Definition vectors.hpp:565
Vector(const Matrix< S > &mat)
From a matrix over a subfield: each column becomes one T component.
Definition vectors.hpp:574
Vector & operator/=(const T &s)
Divide every component by the scalar s.
Definition vectors.hpp:689
Vector & pad_front(size_t n)
Prepend zeros so the vector has length at least n (no-op if already ≥ n).
Definition vectors.hpp:854
Vector(size_t n, const T &l)
Length-n vector with every component equal to l.
Definition vectors.hpp:560
size_t as_integer() const
Interpret as a base-q integer, where q = T::get_size().
Definition vectors.hpp:1136
constexpr size_t cyclic_burst_length() const
Cyclic burst length: shortest circular arc covering all non-zero positions; 0 for an all-zero or empt...
Definition vectors.hpp:1029
Vector & set_subvector(Vector &&v, size_t i)
Rvalue overload of set_subvector (move-assigns components from v).
Definition vectors.hpp:965
Vector & append(U &&rhs)
Append after converting rhs to Vector<T>.
friend T inner_product(const Vector< T > &lhs, const Vector< T > &rhs)
Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i].
Definition vectors.hpp:1590
Vector & prepend(const Vector &rhs)
Prepend rhs: result becomes [rhs | *this].
Definition vectors.hpp:726
friend constexpr bool operator==(const Vector< U > &lhs, const Vector< U > &rhs)
constexpr Vector() noexcept=default
Default constructor: empty vector (length 0).
Vector & randomize()
Replace components with random values appropriate for T.
Definition vectors.hpp:1072
constexpr Vector operator+() &&noexcept
Unary + (rvalue): returns the rvalue itself.
Definition vectors.hpp:224
constexpr Vector operator-() &&
Unary − (rvalue): negates components in place.
Definition vectors.hpp:653
Vector(const std::initializer_list< T > &l)
From an initializer list of components, in order.
Definition vectors.hpp:156
Provides a framework for error correcting codes.
Definition blocks.hpp:57
Vector< T > Schur_product(const Vector< T > &lhs, const Vector< T > &rhs)
Schur (component-wise) product.
Definition vectors.hpp:1603
double dE(const Vector< std::complex< double > > &lhs, const Vector< std::complex< double > > &rhs)
Euclidean distance ‖lhs − rhs‖₂ = √(Σᵢ |lhs[i] − rhs[i]|²) for complex vectors.
Definition vectors.hpp:1727
Vector(const Polynomial< T > &) -> Vector< T >
Deduction guide: a vector constructed from a polynomial deduces the coefficient type.
T inner_product(const Vector< T > &lhs, const Vector< T > &rhs)
Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i].
Definition vectors.hpp:1590
Vector< T > unit_vector(size_t length, size_t i)
Length-length vector with T(1) at index i and zeros elsewhere.
Definition vectors.hpp:1629