#include "ReedSolomonDecoder.h"
#include "GenericGF.h"
#include "ZXConfig.h"
#include <algorithm>
#include <stdexcept>
#include <utility>
namespace ZXing {
static bool
RunEuclideanAlgorithm(const GenericGF& field, std::vector<int>&& rCoefs, GenericGFPoly& sigma, GenericGFPoly& omega)
{
int R = Size(rCoefs); GenericGFPoly r(field, std::move(rCoefs));
GenericGFPoly& tLast = omega.setField(field);
GenericGFPoly& t = sigma.setField(field);
ZX_THREAD_LOCAL GenericGFPoly q, rLast;
rLast.setField(field);
q.setField(field);
rLast.setMonomial(1, R);
tLast.setMonomial(0);
t.setMonomial(1);
if (r.degree() >= rLast.degree())
swap(r, rLast);
while (r.degree() >= R / 2) {
swap(tLast, t);
swap(rLast, r);
if (rLast.isZero())
return false;
r.divide(rLast, q);
q.multiply(tLast);
q.addOrSubtract(t);
swap(t, q);
if (r.degree() >= rLast.degree())
throw std::runtime_error("Division algorithm failed to reduce polynomial?");
}
int sigmaTildeAtZero = t.constant();
if (sigmaTildeAtZero == 0)
return false;
int inverse = field.inverse(sigmaTildeAtZero);
t.multiplyByMonomial(inverse);
r.multiplyByMonomial(inverse);
omega = std::move(r);
return true;
}
static std::vector<int>
FindErrorLocations(const GenericGF& field, const GenericGFPoly& errorLocator)
{
int numErrors = errorLocator.degree();
std::vector<int> res;
res.reserve(numErrors);
for (int i = 1; i < field.size() && Size(res) < numErrors; i++)
if (errorLocator.evaluateAt(i) == 0)
res.push_back(field.inverse(i));
return res;
}
static std::vector<int>
FindErrorMagnitudes(const GenericGF& field, const GenericGFPoly& errorEvaluator, const std::vector<int>& errorLocations)
{
int s = Size(errorLocations);
std::vector<int> res(s);
for (int i = 0; i < s; ++i) {
int xiInverse = field.inverse(errorLocations[i]);
int denom = 1;
for (int j = 0; j < s; ++j)
if (i != j)
denom = field.multiply(denom, 1 ^ field.multiply(errorLocations[j], xiInverse));
res[i] = field.multiply(errorEvaluator.evaluateAt(xiInverse), field.inverse(denom));
if (field.generatorBase() != 0)
res[i] = field.multiply(res[i], xiInverse);
}
return res;
}
bool
ReedSolomonDecode(const GenericGF& field, std::vector<int>& message, int numECCodeWords)
{
GenericGFPoly poly(field, message);
std::vector<int> syndromes(numECCodeWords);
for (int i = 0; i < numECCodeWords; i++)
syndromes[numECCodeWords - 1 - i] = poly.evaluateAt(field.exp(i + field.generatorBase()));
if (std::all_of(syndromes.begin(), syndromes.end(), [](int c) { return c == 0; }))
return true;
ZX_THREAD_LOCAL GenericGFPoly sigma, omega;
if (!RunEuclideanAlgorithm(field, std::move(syndromes), sigma, omega))
return false;
auto errorLocations = FindErrorLocations(field, sigma);
if (Size(errorLocations) != sigma.degree())
return false;
auto errorMagnitudes = FindErrorMagnitudes(field, omega, errorLocations);
int msgLen = Size(message);
for (int i = 0; i < Size(errorLocations); ++i) {
int position = msgLen - 1 - field.log(errorLocations[i]);
if (position < 0)
return false;
message[position] ^= errorMagnitudes[i];
}
#if 1
poly = GenericGFPoly(field, message);
for (int i = 0; i < numECCodeWords; i++)
if (poly.evaluateAt(field.exp(i + field.generatorBase())) != 0)
return false;
#endif
return true;
}
}