#include "field.h"
#include "poly.h"
#include <algorithm>
#include <stdexcept>
#include <type_traits>
#include <utility>
namespace librscpp {
namespace {
template <typename Field>
Poly<Field> build_generator(const Field& field, int degree)
{
Poly<Field> generator(field, degree + 1);
generator.set(1);
Poly<Field> term(field, {1, 0});
for (int d = 1; d <= degree; d++) {
term.coef(0) = field.neg(field.exp(d - 1 + field.fcr()));
generator.mul(term);
}
return generator;
}
}
template <typename Field, std::integral T>
void encode(const Field& field, std::span<const T> data, std::span<T> parity)
{
if (data.empty() || parity.empty())
throw std::invalid_argument("Invalid number of data or parity symbols");
using FT = typename Field::value_type;
std::vector<FT> tmp;
std::span<FT const> data_;
if constexpr (!std::is_same_v<T, FT>) {
tmp.resize(data.size());
std::ranges::transform(data, tmp.begin(), [](T sym) { return static_cast<FT>(static_cast<std::make_unsigned_t<T>>(sym)); });
data_ = tmp;
} else {
data_ = std::span(data);
}
auto generator = build_generator(field, parity.size());
Poly<Field> C(field, data_, data_.size() + parity.size());
Poly<Field> _(field, data_.size() + 1);
C.resize(data_.size() + parity.size()); C.div(generator, _);
C.neg();
auto out = std::fill_n(parity.begin(), parity.size() - C.size(), 0);
std::ranges::transform(C, out, [](FT symbol) { return static_cast<T>(symbol); });
}
template <typename Field, std::ranges::contiguous_range R, std::ranges::contiguous_range S>
requires std::same_as<std::ranges::range_value_t<R>, std::ranges::range_value_t<S>>
void encode(const Field& field, R&& data, S&& parity)
{
using T = std::ranges::range_value_t<R>;
encode<Field, T>(field, std::span<const T>(data), std::span<T>(parity));
}
template <typename Field, std::ranges::contiguous_range R>
void encode_inplace(const Field& field, R&& codeword, int num_parity)
{
std::span cw(codeword);
auto data_size = std::ranges::ssize(codeword) - num_parity;
if (data_size <= 0 || num_parity <= 0)
throw std::invalid_argument("Invalid number of data or parity symbols");
encode(field, cw.subspan(0, data_size), cw.subspan(data_size));
}
template <typename Out = void, typename Field, std::ranges::contiguous_range In>
[[nodiscard]]
auto encode(const Field& field, In&& data, int num_parity)
{
using T = std::conditional_t<std::is_void_v<Out>, std::remove_cvref_t<In>, Out>;
T res;
res.reserve(std::ranges::size(data) + num_parity);
res.insert(res.end(), std::ranges::begin(data), std::ranges::end(data));
res.resize(res.size() + num_parity);
encode_inplace(field, res, num_parity);
return res;
}
}