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.8
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 <algorithm> // transitive through matrices.hpp
49// #include <complex> // transitive through matrices.hpp
50#include <initializer_list>
51// #include <iostream> // transitive through matrices.hpp
52#include <numeric>
53// #include <ranges> // transitive through matrices.hpp
54// #include <set> // transitive through matrices.hpp
55#include <unordered_set>
56// #include <vector> // transitive through matrices.hpp
57
58// #include "InfInt.hpp" // transitive through matrices.hpp
59// #include "helpers.hpp" // transitive through matrices.hpp
60// #include "field_concepts_traits.hpp" // transitive through matrices.hpp
61#include "matrices.hpp"
62
63namespace CECCO {
64
65template <ComponentType T>
66class Vector;
67template <ComponentType T>
68class Polynomial;
69template <ComponentType T>
70class Matrix;
71
72namespace details {
73template <FiniteFieldType T>
75} // namespace details
76
77template <ComponentType T>
78T inner_product(const Vector<T>& lhs, const Vector<T>& rhs);
79template <ComponentType T>
80Vector<T> Schur_product(const Vector<T>& lhs, const Vector<T>& rhs);
81template <ComponentType T>
82Vector<T> unit_vector(size_t length, size_t i);
83template <ComponentType T>
84std::ostream& operator<<(std::ostream& os, const Vector<T>& rhs) noexcept;
85double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs);
86
87/**
88 * @brief Vector v = (vā‚€, v₁, …, vₙ₋₁) over a @ref CECCO::ComponentType
89 *
90 * @tparam T Component type satisfying @ref CECCO::ComponentType (finite field, `double`,
91 * `std::complex<double>`, or signed integer including `InfInt`)
92 *
93 * Components are stored contiguously; length is fixed except via the explicit resizers
94 * (@ref pad_front, @ref pad_back, @ref append, @ref prepend, @ref delete_components, …).
95 * Dimension mismatches in arithmetic throw `std::invalid_argument`. Hamming weight is
96 * computed lazily and cached; the cache is invalidated on any mutation. Methods that
97 * compare against zero (@ref wH, @ref supp, @ref burst_length, …) are gated by
98 * `requires ReliablyComparableType<T>`.
99 *
100 * Cross-field constructors and assignment operators between two finite fields of the same
101 * characteristic route through @ref CECCO::details::largest_common_subfield_t, so vectors
102 * over fields from disjoint construction towers can interoperate.
103 *
104 * @section Usage_Example
105 *
106 * @code{.cpp}
107 * using F4 = Ext<Fp<2>, MOD{1, 1, 1}>;
108 * Vector<F4> v = {0, 1, 2, 3};
109 * size_t weight = v.wH(); // Hamming weight (cached)
110 * size_t burst = v.burst_length();
111 * auto M = v.as_matrix<Fp<2>>(); // 2 Ɨ 4 matrix over š”½ā‚‚ (š”½ā‚‚ āŠ† š”½ā‚„)
112 * @endcode
113 */
114template <ComponentType T>
115class Vector {
116 template <ReliablyComparableType U>
117 friend constexpr bool operator==(const Vector<U>& lhs, const Vector<U>& rhs) noexcept;
118 friend constexpr T inner_product<>(const Vector<T>& lhs, const Vector<T>& rhs);
119 friend constexpr Vector<T> Schur_product<>(const Vector<T>& lhs, const Vector<T>& rhs);
120 friend Vector unit_vector<>(size_t length, size_t i);
121 friend std::ostream& operator<< <>(std::ostream& os, const Vector& rhs) noexcept;
122 friend double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs);
123 friend class Matrix<T>;
124
125 public:
126 // Cache configuration for this class
127 enum CacheIds { Weight = 0 };
128
129 /** @name Constructors
130 * @{
131 */
132
133 /// @brief Default constructor: empty vector (length 0)
134 constexpr Vector() noexcept : data(0) {}
135
136 /// @brief Length-`n` vector with default-initialised components (T() = 0)
137 explicit Vector(size_t n) : data(n) {}
138
139 /// @brief Length-`n` vector with every component equal to `l`
140 Vector(size_t n, const T& l);
141
142 /// @brief From an initializer list of components, in order
143 Vector(const std::initializer_list<T>& l) : data(l) {}
144
145 Vector(const Vector& other);
146 constexpr Vector(Vector&& other) noexcept;
147
148 /**
149 * @brief From a matrix over a subfield: each column becomes one T component
150 *
151 * @tparam S Subfield of T (in the construction tower)
152 * @throws std::invalid_argument if `mat.get_m()` does not match `[T:S]` (the extension
153 * degree of T over S)
154 */
155 template <FiniteFieldType S>
156 Vector(const Matrix<S>& mat)
158
159 /**
160 * @brief Cross-field conversion between two finite fields of the same characteristic
161 *
162 * @tparam S Source field type (`Vector<S>`); must share characteristic with T
163 *
164 * Converts component by component via T's cross-field constructor, which routes through
165 * @ref CECCO::details::largest_common_subfield_t and so handles disjoint construction towers.
166 * Propagates `std::invalid_argument` if any component is not representable in T.
167 */
168 template <FiniteFieldType S>
170 Vector(const Vector<S>& other);
171
172 /**
173 * @brief From polynomial coefficients: component `i` becomes `poly[i]`
174 *
175 * Resulting length is `poly.degree() + 1`. For cross-field conversion, convert the
176 * polynomial first.
177 */
178 Vector(const Polynomial<T>& poly);
179
180 /** @} */
181
182 /** @name Assignment Operators
183 * @{
184 */
185
186 constexpr Vector& operator=(const Vector& rhs);
187 constexpr Vector& operator=(Vector&& rhs) noexcept;
188
189 /// @brief Set every component to `rhs`
190 constexpr Vector& operator=(const T& rhs) noexcept;
191
192 /// @brief Cross-field assignment (same semantics as the cross-field constructor)
193 template <FiniteFieldType S>
196
197 /** @} */
198
199 /** @name Unary Arithmetic Operations
200 * @{
201 */
202
203 /// @brief Unary `+` (lvalue): returns a copy
204 constexpr Vector operator+() const& noexcept { return *this; }
205 /// @brief Unary `+` (rvalue): returns the rvalue itself
206 constexpr Vector operator+() && noexcept { return std::move(*this); }
207
208 /// @brief Unary `āˆ’` (lvalue): returns a new vector with each component negated
209 constexpr Vector operator-() const&;
210 /// @brief Unary `āˆ’` (rvalue): negates components in place
211 constexpr Vector operator-() && noexcept;
212
213 /** @} */
214
215 /** @name Compound Assignment Operations
216 * @{
217 */
218
219 /**
220 * @brief Component-wise addition `this[i] += rhs[i]`
221 *
222 * @throws std::invalid_argument if `this->get_n() != rhs.get_n()`
223 */
224 Vector& operator+=(const Vector& rhs);
225
226 /**
227 * @brief Component-wise subtraction `this[i] -= rhs[i]`
228 *
229 * @throws std::invalid_argument if `this->get_n() != rhs.get_n()`
230 */
231 Vector& operator-=(const Vector& rhs);
232
233 /// @brief Multiply every component by the scalar `s`
234 constexpr Vector& operator*=(const T& s) noexcept;
235
236 /**
237 * @brief Divide every component by the scalar `s`
238 *
239 * @throws std::invalid_argument if `s == T(0)`
240 * @note Round-trip `(v / s) * s == v` is only guaranteed when T satisfies @ref CECCO::FieldType
241 * (otherwise integer rounding may corrupt components).
242 */
243 Vector& operator/=(const T& s);
244
245 /** @} */
246
247 /** @name Randomization
248 * @{
249 */
250
251 /**
252 * @brief Replace components with random values appropriate for T
253 *
254 * Distribution per component: finite-field types draw uniformly from the field; signed
255 * integers from [āˆ’100, 100]; `double` and the parts of `std::complex<double>` from [āˆ’1, 1].
256 */
257 Vector& randomize() noexcept;
258
259 /// @brief Like @ref randomize but every component is guaranteed non-zero
260 Vector& randomize_nonzero() noexcept
262
263 /**
264 * @brief Fill with pairwise distinct field elements (shuffled labels 0, …, q āˆ’ 1)
265 *
266 * @throws std::invalid_argument if the vector length exceeds the field cardinality q
267 */
270
271 /** @} */
272
273 /** @name Information and Properties
274 * @{
275 */
276
277 /// @brief Vector length (number of components)
278 constexpr size_t get_n() const noexcept { return data.size(); }
279
280 /// @brief True iff length is 0 (distinct from a non-empty all-zero vector)
281 constexpr bool is_empty() const noexcept { return data.empty(); }
282
283 /// @brief True iff every component equals T(0); also true for the empty vector
284 constexpr bool is_zero() const noexcept
286
287 /// @brief True iff no two components are equal
288 constexpr bool is_pairwise_distinct() const
290
291 /**
292 * @brief Sorted indices of non-zero components (empty vector for an all-zero input)
293 *
294 * @throws std::invalid_argument if `*this` is empty (length 0)
295 */
298 {
299 if (data.empty()) throw std::invalid_argument("Support of an empty (length zero) vector is undefined!");
301 for (size_t i = 0; i < data.size(); ++i)
302 if (data[i] != T(0)) supp.push_back(i);
303 return supp;
304 }
305
306 /// @brief Hamming weight: number of non-zero, non-erased components; cached on first call
307 size_t wH() const noexcept
309 {
310 return cache.template get_or_compute<Weight>([this] { return calculate_weight(); });
311 }
312
313 /// @brief Burst length R āˆ’ L + 1, where L, R are the first and last non-zero indices; 0 for an all-zero or empty vector
314 constexpr size_t burst_length() const noexcept;
315
316 /// @brief Cyclic burst length: shortest circular arc covering all non-zero positions; 0 for an all-zero or empty vector
317 constexpr size_t cyclic_burst_length() const noexcept;
318
319 /** @} */
320
321 /** @name Component Access and Manipulation
322 * @{
323 */
324
325 /**
326 * @brief Set component `i` by perfect-forwarding `c`
327 *
328 * @throws std::invalid_argument if `i` is out of bounds
329 */
330 template <typename U>
331 Vector& set_component(size_t i, U&& c)
333
334 /**
335 * @brief Read access to component `i`
336 *
337 * @throws std::invalid_argument if `i` is out of bounds
338 */
339 const T& operator[](size_t i) const;
340
341 /**
342 * @brief Extract the contiguous subvector `[i, i + w)`
343 *
344 * @throws std::invalid_argument if `[i, i + w)` exceeds the vector
345 */
346 Vector get_subvector(size_t i, size_t w) const&;
347
348 /// @brief In-place rvalue overload of @ref get_subvector (truncates to `[i, i + w)`)
349 Vector& get_subvector(size_t i, size_t w) &&;
350
351 /**
352 * @brief Overwrite components `[i, i + v.get_n())` with `v`
353 *
354 * @throws std::invalid_argument if the replacement region exceeds the vector
355 */
356 Vector& set_subvector(const Vector& v, size_t i);
357
358 /// @brief Rvalue overload of @ref set_subvector (move-assigns components from `v`)
359 Vector& set_subvector(Vector&& v, size_t i);
360
361 /// @brief Append `rhs`: result becomes `[*this | rhs]`
362 Vector& append(const Vector& rhs);
363 /// @brief Rvalue overload of @ref append (moves components from `rhs`)
364 Vector& append(Vector&& rhs);
365
366 /// @brief Append after converting `rhs` to `Vector<T>`
367 template <typename U>
368 Vector& append(U&& rhs)
370
371 /// @brief Prepend `rhs`: result becomes `[rhs | *this]`
372 Vector& prepend(const Vector& rhs);
373 /// @brief Rvalue overload of @ref prepend (moves components from `rhs`)
374 Vector& prepend(Vector&& rhs);
375
376 /// @brief Prepend after converting `lhs` to `Vector<T>`
377 template <typename U>
378 Vector& prepend(U&& lhs)
380
381 /**
382 * @brief Delete the components whose indices appear in `v` and compact
383 *
384 * @param v Indices (deduplicated internally)
385 * @throws std::invalid_argument if any index in `v` is out of bounds
386 */
387 Vector& delete_components(const std::vector<size_t>& v);
388
389 /**
390 * @brief Delete component `i` (single-index convenience for @ref delete_components)
391 *
392 * @throws std::invalid_argument if `i` is out of bounds
393 */
394 Vector& delete_component(size_t i) { return delete_components({i}); }
395
396#ifdef CECCO_ERASURE_SUPPORT
397
398 /**
399 * @brief Mark every component whose index appears in `v` as erased
400 *
401 * @param v Indices (deduplicated internally)
402 * @throws std::invalid_argument if any index in `v` is out of bounds
403 *
404 * @warning Erased components must not participate in field arithmetic — see
405 * @ref CECCO_ERASURE_SUPPORT.
406 */
409
410 /// @brief Erase component `i` (single-index convenience for @ref erase_components)
416
417 /**
418 * @brief Clear the erasure flag on every component whose index appears in `v` (resets to T(0))
419 *
420 * @param v Indices (deduplicated internally)
421 * @throws std::invalid_argument if any index in `v` is out of bounds
422 */
425
426 /// @brief Un-erase component `i` (single-index convenience for @ref unerase_components)
432
433#endif
434
435 /// @brief Prepend zeros so the vector has length at least `n` (no-op if already ≄ `n`)
436 Vector& pad_front(size_t n);
437
438 /// @brief Append zeros so the vector has length at least `n` (no-op if already ≄ `n`)
439 Vector& pad_back(size_t n);
440
441 /** @} */
442
443 /** @name Transformations
444 * @{
445 */
446
447 /** @brief Circular left shift by `i` positions: `j ↦ (j āˆ’ i) mod n`. */
448 constexpr Vector& rotate_left(size_t i) noexcept;
449
450 /** @brief Circular right shift by `i` positions: `j ↦ (j + i) mod n`. */
451 constexpr Vector& rotate_right(size_t i) noexcept;
452
453 /** @brief Reverse component order: `i ↦ n āˆ’ 1 āˆ’ i`. */
454 constexpr Vector& reverse() noexcept;
455
456 /** @brief Set every component to `value`. */
457 constexpr Vector& fill(const T& value) noexcept;
458
459 /** @} */
460
461 /** @name Finite Field Specific Operations
462 * @{
463 */
464
465 /**
466 * @brief Interpret as a base-q integer, where q = T::get_size()
467 *
468 * Component `data[n āˆ’ 1 āˆ’ i]` is the i-th digit (least-significant first), so the value
469 * is Ī£ data[n āˆ’ 1 āˆ’ i] Ā· qⁱ.
470 *
471 * @note May overflow `size_t` for large vectors over large fields.
472 */
473 size_t as_integer() const noexcept
475
476 /**
477 * @brief Replace `*this` with the length-`n` base-q encoding of `value`
478 *
479 * Left inverse of @ref as_integer: `v.from_integer(v.as_integer(), v.get_n()) == v`.
480 *
481 * @throws std::out_of_range if `value` does not fit into n base-q digits
482 */
483 Vector& from_integer(size_t value, size_t n)
485
486 /**
487 * @brief Expand each component into its S-coefficient column
488 *
489 * @tparam S Subfield of T
490 * @return [T:S] Ɨ `get_n()` matrix whose column j is `data[j].as_vector<S>()`
491 *
492 * @note Under @ref CECCO_ERASURE_SUPPORT, an erased component j produces an entirely
493 * erased column j.
494 */
495 template <FiniteFieldType S>
496 Matrix<S> as_matrix() const
498
499 /**
500 * @brief Reshape into an `m Ɨ (n/m)` matrix, row-major
501 *
502 * Inverse of @ref Matrix::to_vector: index k of this vector lands at `(k/cols, k%cols)`
503 * with `cols = n/m`.
504 *
505 * @throws std::invalid_argument if `m` does not divide the vector length
506 */
507 Matrix<T> to_matrix(size_t m) const;
508
509 /** @} */
510
511 private:
512 std::vector<T> data;
513
514 /// @brief Cache for Hamming weight (invalidated by mutating operations)
515 mutable details::Cache<details::CacheEntry<Weight, size_t>> cache;
516
517 constexpr size_t calculate_weight() const noexcept
519};
520
521template <ComponentType T>
522Vector<T>::Vector(size_t n, const T& l) : data(n, l) {}
523
524template <ComponentType T>
525Vector<T>::Vector(const Vector<T>& other) : data(other.data), cache(other.cache) {}
526
527template <ComponentType T>
528constexpr Vector<T>::Vector(Vector<T>&& other) noexcept : data(std::move(other.data)), cache(std::move(other.cache)) {}
529
530template <ComponentType T>
531template <FiniteFieldType S>
532Vector<T>::Vector(const Matrix<S>& mat)
534{
535 const auto m = T().template as_vector<S>().get_n();
536 if (m != mat.get_m())
537 throw std::invalid_argument("trying to construct base field vector from subfield matrix of incompatible size");
538 data.resize(mat.get_n());
539 for (size_t i = 0; i < mat.get_n(); ++i) {
540 data[i] = T(mat.get_col(i));
541 }
542}
543
544template <ComponentType T>
545template <FiniteFieldType S>
549 for (size_t i = 0; i < other.get_n(); ++i) {
550 data[i] = T(other[i]); // Uses enhanced cross-field constructors
551 }
552}
553
554template <ComponentType T>
555Vector<T>::Vector(const Polynomial<T>& poly) {
556 data.resize(poly.degree() + 1);
557 for (size_t i = 0; i <= poly.degree(); ++i) {
558 data[i] = poly[i];
559 }
560}
561
562template <ComponentType T>
563constexpr Vector<T>& Vector<T>::operator=(const Vector<T>& rhs) {
564 if (this == &rhs) return *this;
565 data = rhs.data;
566 cache = rhs.cache;
567 return *this;
568}
569
570template <ComponentType T>
571constexpr Vector<T>& Vector<T>::operator=(Vector<T>&& rhs) noexcept {
572 if (this == &rhs) return *this;
573 data = std::move(rhs.data);
574 cache = std::move(rhs.cache);
575 return *this;
576}
577
578template <ComponentType T>
579constexpr Vector<T>& Vector<T>::operator=(const T& rhs) noexcept {
580 std::fill(data.begin(), data.end(), rhs);
581 cache.invalidate();
582 return *this;
583}
584
585template <ComponentType T>
586template <FiniteFieldType S>
590 for (size_t i = 0; i < other.get_n(); ++i) {
591 data[i] = T(other[i]); // Uses enhanced cross-field constructors
592 }
594 return *this;
595}
596
597template <ComponentType T>
598constexpr Vector<T> Vector<T>::operator-() const& {
599 Vector res(*this);
600 std::ranges::for_each(res.data, [](T& v) { v = -v; });
601 return res; // move elision
602}
603
604template <ComponentType T>
605constexpr Vector<T> Vector<T>::operator-() && noexcept {
606 std::ranges::for_each(data, [](T& v) { v = -v; });
607 return std::move(*this);
608}
609
610template <ComponentType T>
611Vector<T>& Vector<T>::operator+=(const Vector<T>& rhs) {
612 if (data.size() != rhs.data.size()) throw std::invalid_argument("trying to add vectors of different lengths");
613 std::transform(data.begin(), data.end(), rhs.data.begin(), data.begin(), std::plus<T>{});
614 cache.invalidate();
615 return *this;
616}
617
618template <ComponentType T>
619Vector<T>& Vector<T>::operator-=(const Vector<T>& rhs) {
620 if (data.size() != rhs.data.size())
621 throw std::invalid_argument(
622 "trying to subtract vectors of different "
623 "lengths");
624 std::transform(data.begin(), data.end(), rhs.data.begin(), data.begin(), std::minus<T>{});
625 cache.invalidate();
626 return *this;
627}
628
629template <ComponentType T>
630constexpr Vector<T>& Vector<T>::operator*=(const T& s) noexcept {
631 if (s == T(0)) {
632 fill(T(0));
633 cache.invalidate();
634 } else {
635 std::ranges::for_each(data, [&s](T& v) { v *= s; });
636 }
637
638 return *this;
639}
640
641template <ComponentType T>
642Vector<T>& Vector<T>::operator/=(const T& s) {
643 if (s == T(0)) throw std::invalid_argument("trying to divide components of vector by zero");
644 std::ranges::for_each(data, [&s](T& v) { v /= s; });
645 return *this;
646}
647
648template <ComponentType T>
649Vector<T>& Vector<T>::append(const Vector& rhs) {
650 if (this == &rhs) {
651 Vector tmp(rhs);
652 return append(std::move(tmp));
653 }
654
655 data.reserve(data.size() + rhs.data.size());
656 data.insert(data.end(), rhs.data.begin(), rhs.data.end());
657 cache.invalidate();
658 return *this;
659}
660
661template <ComponentType T>
662Vector<T>& Vector<T>::append(Vector&& rhs) {
663 if (this == &rhs) return *this;
664
665 data.reserve(data.size() + rhs.data.size());
666 data.insert(data.end(), std::make_move_iterator(rhs.data.begin()), std::make_move_iterator(rhs.data.end()));
667 cache.invalidate();
668 return *this;
669}
670
671template <ComponentType T>
672Vector<T>& Vector<T>::prepend(const Vector& lhs) {
673 if (this == &lhs) {
674 Vector tmp(lhs);
675 return prepend(std::move(tmp));
676 }
677
678 data.reserve(data.size() + lhs.data.size());
679 data.insert(data.begin(), lhs.data.begin(), lhs.data.end());
680 cache.invalidate();
681 return *this;
682}
683
684template <ComponentType T>
685Vector<T>& Vector<T>::prepend(Vector&& lhs) {
686 if (this == &lhs) return *this;
687
688 data.reserve(data.size() + lhs.data.size());
689 data.insert(data.begin(), std::make_move_iterator(lhs.data.begin()), std::make_move_iterator(lhs.data.end()));
690 cache.invalidate();
691 return *this;
692}
693
694template <ComponentType T>
695template <typename U>
696Vector<T>& Vector<T>::append(U&& rhs)
697 requires std::constructible_from<Vector<T>, U>
698{
699 if constexpr (std::same_as<std::remove_cvref_t<U>, Vector<T>>) {
700 return append(std::forward<U>(rhs));
701 } else {
702 Vector<T> tmp(std::forward<U>(rhs));
703 return append(std::move(tmp));
704 }
705}
706
707template <ComponentType T>
708template <typename U>
711{
712 if constexpr (std::same_as<std::remove_cvref_t<U>, Vector<T>>) {
713 return prepend(std::forward<U>(lhs));
714 } else {
715 Vector<T> tmp(std::forward<U>(lhs));
716 return prepend(std::move(tmp));
717 }
718}
719
720template <ComponentType T>
722 if (v.empty()) return *this;
723
724 // Validate and create sorted set of unique indices (deduplicate)
725 std::set<size_t> indices(v.begin(), v.end());
726 for (size_t idx : indices) {
727 if (idx >= data.size()) throw std::invalid_argument("trying to delete non-existent component");
728 }
729
730 // Single-pass filtering: copy components that should be kept
733
734 for (size_t i = 0; i < data.size(); ++i) {
736 }
737
738 data = std::move(new_data);
740 return *this;
741}
742
743#ifdef CECCO_ERASURE_SUPPORT
744
745template <ComponentType T>
748{
749 if (v.empty()) return *this;
750
751 // Validate and create sorted set of unique indices (deduplicate)
752 std::set<size_t> indices(v.begin(), v.end());
753 for (size_t idx : indices) {
754 if (idx >= data.size()) throw std::invalid_argument("trying to erase non-existent component");
755 }
756
757 std::for_each(indices.cbegin(), indices.cend(), [&](auto i) { data[i].erase(); });
758
760 return *this;
761}
762
763template <ComponentType T>
766{
767 if (v.empty()) return *this;
768
769 // Validate and create sorted set of unique indices (deduplicate)
770 std::set<size_t> indices(v.begin(), v.end());
771 for (size_t idx : indices) {
772 if (idx >= data.size()) throw std::invalid_argument("trying to un-erase non-existent component");
773 }
774
775 std::for_each(indices.cbegin(), indices.cend(), [&](auto i) { data[i].unerase(); });
776
778 return *this;
779}
780
781#endif
782
783template <ComponentType T>
785 Vector<T> res(lhs);
787 return res;
788}
789
790template <ComponentType T>
792 Vector<T> res(std::move(lhs));
794 return res;
795}
796
797template <ComponentType T>
799 if (n <= data.size()) return *this;
800
801 std::vector<T> new_data(n);
802 std::copy(data.begin(), data.end(), new_data.begin() + (n - data.size()));
803 data = std::move(new_data);
805 return *this;
806}
807
808template <ComponentType T>
810 if (n <= data.size()) return *this;
811
812 data.resize(n); // Automatically fills with T() (zeros)
814 return *this;
815}
816
817template <ComponentType T>
818constexpr Vector<T>& Vector<T>::fill(const T& value) noexcept {
819 std::fill(data.begin(), data.end(), value);
820 if (value == T(0))
821 cache.template set<Weight>(0);
822 else
823 cache.template set<Weight>(data.size());
824
825 return *this;
826}
827
828template <ComponentType T>
829constexpr Vector<T>& Vector<T>::rotate_left(size_t i) noexcept {
830 std::rotate(data.begin(), data.begin() + i, data.end());
831 return *this;
832}
833
834template <ComponentType T>
835constexpr Vector<T>& Vector<T>::rotate_right(size_t i) noexcept {
836 std::rotate(data.rbegin(), data.rbegin() + i, data.rend());
837 return *this;
838}
839
840template <ComponentType T>
841constexpr Vector<T>& Vector<T>::reverse() noexcept {
842 std::reverse(data.begin(), data.end());
843 return *this;
844}
845
846template <ComponentType T>
847template <typename U>
850{
851 if (i >= data.size()) throw std::invalid_argument("trying to access non-existent element");
852
853 T& old_value = data[i];
854
855 T new_value(std::forward<U>(c));
856 if (old_value == new_value) return *this;
858
860
861 return *this;
862}
863
864template <ComponentType T>
865const T& Vector<T>::operator[](size_t i) const {
866 if (i >= data.size()) throw std::invalid_argument("trying to access non-existent element");
867 return data[i];
868}
869
870template <ComponentType T>
872 if (i + w > data.size())
873 throw std::invalid_argument(
874 "trying to extract a subvector with incompatible "
875 "length");
876 Vector res;
877 res.data.assign(data.begin() + i, data.begin() + i + w);
878 return res;
879}
880
881template <ComponentType T>
883 if (i + w > data.size())
884 throw std::invalid_argument(
885 "trying to extract a subvector with incompatible "
886 "length");
887 data.resize(i + w);
888 data.erase(data.begin(), data.begin() + i);
890 return *this;
891}
892
893template <ComponentType T>
895 if (i + v.get_n() > data.size())
896 throw std::invalid_argument(
897 "trying to replace subvector with "
898 "vector of incompatible length");
899 for (size_t j = 0; j < v.get_n(); ++j) {
900 data[i + j] = v.data[j];
901 }
903 return *this;
904}
905
906template <ComponentType T>
908 if (i + v.get_n() > data.size())
909 throw std::invalid_argument(
910 "trying to replace subvector with "
911 "vector of incompatible length");
912 for (size_t j = 0; j < v.get_n(); ++j) {
913 data[i + j] = std::move(v.data[j]);
914 }
916 return *this;
917}
918
919template <ComponentType T>
920constexpr bool Vector<T>::is_zero() const noexcept
922{
923 return std::all_of(data.cbegin(), data.cend(), [](const T& x) { return x == T(0); });
924}
925
926template <ComponentType T>
927constexpr bool Vector<T>::is_pairwise_distinct() const
929{
930 for (size_t i = 0; i < data.size(); ++i) {
931 for (size_t j = i + 1; j < data.size(); ++j) {
932 if (data[i] == data[j]) return false;
933 }
934 }
935
936 return true;
937}
938
939template <ComponentType T>
940constexpr size_t Vector<T>::calculate_weight() const noexcept
942{
943 size_t res = data.size() - std::count(data.cbegin(), data.cend(), T(0));
944
945#ifdef CECCO_ERASURE_SUPPORT
946 if constexpr (FieldType<T>) res -= std::count_if(data.cbegin(), data.cend(), [](T x) { return x.is_erased(); });
947#endif
948
949 return res;
950}
951
952template <ComponentType T>
953constexpr size_t Vector<T>::burst_length() const noexcept {
954 // Find first non-zero element
955 auto first_nonzero = std::find_if(data.begin(), data.end(), [](const T& x) { return x != T(0); });
956
957 if (first_nonzero == data.end()) return 0; // All zeros
958
959 // Find last non-zero component(search from end)
960 auto last_nonzero = std::find_if(data.rbegin(), data.rend(), [](const T& x) { return x != T(0); });
961
964
965 return R - L + 1;
966}
967
968template <ComponentType T>
969constexpr size_t Vector<T>::cyclic_burst_length() const noexcept {
970 if (data.empty()) return 0;
971
972 // Handle all-zero vector
973 if (burst_length() == 0) return 0;
974
975 size_t n = data.size();
978
979 // First pass: find longest zero run within the vector
980 for (size_t i = 0; i < n; ++i) {
981 if (data[i] == T(0)) {
984 } else {
986 }
987 }
988
989 // Second pass: check for wraparound zeros only if needed
990 if (data[0] == T(0) && data[n - 1] == T(0)) {
991 // Count zeros from start
993 for (size_t i = 0; i < n && data[i] == T(0); ++i) {
995 }
996
997 // Count zeros from end
999 for (size_t i = n; i > 0 && data[i - 1] == T(0); --i) {
1001 }
1002
1003 // Update max if wraparound creates longer run
1004 if (zeros_from_start + zeros_from_end < n) { // Avoid double counting all-zero case
1006 }
1007 }
1008
1009 return n - max_zero_run;
1010}
1011
1012template <ComponentType T>
1013Vector<T>& Vector<T>::randomize() noexcept {
1014 if constexpr (FieldType<T>) {
1016 } else if constexpr (std::same_as<T, double>) {
1017 std::uniform_real_distribution<double> dist(-1.0, 1.0);
1018 std::ranges::for_each(data, [&](double& val) { val = dist(gen()); });
1019 } else if constexpr (std::same_as<T, std::complex<double>>) {
1020 std::uniform_real_distribution<double> dist(-1.0, 1.0);
1022 [&](std::complex<double>& val) { val = std::complex<double>(dist(gen()), dist(gen())); });
1023 } else if constexpr (SignedIntType<T>) {
1024 std::uniform_int_distribution<long long> dist(-100, 100);
1025 std::ranges::for_each(data, [&](T& val) { val = T(dist(gen())); });
1026 }
1027 cache.invalidate();
1028 return *this;
1029}
1030
1031template <ComponentType T>
1034{
1035 for (size_t i = 0; i < data.size(); ++i) {
1036 data[i] = T(0);
1038 }
1039 cache.invalidate();
1040 return *this;
1041}
1042
1043template <ComponentType T>
1046{
1047 constexpr size_t q = T::get_size();
1048 const size_t n = data.size();
1049 if (n > q)
1050 throw std::invalid_argument("Cannot generate pairwise distinct vector: length " +
1051 std::to_string(n) + " exceeds field size " + std::to_string(q));
1053 std::iota(labels.begin(), labels.end(), 0);
1054 std::shuffle(labels.begin(), labels.end(), gen());
1055 for (size_t i = 0; i < n; ++i)
1056 data[i] = T(labels[i]);
1057 cache.invalidate();
1058 return *this;
1059}
1060
1061template <ComponentType T>
1062size_t Vector<T>::as_integer() const noexcept
1064{
1065 size_t result = 0;
1066 for (size_t i = 0; i < data.size(); ++i)
1067 result += data[data.size() - i - 1].get_label() * sqm<size_t>(T::get_size(), i);
1068 return result;
1069}
1070
1071template <ComponentType T>
1074{
1075 constexpr size_t q = T::get_size();
1076
1077 Vector<T> v(n, T(0));
1078 for (size_t i = 0; i < n; ++i) {
1079 const size_t digit = value % q;
1080 value /= q;
1081 v.set_component(n - 1 - i, T(digit));
1082 }
1083 if (value != 0)
1084 throw std::out_of_range("Cannot convert integer value to base " + std::to_string(T::get_size()) +
1085 " vector of length " + std::to_string(n) + "!");
1086 *this = v;
1087 return *this;
1088}
1089
1090template <ComponentType T>
1091template <FiniteFieldType S>
1094{
1095 const auto v = data[0].template as_vector<S>();
1096 const auto m = v.get_n();
1097 Matrix<S> res(m, data.size());
1098
1099 Matrix<S> temp(v);
1100 temp.transpose();
1101 res.set_submatrix(0, 0, temp);
1102
1103 for (size_t i = 1; i < data.size(); ++i) {
1104#ifdef CECCO_ERASURE_SUPPORT
1105 if (data[i].is_erased()) {
1106 for (size_t j = 0; j < m; ++j) {
1108 }
1109 } else {
1110 Matrix<S> temp(data[i].template as_vector<S>());
1111 temp.transpose();
1112 res.set_submatrix(0, i, temp);
1113 }
1114#else
1115 Matrix<S> temp(data[i].template as_vector<S>());
1116 temp.transpose();
1117 res.set_submatrix(0, i, temp);
1118#endif
1119 }
1120
1121 return res;
1122}
1123
1124template <ComponentType T>
1126 if (m == 0) throw std::invalid_argument("Trying to convert vector into a matrix with zero rows");
1127 if (get_n() % m != 0)
1128 throw std::invalid_argument(std::string("Cannot convert vector into a matrix with ") + std::to_string(m) +
1129 std::string(" rows, number of rows m (") + std::to_string(m) +
1130 std::string(") is not a divisor of vector length n (") + std::to_string(get_n()) +
1131 ")!");
1132
1133 const size_t cols = get_n() / m;
1134 Matrix<T> M(m, cols);
1135 size_t k = 0;
1136 for (size_t i = 0; i < m; ++i) {
1137 for (size_t j = 0; j < cols; ++j) {
1138 M.set_component(i, j, data[k]);
1139 ++k;
1140 }
1141 }
1142 return M;
1143}
1144
1145/* free functions wrt. Vector */
1146
1147/*
1148 * vector + vector
1149 */
1150
1151template <ComponentType T>
1152constexpr Vector<T> operator+(const Vector<T>& lhs, const Vector<T>& rhs) {
1153 Vector res(lhs);
1154 res += rhs;
1155 return res;
1156}
1157
1158template <ComponentType T>
1159constexpr Vector<T> operator+(Vector<T>&& lhs, const Vector<T>& rhs) {
1160 Vector res(std::move(lhs));
1161 res += rhs;
1162 return res;
1163}
1164
1165template <ComponentType T>
1166constexpr Vector<T> operator+(const Vector<T>& lhs, Vector<T>&& rhs) {
1167 Vector res(std::move(rhs));
1168 res += lhs;
1169 return res;
1170}
1171
1172template <ComponentType T>
1173constexpr Vector<T> operator+(Vector<T>&& lhs, Vector<T>&& rhs) {
1174 Vector res(std::move(lhs));
1175 res += rhs;
1176 return res;
1177}
1178
1179/*
1180 * vector - vector
1181 */
1182
1183template <ComponentType T>
1184constexpr Vector<T> operator-(const Vector<T>& lhs, const Vector<T>& rhs) {
1185 Vector res(lhs);
1186 res -= rhs;
1187 return res;
1188}
1189
1190template <ComponentType T>
1191constexpr Vector<T> operator-(Vector<T>&& lhs, const Vector<T>& rhs) {
1192 Vector res(std::move(lhs));
1193 res -= rhs;
1194 return res;
1195}
1196
1197template <ComponentType T>
1198constexpr Vector<T> operator-(const Vector<T>& lhs, Vector<T>&& rhs) {
1199 Vector res(-std::move(rhs));
1200 res += lhs;
1201 return res;
1202}
1203
1204template <ComponentType T>
1205constexpr Vector<T> operator-(Vector<T>&& lhs, Vector<T>&& rhs) {
1206 Vector res(std::move(lhs));
1207 res -= rhs;
1208 return res;
1209}
1210
1211/*
1212 * vector * T
1213 */
1214
1215template <ComponentType T>
1216constexpr Vector<T> operator*(const Vector<T>& lhs, const T& rhs) noexcept {
1217 Vector res(lhs);
1218 res *= rhs;
1219 return res;
1220}
1221
1222template <ComponentType T>
1223constexpr Vector<T> operator*(Vector<T>&& lhs, const T& rhs) noexcept {
1224 Vector res(std::move(lhs));
1225 res *= rhs;
1226 return res;
1227}
1228
1229/*
1230 * T * vector
1231 */
1232
1233template <ComponentType T>
1234constexpr Vector<T> operator*(const T& lhs, const Vector<T>& rhs) noexcept {
1235 Vector res(rhs);
1236 res *= lhs;
1237 return res;
1238}
1239
1240template <ComponentType T>
1241constexpr Vector<T> operator*(const T& lhs, Vector<T>&& rhs) noexcept {
1242 Vector res(std::move(rhs));
1243 res *= lhs;
1244 return res;
1245}
1246
1247/*
1248 * vector / T
1249 */
1250
1251template <ComponentType T>
1252constexpr Vector<T> operator/(const Vector<T>& lhs, const T& rhs) {
1253 Vector res(lhs);
1254 res /= rhs;
1255 return res;
1256}
1257
1258template <FieldType T>
1259constexpr Vector<T> operator/(Vector<T>&& lhs, const T& rhs) {
1260 Vector res(std::move(lhs));
1261 res /= rhs;
1262 return res;
1263}
1264
1265/// @brief Discrete linear convolution (= coefficients of `Polynomial(v) * Polynomial(w)`)
1266template <ComponentType T>
1267Vector<T> convolve(const Vector<T>& v, const Vector<T>& w) {
1268 return Vector(Polynomial(v) * Polynomial(w));
1269}
1270
1271template <ComponentType T>
1273 Vector<T> result = v;
1274 result.randomize();
1275 return result;
1276}
1277
1278template <ComponentType T>
1280 Vector<T> result = std::move(v);
1281 result.randomize();
1282 return result;
1283}
1284
1285template <ComponentType T>
1286Vector<T> set_component(const Vector<T>& v, size_t i, const T& c) {
1287 Vector<T> res(v);
1288 res.set_component(i, c);
1289 return res;
1290}
1291
1292template <ComponentType T>
1294 Vector<T> res(std::move(v));
1295 res.set_component(i, c);
1296 return res;
1297}
1298
1299template <ComponentType T>
1301 return v.get_subvector(start, end);
1302}
1303
1304template <ComponentType T>
1306 return std::move(v).get_subvector(start, end);
1307}
1308
1309template <ComponentType T>
1310constexpr Vector<T> set_subvector(Vector<T> v, size_t start, const Vector<T>& w) {
1311 return v.set_subvector(w, start);
1312}
1313
1314template <ComponentType T>
1315constexpr Vector<T> set_subvector(Vector<T>&& v, size_t start, const Vector<T>& w) {
1316 return std::move(v).set_subvector(w, start);
1317}
1318
1319template <ComponentType T>
1320constexpr Vector<T> concatenate(const Vector<T>& lhs, const Vector<T>& rhs) {
1321 Vector res(lhs);
1322 res.append(rhs);
1323 return res;
1324}
1325
1326template <ComponentType T>
1327constexpr Vector<T> concatenate(Vector<T>&& lhs, const Vector<T>& rhs) {
1328 Vector res(std::move(lhs));
1329 res.append(rhs);
1330 return res;
1331}
1332
1333template <ComponentType T>
1334constexpr Vector<T> concatenate(const Vector<T>& lhs, Vector<T>&& rhs) {
1335 Vector res(std::move(rhs));
1336 res.prepend(lhs);
1337 return res;
1338}
1339
1340template <ComponentType T>
1341constexpr Vector<T> concatenate(Vector<T>&& lhs, Vector<T>&& rhs) {
1342 Vector res(std::move(lhs));
1343 res.append(rhs);
1344 return res;
1345}
1346
1347template <ComponentType T>
1349 Vector res(lhs);
1351 return res;
1352}
1353
1354template <ComponentType T>
1356 Vector res(std::move(lhs));
1358 return res;
1359}
1360
1361#ifdef CECCO_ERASURE_SUPPORT
1362
1363template <ComponentType T>
1365 Vector res(lhs);
1367 return res;
1368}
1369
1370template <ComponentType T>
1372 Vector res(std::move(lhs));
1374 return res;
1375}
1376
1377template <ComponentType T>
1379 Vector res(lhs);
1381 return res;
1382}
1383
1384template <ComponentType T>
1386 Vector res(std::move(lhs));
1388 return res;
1389}
1390
1391template <ComponentType T>
1393 Vector res(lhs);
1395 return res;
1396}
1397
1398template <ComponentType T>
1400 Vector res(std::move(lhs));
1402 return res;
1403}
1404
1405template <ComponentType T>
1407 Vector res(lhs);
1409 return res;
1410}
1411
1412template <ComponentType T>
1414 Vector res(std::move(lhs));
1416 return res;
1417}
1418
1419#endif
1420
1421template <ComponentType T>
1422constexpr Vector<T> pad_front(const Vector<T>& v, size_t n) {
1423 Vector res(v);
1424 res.pad_front(n);
1425 return res;
1426}
1427
1428template <ComponentType T>
1429constexpr Vector<T> pad_front(Vector<T>&& v, size_t n) {
1430 Vector res(std::move(v));
1431 res.pad_front(n);
1432 return res;
1433}
1434
1435template <ComponentType T>
1436constexpr Vector<T> pad_back(const Vector<T>& v, size_t n) {
1437 Vector res(v);
1438 res.pad_back(n);
1439 return res;
1440}
1441
1442template <ComponentType T>
1443constexpr Vector<T> pad_back(Vector<T>&& v, size_t n) {
1444 Vector res(std::move(v));
1445 res.pad_back(n);
1446 return res;
1447}
1448
1449template <ComponentType T>
1450constexpr Vector<T> rotate_left(const Vector<T>& v, size_t i) {
1451 Vector res(v);
1452 res.rotate_left(i);
1453 return res;
1454}
1455
1456template <ComponentType T>
1458 Vector res(std::move(v));
1459 res.rotate_left(i);
1460 return res;
1461}
1462
1463template <ComponentType T>
1464constexpr Vector<T> rotate_right(const Vector<T>& v, size_t i) {
1465 Vector res(v);
1467 return res;
1468}
1469
1470template <ComponentType T>
1472 Vector res(std::move(v));
1474 return res;
1475}
1476
1477template <ComponentType T>
1478constexpr Vector<T> reverse(const Vector<T>& v) {
1479 Vector res(v);
1480 res.reverse();
1481 return res;
1482}
1483
1484template <ComponentType T>
1485constexpr Vector<T> reverse(Vector<T>&& v) {
1486 Vector res(std::move(v));
1487 res.reverse();
1488 return res;
1489}
1490
1491template <ComponentType T>
1492constexpr Vector<T> fill(const Vector<T>& v, const T& value) {
1493 Vector<T> res(v);
1494 res.fill(value);
1495 return res;
1496}
1497
1498template <ComponentType T>
1499constexpr Vector<T> fill(Vector<T>&& v, const T& value) {
1500 Vector<T> res(std::move(v));
1501 res.fill(value);
1502 return res;
1503}
1504
1505/**
1506 * @brief Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i]
1507 *
1508 * For complex inputs this is the standard product (not the conjugate inner product).
1509 *
1510 * @throws std::invalid_argument if lengths differ
1511 */
1512template <ComponentType T>
1513T inner_product(const Vector<T>& lhs, const Vector<T>& rhs) {
1514 if (lhs.get_n() != rhs.get_n())
1515 throw std::invalid_argument(
1516 "trying to calculate inner product of "
1517 "vectors of different lengths");
1518 return std::inner_product(lhs.data.cbegin(), lhs.data.cend(), rhs.data.begin(), T(0));
1519}
1520
1521/**
1522 * @brief Schur (component-wise) product
1523 * @throws std::invalid_argument if lengths differ
1524 */
1525template <ComponentType T>
1527 if (lhs.get_n() != rhs.get_n())
1528 throw std::invalid_argument(
1529 "trying to calculate Schur product of "
1530 "vectors of different lengths");
1531 auto res = lhs;
1532 for (size_t i = 0; i < res.get_n(); ++i) res.set_component(i, res[i] * rhs[i]);
1533 return res;
1534}
1535
1536template <ReliablyComparableType T>
1537constexpr bool operator==(const Vector<T>& lhs, const Vector<T>& rhs) noexcept {
1538 if (lhs.data.size() != rhs.data.size()) return false;
1539 return lhs.data == rhs.data;
1540}
1541
1542template <ReliablyComparableType T>
1543constexpr bool operator!=(const Vector<T>& lhs, const Vector<T>& rhs) noexcept {
1544 return !(lhs == rhs);
1545}
1546
1547/**
1548 * @brief Length-`length` vector with `T(1)` at index `i` and zeros elsewhere
1549 * @throws std::invalid_argument if `i >= length`
1550 */
1551template <ComponentType T>
1553 if (i >= length) throw std::invalid_argument("trying to create invalid unit vector");
1554 Vector<T> res(length);
1555 res.set_component(i, T(1));
1556 res.cache.template set<Vector<T>::Weight>(1);
1557 return res;
1558}
1559
1560/** @brief Stream output as `( cā‚€, c₁, …, cₙ₋₁ )`. */
1561template <ComponentType T>
1562std::ostream& operator<<(std::ostream& os, const Vector<T>& rhs) noexcept {
1563 os << "( ";
1564 for (auto it = rhs.data.cbegin(); it != rhs.data.cend(); ++it) {
1565 os << *it;
1566 if (it != rhs.data.cend() - 1) {
1567 os << ", ";
1568 }
1569 }
1570 os << " )";
1571 return os;
1572}
1573
1574/** @name Error control coding-related functions
1575 * @{
1576 */
1577
1578/** @brief Hamming weight; see @ref Vector::wH for semantics. */
1579template <ReliablyComparableType T>
1580constexpr size_t wH(const Vector<T>& v) noexcept {
1581 return v.wH();
1582}
1583
1584/** @brief Support; see @ref Vector::supp for semantics. */
1585template <ReliablyComparableType T>
1587 return v.supp();
1588}
1589
1590/**
1591 * @brief Hamming distance dā‚•(lhs, rhs) = wā‚•(lhs āˆ’ rhs)
1592 *
1593 * Number of positions in which `lhs` and `rhs` differ. Under @ref CECCO_ERASURE_SUPPORT,
1594 * positions where either side is erased are not counted (subtraction propagates the
1595 * erasure marker, and @ref wH skips erased components).
1596 *
1597 * @throws std::invalid_argument if lengths differ
1598 */
1599template <ReliablyComparableType T>
1600size_t dH(const Vector<T>& lhs, const Vector<T>& rhs) {
1601 if (lhs.get_n() != rhs.get_n())
1602 throw std::invalid_argument(
1603 "trying to calculate Hamming distance between vectors of different "
1604 "lengths");
1605 return (lhs - rhs).wH();
1606}
1607
1608// rvalue overloads of dH share semantics with the const&,const& version above.
1609template <ReliablyComparableType T>
1610size_t dH(Vector<T>&& lhs, const Vector<T>& rhs) {
1611 if (lhs.get_n() != rhs.get_n())
1612 throw std::invalid_argument(
1613 "trying to calculate Hamming distance between vectors of different "
1614 "lengths");
1615 return (std::move(lhs) - rhs).wH();
1616}
1617
1618template <ReliablyComparableType T>
1619size_t dH(const Vector<T>& lhs, Vector<T>&& rhs) {
1620 if (lhs.get_n() != rhs.get_n())
1621 throw std::invalid_argument(
1622 "trying to calculate Hamming distance between vectors of different "
1623 "lengths");
1624 return (lhs - std::move(rhs)).wH();
1625}
1626template <ReliablyComparableType T>
1628 if (lhs.get_n() != rhs.get_n())
1629 throw std::invalid_argument(
1630 "trying to calculate Hamming distance between vectors of different "
1631 "lengths");
1632 return (std::move(lhs) - std::move(rhs)).wH();
1633}
1634
1635/** @brief Burst length; see @ref Vector::burst_length for semantics. */
1636template <ReliablyComparableType T>
1637constexpr size_t burst_length(const Vector<T>& v) noexcept {
1638 return v.burst_length();
1639}
1640
1641/** @brief Cyclic burst length; see @ref Vector::cyclic_burst_length for semantics. */
1642template <ReliablyComparableType T>
1643constexpr size_t cyclic_burst_length(const Vector<T>& v) noexcept {
1644 return v.cyclic_burst_length();
1645}
1646
1647/**
1648 * @brief Euclidean distance ‖lhs āˆ’ rhs‖₂ = √(Σᵢ |lhs[i] āˆ’ rhs[i]|²) for complex vectors
1649 * @throws std::invalid_argument if lengths differ
1650 */
1651inline double dE(const Vector<std::complex<double>>& lhs, const Vector<std::complex<double>>& rhs) {
1652 if (lhs.get_n() != rhs.get_n())
1653 throw std::invalid_argument(
1654 "trying to calculate euclidean distance between vectors of different "
1655 "lengths");
1656
1658 lhs.data.begin(), lhs.data.end(), rhs.data.begin(), 0.0, std::plus<double>{},
1659 [](const std::complex<double>& l, const std::complex<double>& r) { return std::norm(l - r); });
1660
1661 return sqrt(sum_of_squares);
1662}
1663
1664/** @} */
1665
1666} // namespace CECCO
1667
1668#endif
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.
Vector v = (vā‚€, v₁, …, vₙ₋₁) over a CECCO::ComponentType.
Definition vectors.hpp:115
const T & operator[](size_t i) const
Read access to component i.
Definition vectors.hpp:865
constexpr bool is_zero() const noexcept
True iff every component equals T(0); also true for the empty vector.
Definition vectors.hpp:920
Vector(size_t n)
Length-n vector with default-initialised components (T() = 0).
Definition vectors.hpp:137
Vector & delete_components(const std::vector< size_t > &v)
Delete the components whose indices appear in v and compact.
Definition vectors.hpp:721
constexpr bool is_pairwise_distinct() const
True iff no two components are equal.
Definition vectors.hpp:927
Vector(const Polynomial< T > &poly)
From polynomial coefficients: component i becomes poly[i].
Definition vectors.hpp:555
Matrix< T > to_matrix(size_t m) const
Reshape into an m Ɨ (n/m) matrix, row-major.
Definition vectors.hpp:1125
constexpr Vector & fill(const T &value) noexcept
Set every component to value.
Definition vectors.hpp:818
Matrix< S > as_matrix() const
Expand each component into its S-coefficient column.
Definition vectors.hpp:1092
constexpr Vector & operator=(const T &rhs) noexcept
Set every component to rhs.
Definition vectors.hpp:579
std::vector< size_t > supp() const
Sorted indices of non-zero components (empty vector for an all-zero input).
Definition vectors.hpp:296
friend constexpr T inner_product(const Vector< T > &lhs, const Vector< T > &rhs)
Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i].
Definition vectors.hpp:1513
friend constexpr Vector< T > Schur_product(const Vector< T > &lhs, const Vector< T > &rhs)
Schur (component-wise) product.
Definition vectors.hpp:1526
constexpr Vector & rotate_right(size_t i) noexcept
Circular right shift by i positions: j ↦ (j + i) mod n.
Definition vectors.hpp:835
size_t as_integer() const noexcept
Interpret as a base-q integer, where q = T::get_size().
Definition vectors.hpp:1062
Vector & set_component(size_t i, U &&c)
Set component i by perfect-forwarding c.
constexpr Vector(Vector &&other) noexcept
Definition vectors.hpp:528
friend constexpr bool operator==(const Vector< U > &lhs, const Vector< U > &rhs) noexcept
constexpr Vector & reverse() noexcept
Reverse component order: i ↦ n āˆ’ 1 āˆ’ i.
Definition vectors.hpp:841
Vector & randomize_pairwise_distinct()
Fill with pairwise distinct field elements (shuffled labels 0, …, q āˆ’ 1).
Definition vectors.hpp:1044
Vector & set_subvector(const Vector &v, size_t i)
Overwrite components [i, i + v.get_n()) with v.
Definition vectors.hpp:894
Vector & prepend(Vector &&rhs)
Rvalue overload of prepend (moves components from rhs).
Definition vectors.hpp:685
Vector & append(const Vector &rhs)
Append rhs: result becomes [*this | rhs].
Definition vectors.hpp:649
constexpr Vector & operator=(const Vector &rhs)
Definition vectors.hpp:563
Vector & append(Vector &&rhs)
Rvalue overload of append (moves components from rhs).
Definition vectors.hpp:662
Vector get_subvector(size_t i, size_t w) const &
Extract the contiguous subvector [i, i + w).
Definition vectors.hpp:871
Vector & operator+=(const Vector &rhs)
Component-wise addition this[i] += rhs[i].
Definition vectors.hpp:611
Vector & pad_back(size_t n)
Append zeros so the vector has length at least n (no-op if already ≄ n).
Definition vectors.hpp:809
constexpr Vector operator+() const &noexcept
Unary + (lvalue): returns a copy.
Definition vectors.hpp:204
Vector & randomize_nonzero() noexcept
Like randomize but every component is guaranteed non-zero.
Definition vectors.hpp:1032
Vector & operator-=(const Vector &rhs)
Component-wise subtraction this[i] -= rhs[i].
Definition vectors.hpp:619
Vector & delete_component(size_t i)
Delete component i (single-index convenience for delete_components).
Definition vectors.hpp:394
constexpr Vector operator-() const &
Unary āˆ’ (lvalue): returns a new vector with each component negated.
Definition vectors.hpp:598
constexpr Vector & operator=(Vector &&rhs) noexcept
Definition vectors.hpp:571
constexpr bool is_empty() const noexcept
True iff length is 0 (distinct from a non-empty all-zero vector).
Definition vectors.hpp:281
Vector & from_integer(size_t value, size_t n)
Replace *this with the length-n base-q encoding of value.
Definition vectors.hpp:1072
Vector & get_subvector(size_t i, size_t w) &&
In-place rvalue overload of get_subvector (truncates to [i, i + w)).
Definition vectors.hpp:882
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:278
Vector & randomize() noexcept
Replace components with random values appropriate for T.
Definition vectors.hpp:1013
Vector(const Vector &other)
Definition vectors.hpp:525
constexpr Vector & rotate_left(size_t i) noexcept
Circular left shift by i positions: j ↦ (j āˆ’ i) mod n.
Definition vectors.hpp:829
Vector(const Matrix< S > &mat)
From a matrix over a subfield: each column becomes one T component.
Definition vectors.hpp:532
Vector & pad_front(size_t n)
Prepend zeros so the vector has length at least n (no-op if already ≄ n).
Definition vectors.hpp:798
constexpr size_t cyclic_burst_length() const noexcept
Cyclic burst length: shortest circular arc covering all non-zero positions; 0 for an all-zero or empt...
Definition vectors.hpp:969
Vector(size_t n, const T &l)
Length-n vector with every component equal to l.
Definition vectors.hpp:522
constexpr Vector & operator*=(const T &s) noexcept
Multiply every component by the scalar s.
Definition vectors.hpp:630
Vector & set_subvector(Vector &&v, size_t i)
Rvalue overload of set_subvector (move-assigns components from v).
Definition vectors.hpp:907
constexpr Vector() noexcept
Default constructor: empty vector (length 0).
Definition vectors.hpp:134
constexpr Vector operator-() &&noexcept
Unary āˆ’ (rvalue): negates components in place.
Definition vectors.hpp:605
Vector & append(U &&rhs)
Append after converting rhs to Vector<T>.
Vector & prepend(const Vector &rhs)
Prepend rhs: result becomes [rhs | *this].
Definition vectors.hpp:672
Vector & operator/=(const T &s)
Divide every component by the scalar s.
Definition vectors.hpp:642
constexpr Vector operator+() &&noexcept
Unary + (rvalue): returns the rvalue itself.
Definition vectors.hpp:206
Vector(const std::initializer_list< T > &l)
From an initializer list of components, in order.
Definition vectors.hpp:143
Contains implementation details not to be exposed to the user. Functions and classes here may change ...
Definition blocks.hpp:59
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:1526
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:1651
T inner_product(const Vector< T > &lhs, const Vector< T > &rhs)
Inner product ⟨lhs, rhs⟩ = Σᵢ lhs[i] · rhs[i].
Definition vectors.hpp:1513
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:1552