kopia lustrzana https://github.com/AlexandreRouma/SDRPlusPlus
256 wiersze
9.8 KiB
C
256 wiersze
9.8 KiB
C
#include "correct/reed-solomon/polynomial.h"
|
|
|
|
polynomial_t polynomial_create(unsigned int order) {
|
|
polynomial_t polynomial;
|
|
polynomial.coeff = malloc(sizeof(field_element_t) * (order + 1));
|
|
polynomial.order = order;
|
|
return polynomial;
|
|
}
|
|
|
|
void polynomial_destroy(polynomial_t polynomial) {
|
|
free(polynomial.coeff);
|
|
}
|
|
|
|
// if you want a full multiplication, then make res.order = l.order + r.order
|
|
// but if you just care about a lower order, e.g. mul mod x^i, then you can select
|
|
// fewer coefficients
|
|
void polynomial_mul(field_t field, polynomial_t l, polynomial_t r, polynomial_t res) {
|
|
// perform an element-wise multiplication of two polynomials
|
|
memset(res.coeff, 0, sizeof(field_element_t) * (res.order + 1));
|
|
for (unsigned int i = 0; i <= l.order; i++) {
|
|
if (i > res.order) {
|
|
continue;
|
|
}
|
|
unsigned int j_limit = (r.order > res.order - i) ? res.order - i : r.order;
|
|
for (unsigned int j = 0; j <= j_limit; j++) {
|
|
// e.g. alpha^5*x * alpha^37*x^2 --> alpha^42*x^3
|
|
res.coeff[i + j] = field_add(field, res.coeff[i + j], field_mul(field, l.coeff[i], r.coeff[j]));
|
|
}
|
|
}
|
|
}
|
|
|
|
void polynomial_mod(field_t field, polynomial_t dividend, polynomial_t divisor, polynomial_t mod) {
|
|
// find the polynomial remainder of dividend mod divisor
|
|
// do long division and return just the remainder (written to mod)
|
|
|
|
if (mod.order < dividend.order) {
|
|
// mod.order must be >= dividend.order (scratch space needed)
|
|
// this is an error -- catch it in debug?
|
|
return;
|
|
}
|
|
// initialize remainder as dividend
|
|
memcpy(mod.coeff, dividend.coeff, sizeof(field_element_t) * (dividend.order + 1));
|
|
|
|
// XXX make sure divisor[divisor_order] is nonzero
|
|
field_logarithm_t divisor_leading = field.log[divisor.coeff[divisor.order]];
|
|
// long division steps along one order at a time, starting at the highest order
|
|
for (unsigned int i = dividend.order; i > 0; i--) {
|
|
// look at the leading coefficient of dividend and divisor
|
|
// if leading coefficient of dividend / leading coefficient of divisor is q
|
|
// then the next row of subtraction will be q * divisor
|
|
// if order of q < 0 then what we have is the remainder and we are done
|
|
if (i < divisor.order) {
|
|
break;
|
|
}
|
|
if (mod.coeff[i] == 0) {
|
|
continue;
|
|
}
|
|
unsigned int q_order = i - divisor.order;
|
|
field_logarithm_t q_coeff = field_div_log(field, field.log[mod.coeff[i]], divisor_leading);
|
|
|
|
// now that we've chosen q, multiply the divisor by q and subtract from
|
|
// our remainder. subtracting in GF(2^8) is XOR, just like addition
|
|
for (unsigned int j = 0; j <= divisor.order; j++) {
|
|
if (divisor.coeff[j] == 0) {
|
|
continue;
|
|
}
|
|
// all of the multiplication is shifted up by q_order places
|
|
mod.coeff[j + q_order] = field_add(field, mod.coeff[j + q_order],
|
|
field_mul_log_element(field, field.log[divisor.coeff[j]], q_coeff));
|
|
}
|
|
}
|
|
}
|
|
|
|
void polynomial_formal_derivative(field_t field, polynomial_t poly, polynomial_t der) {
|
|
// if f(x) = a(n)*x^n + ... + a(1)*x + a(0)
|
|
// then f'(x) = n*a(n)*x^(n-1) + ... + 2*a(2)*x + a(1)
|
|
// where n*a(n) = sum(k=1, n, a(n)) e.g. the nth sum of a(n) in GF(2^8)
|
|
|
|
// assumes der.order = poly.order - 1
|
|
memset(der.coeff, 0, sizeof(field_element_t) * (der.order + 1));
|
|
for (unsigned int i = 0; i <= der.order; i++) {
|
|
// we're filling in the ith power of der, so we look ahead one power in poly
|
|
// f(x) = a(i + 1)*x^(i + 1) -> f'(x) = (i + 1)*a(i + 1)*x^i
|
|
// where (i + 1)*a(i + 1) is the sum of a(i + 1) (i + 1) times, not the product
|
|
der.coeff[i] = field_sum(field, poly.coeff[i + 1], i + 1);
|
|
}
|
|
}
|
|
|
|
field_element_t polynomial_eval(field_t field, polynomial_t poly, field_element_t val) {
|
|
// evaluate the polynomial poly at a particular element val
|
|
if (val == 0) {
|
|
return poly.coeff[0];
|
|
}
|
|
|
|
field_element_t res = 0;
|
|
|
|
// we're going to start at 0th order and multiply by val each time
|
|
field_logarithm_t val_exponentiated = field.log[1];
|
|
field_logarithm_t val_log = field.log[val];
|
|
|
|
for (unsigned int i = 0; i <= poly.order; i++) {
|
|
if (poly.coeff[i] != 0) {
|
|
// multiply-accumulate by the next coeff times the next power of val
|
|
res = field_add(field, res,
|
|
field_mul_log_element(field, field.log[poly.coeff[i]], val_exponentiated));
|
|
}
|
|
// now advance to the next power
|
|
val_exponentiated = field_mul_log(field, val_exponentiated, val_log);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
field_element_t polynomial_eval_lut(field_t field, polynomial_t poly, const field_logarithm_t *val_exp) {
|
|
// evaluate the polynomial poly at a particular element val
|
|
// in this case, all of the logarithms of the successive powers of val have been precalculated
|
|
// this removes the extra work we'd have to do to calculate val_exponentiated each time
|
|
// if this function is to be called on the same val multiple times
|
|
if (val_exp[0] == 0) {
|
|
return poly.coeff[0];
|
|
}
|
|
|
|
field_element_t res = 0;
|
|
|
|
for (unsigned int i = 0; i <= poly.order; i++) {
|
|
if (poly.coeff[i] != 0) {
|
|
// multiply-accumulate by the next coeff times the next power of val
|
|
res = field_add(field, res,
|
|
field_mul_log_element(field, field.log[poly.coeff[i]], val_exp[i]));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
field_element_t polynomial_eval_log_lut(field_t field, polynomial_t poly_log, const field_logarithm_t *val_exp) {
|
|
// evaluate the log_polynomial poly at a particular element val
|
|
// like polynomial_eval_lut, the logarithms of the successive powers of val have been
|
|
// precomputed
|
|
if (val_exp[0] == 0) {
|
|
if (poly_log.coeff[0] == 0) {
|
|
// special case for the non-existant log case
|
|
return 0;
|
|
}
|
|
return field.exp[poly_log.coeff[0]];
|
|
}
|
|
|
|
field_element_t res = 0;
|
|
|
|
for (unsigned int i = 0; i <= poly_log.order; i++) {
|
|
// using 0 as a sentinel value in log -- log(0) is really -inf
|
|
if (poly_log.coeff[i] != 0) {
|
|
// multiply-accumulate by the next coeff times the next power of val
|
|
res = field_add(field, res,
|
|
field_mul_log_element(field, poly_log.coeff[i], val_exp[i]));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void polynomial_build_exp_lut(field_t field, field_element_t val, unsigned int order, field_logarithm_t *val_exp) {
|
|
// create the lookup table of successive powers of val used by polynomial_eval_lut
|
|
field_logarithm_t val_exponentiated = field.log[1];
|
|
field_logarithm_t val_log = field.log[val];
|
|
for (unsigned int i = 0; i <= order; i++) {
|
|
if (val == 0) {
|
|
val_exp[i] = 0;
|
|
} else {
|
|
val_exp[i] = val_exponentiated;
|
|
val_exponentiated = field_mul_log(field, val_exponentiated, val_log);
|
|
}
|
|
}
|
|
}
|
|
|
|
polynomial_t polynomial_init_from_roots(field_t field, unsigned int nroots, field_element_t *roots, polynomial_t poly, polynomial_t *scratch) {
|
|
unsigned int order = nroots;
|
|
polynomial_t l;
|
|
field_element_t l_coeff[2];
|
|
l.order = 1;
|
|
l.coeff = l_coeff;
|
|
|
|
// we'll keep two temporary stores of rightside polynomial
|
|
// each time through the loop, we take the previous result and use it as new rightside
|
|
// swap back and forth (prevents the need for a copy)
|
|
polynomial_t r[2];
|
|
r[0] = scratch[0];
|
|
r[1] = scratch[1];
|
|
unsigned int rcoeffres = 0;
|
|
|
|
// initialize the result with x + roots[0]
|
|
r[rcoeffres].coeff[1] = 1;
|
|
r[rcoeffres].coeff[0] = roots[0];
|
|
r[rcoeffres].order = 1;
|
|
|
|
// initialize lcoeff[1] with x
|
|
// we'll fill in the 0th order term in each loop iter
|
|
l.coeff[1] = 1;
|
|
|
|
// loop through, using previous run's result as the new right hand side
|
|
// this allows us to multiply one group at a time
|
|
for (unsigned int i = 1; i < nroots; i++) {
|
|
l.coeff[0] = roots[i];
|
|
unsigned int nextrcoeff = rcoeffres;
|
|
rcoeffres = (rcoeffres + 1) % 2;
|
|
r[rcoeffres].order = i + 1;
|
|
polynomial_mul(field, l, r[nextrcoeff], r[rcoeffres]);
|
|
}
|
|
|
|
memcpy(poly.coeff, r[rcoeffres].coeff, (order + 1) * sizeof(field_element_t));
|
|
poly.order = order;
|
|
|
|
return poly;
|
|
}
|
|
|
|
polynomial_t polynomial_create_from_roots(field_t field, unsigned int nroots, field_element_t *roots) {
|
|
polynomial_t poly = polynomial_create(nroots);
|
|
unsigned int order = nroots;
|
|
polynomial_t l;
|
|
l.order = 1;
|
|
l.coeff = calloc(2, sizeof(field_element_t));
|
|
|
|
polynomial_t r[2];
|
|
// we'll keep two temporary stores of rightside polynomial
|
|
// each time through the loop, we take the previous result and use it as new rightside
|
|
// swap back and forth (prevents the need for a copy)
|
|
r[0].coeff = calloc(order + 1, sizeof(field_element_t));
|
|
r[1].coeff = calloc(order + 1, sizeof(field_element_t));
|
|
unsigned int rcoeffres = 0;
|
|
|
|
// initialize the result with x + roots[0]
|
|
r[rcoeffres].coeff[0] = roots[0];
|
|
r[rcoeffres].coeff[1] = 1;
|
|
r[rcoeffres].order = 1;
|
|
|
|
// initialize lcoeff[1] with x
|
|
// we'll fill in the 0th order term in each loop iter
|
|
l.coeff[1] = 1;
|
|
|
|
// loop through, using previous run's result as the new right hand side
|
|
// this allows us to multiply one group at a time
|
|
for (unsigned int i = 1; i < nroots; i++) {
|
|
l.coeff[0] = roots[i];
|
|
unsigned int nextrcoeff = rcoeffres;
|
|
rcoeffres = (rcoeffres + 1) % 2;
|
|
r[rcoeffres].order = i + 1;
|
|
polynomial_mul(field, l, r[nextrcoeff], r[rcoeffres]);
|
|
}
|
|
|
|
memcpy(poly.coeff, r[rcoeffres].coeff, (order + 1) * sizeof(field_element_t));
|
|
poly.order = order;
|
|
|
|
free(l.coeff);
|
|
free(r[0].coeff);
|
|
free(r[1].coeff);
|
|
|
|
return poly;
|
|
}
|