/*
* *****************************************************************************
* Copyright (C) 2014-2025 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
* ****************************************************************************
*/
package io.github.dsheirer.edac.bch;
import io.github.dsheirer.bits.CorrectedBinaryMessage;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.ArrayUtils;
/**
* Bose-Chaudhuri-Hocquenghem (BCH) decoder. Note: does NOT include the encoder from the original implementation.
*
* Based on the GPL2 Linux BCH decoder implementation here:
* https://github.com/Parrot-Developers/bch/blob/master/include/linux/bch.h
*
* Note: original implementation method and variable names were largely used/retained in porting to Java.
*/
public abstract class BCH
{
public static final int MESSAGE_NOT_CORRECTED = -1;
public static final int PRIMITIVE_POLYNOMIAL_GF_3 = 0x7;
public static final int PRIMITIVE_POLYNOMIAL_GF_7 = 0xB;
public static final int PRIMITIVE_POLYNOMIAL_GF_15 = 0x13;
public static final int PRIMITIVE_POLYNOMIAL_GF_31 = 0x25;
public static final int PRIMITIVE_POLYNOMIAL_GF_63 = 0x43;
public static final int PRIMITIVE_POLYNOMIAL_GF_127 = 0x83;
public static final int PRIMITIVE_POLYNOMIAL_GF_255 = 0x11D;
/**
* Primitive polynomials for GF(2) to GF(8) are defined in this class via the PRIMITIVE_POLYNOMIAL_GF_xx constants.
* This is different from the generator polynomial that is used to create message codewords for the BCH code, where
* the generator polynomial dictates the error correcting capacity value T for the BCH finite set. The base
* primitive polynomial is used to calculate the syndromes for the BCH code regardless of the generator polynomial
* that is used to generate the codewords and the specified value of T dictates how many syndromes are calculated
* and therefore how many bit errors can be detected and corrected.
*/
private int mPrimitivePolynomial;
/**
* Galois Field size: GF(2^m)
*/
private int mM;
/**
* Codeword size where: N = 2 ^ M - 1
*/
private int mN;
/**
* Codeword message content size or number of data bits.
*/
private int mK;
/**
* Error detection and correction capacity of the BCH code. This is the maximum number of bit errors that can
* be detected and corrected.
*/
private int mT;
/**
* Lookup tables. Note: naming convention is from the original C++ implementation.
*/
public int[] a_pow_tab;
public int[] a_log_tab;
private int[] xi_tab;
/**
* Constructs an instance of a BCH decoder with the following design parameters:
*
* @param m Galois Field (2^m) where the BCH code is 2^m-1
* @param k message data bits
* @param t maximum correctable errors. This is a design parameter of the generator polynomial used to form the
* codewords
* @param primitivePolynomial for the GF(2). Note: use one of the PRIMITIVE_POLYNOMIAL_GF_xxx constants defined in
* this class which cover use cases for M: 2-8.
*/
public BCH(int m, int k, int t, int primitivePolynomial)
{
mM = m;
mN = (1 << m) - 1;
mK = k;
mT = t;
mPrimitivePolynomial = primitivePolynomial;
initTables();
}
/**
* Maximum bit error detection and correction capacity (T) of this decoder.
* @return maximum bit error correction.
*/
public int getMaxErrorCorrection()
{
return mT;
}
/*
* Creates lookup table for finding the roots of a degree 2 error locator polynomial.
*/
private void buildDegree2Base()
{
xi_tab = new int[mM];
int i, j, r, sum, x, y, remaining, ak = 0;
int[] xi = new int[mM];
/* find k s.t. Tr(a^k) = 1 and 0 <= k < m */
for(i = 0; i < mM; i++)
{
sum = 0;
for(j = 0; j < mM; j++)
{
sum ^= a_pow(i * (1 << j));
}
if(sum != 0)
{
ak = a_pow_tab[i];
break;
}
}
/* find xi, i=0..m-1 such that xi^2+xi = a^i+Tr(a^i).a^k */
remaining = mM;
for(x = 0; (x <= mN) && remaining != 0; x++)
{
y = gf_sqr(x) ^ x;
for(i = 0; i < 2; i++)
{
r = a_log(y);
if(y != 0 && (r < mM) && xi[r] == 0)
{
xi_tab[r] = x;
xi[r] = 1;
remaining--;
break;
}
y ^= ak;
}
}
if(remaining != 0)
{
throw new IllegalStateException("Unexpected remaining value: " + remaining);
}
}
/*
* Finds the error roots of the error locator polynomial, using BTZ algorithm
*/
public int[] find_poly_roots(GFPoly poly, int k)
{
int[] roots = new int[0];
switch(poly.mDegree)
{
/* handle low degree polynomials with ad hoc techniques */
case 1:
roots = find_poly_deg1_roots(poly);
break;
case 2:
roots = find_poly_deg2_roots(poly);
break;
case 3:
roots = find_poly_deg3_roots(poly);
break;
case 4:
roots = find_poly_deg4_roots(poly);
break;
default:
/* factor polynomial using Berlekamp Trace Algorithm (BTA) */
if (poly.mDegree != 0 && (k <= mM))
{
GFPoly[] factors = factor_polynomial(k, poly);
if (factors != null && factors.length == 2)
{
int[] aRoots = find_poly_roots(factors[0], k + 1);
int[] bRoots = find_poly_roots(factors[1], k + 1);
roots = new int[aRoots.length + bRoots.length];
System.arraycopy(aRoots, 0, roots, 0, aRoots.length);
System.arraycopy(bRoots, 0, roots, aRoots.length, bRoots.length);
}
else if (factors != null && factors.length > 0)
{
roots = find_poly_roots(factors[0], k + 1);
}
}
break;
}
return roots;
}
/*
* Factors a polynomial using Berlekamp Trace algorithm (BTA)
*/
public GFPoly[] factor_polynomial(int k, GFPoly f)
{
GFPoly f2 = new GFPoly(2 * mT);
GFPoly q = new GFPoly(2 * mT);
GFPoly z = new GFPoly(2 * mT);
GFPoly tk;
GFPoly gcd;
GFPoly g = new GFPoly(1);
f.copyTo(g);
/* tk = Tr(a^k.X) mod f */
tk = compute_trace_bk_mod(k, f, z);
if (tk.mDegree > 0)
{
f.copyTo(f2);
/* compute g = gcd(f, tk) (destructive operation) */
gcd = gf_poly_gcd(f2, tk);
if (gcd.mDegree < f.mDegree)
{
/* compute h=f/gcd(f,tk); this will modify f and q */
gf_poly_div(f, gcd, q);
GFPoly[] results = new GFPoly[2];
results[0] = gcd;
results[1] = q;
return results;
}
}
GFPoly[] results = new GFPoly[1];
results[0] = g;
return results;
}
/*
* Compute polynomial Euclidean division quotient in GF(2^m)[X]
*/
public void gf_poly_div(GFPoly a, GFPoly b, GFPoly q)
{
if (a.mDegree >= b.mDegree)
{
q.mDegree = a.mDegree - b.mDegree;
/* compute a mod b (modifies a) */
gf_poly_mod(a, b);
/* quotient is stored in upper part of polynomial a */
System.arraycopy(a.mC, b.mDegree, q.mC, 0, 1 + q.mDegree);
}
else
{
q.mDegree = 0;
q.mC[0] = 0;
}
}
/*
* Compute polynomial GCD (Greatest Common Divisor) in GF(2^m)[X]
*/
public GFPoly gf_poly_gcd(GFPoly a, GFPoly b)
{
GFPoly tmp;
if (a.mDegree < b.mDegree)
{
tmp = b;
b = a;
a = tmp;
}
while (b.mDegree > 0)
{
gf_poly_mod(a, b);
tmp = b;
b = a;
a = tmp;
}
return a;
}
/*
* Given a polynomial f and an integer k, compute Tr(a^kX) mod f
* This is used in Berlekamp Trace algorithm for splitting polynomials
*/
public GFPoly compute_trace_bk_mod(int k, GFPoly f, GFPoly z)
{
int m = mM;
int i, j;
/* z contains z^2j mod f */
z.mDegree = 1;
z.mC[0] = 0;
z.mC[1] = a_pow_tab[k];
GFPoly outTK = new GFPoly(f.mC.length);
outTK.mDegree = 0;
for (i = 0; i < m; i++)
{
/* add a^(k*2^i)(z^(2^i) mod f) and compute (z^(2^i) mod f)^2 */
for (j = z.mDegree; j >= 0; j--)
{
if(2 * j >= z.mC.length)
{
//Fail ... set degree to 0 to signal fail
outTK.mDegree = 0;
return outTK;
}
outTK.mC[j] ^= z.mC[j];
z.mC[2 * j] = gf_sqr(z.mC[j]);
z.mC[2 * j + 1] = 0;
}
if (z.mDegree > outTK.mDegree)
{
outTK.mDegree = z.mDegree;
}
if (i < m-1)
{
z.mDegree *= 2;
/* z^(2(i+1)) mod f = (z^(2^i) mod f)^2 mod f */
gf_poly_mod(z, f);
}
}
while (outTK.mC[outTK.mDegree] == 0 && outTK.mDegree != 0)
{
outTK.mDegree--;
}
return outTK;
}
/*
* compute polynomial Euclidean division remainder in GF(2^m)[X]
*/
public void gf_poly_mod(GFPoly a, GFPoly b)
{
if (a.mDegree < b.mDegree)
{
return;
}
int la, p, m;
int i, j;
int[] c = Arrays.copyOf(a.mC, a.mC.length);
int d = b.mDegree;
int[] rep = gf_poly_logrep(b);
for (j = a.mDegree; j >= d; j--)
{
if (c[j] != 0)
{
la = a_log(c[j]);
p = j-d;
for (i = 0; i < d; i++, p++)
{
m = rep[i];
if (m >= 0)
{
c[p] ^= a_pow_tab[mod_s(m + la)];
}
}
}
}
a.mDegree = d - 1;
while (c[a.mDegree] == 0 && a.mDegree != 0)
{
a.mDegree--;
}
//Reassign c back to a's polynomial
a.mC = c;
}
/*
* Build monic, log-based representation of a polynomial
*/
public int[] gf_poly_logrep(GFPoly a)
{
int i;
int d = a.mDegree;
int l = mN - a_log(a.mC[a.mDegree]);
int[] rep = new int[d];
/* represent 0 values with -1; warning, rep[d] is not set to 1 */
for (i = 0; i < d; i++)
{
rep[i] = a.mC[i] != 0 ? mod_s(a_log(a.mC[i]) + l) : -1;
}
return rep;
}
/*
* Compute root r of a degree 1 polynomial over GF(2^m) (returned as log(1/r))
*/
public int[] find_poly_deg1_roots(GFPoly poly)
{
int[] roots = new int[1];
if(poly.mC[0] != 0)
{
/* poly[X] = bX+c with c!=0, root=c/b */
roots[0] = mod_s(mN - a_log_tab[poly.mC[0]] + a_log_tab[poly.mC[1]]);
}
return roots;
}
/*
* Compute roots of a degree 2 polynomial over GF(2^m)
*/
public int[] find_poly_deg2_roots(GFPoly poly)
{
int[] roots = new int[poly.mDegree];
int n = 0, i, l0, l1, l2;
int u, v, r;
if(poly.mC[0] > 0 && poly.mC[1] > 0)
{
l0 = a_log_tab[poly.mC[0]];
l1 = a_log_tab[poly.mC[1]];
l2 = a_log_tab[poly.mC[2]];
/* using z=a/bX, transform aX^2+bX+c into z^2+z+u (u=ac/b^2) */
u = a_pow(l0 + l2 + 2 * (mN - l1));
/*
* let u = sum(li.a^i) i=0..m-1; then compute r = sum(li.xi):
* r^2+r = sum(li.(xi^2+xi)) = sum(li.(a^i+Tr(a^i).a^k)) =
* u + sum(li.Tr(a^i).a^k) = u+a^k.Tr(sum(li.a^i)) = u+a^k.Tr(u)
* i.e. r and r+1 are roots iff Tr(u)=0
*/
r = 0;
v = u;
while(v != 0)
{
i = deg(v);
r ^= xi_tab[i];
v ^= (1 << i);
}
/* verify root */
if((gf_sqr(r) ^ r) == u)
{
/* reverse z=a/bX transformation and compute log(1/r) */
roots[n++] = modulo(2 * mN - l1 - a_log_tab[r] + l2);
roots[n++] = modulo(2 * mN - l1 - a_log_tab[r ^ 1] + l2);
}
}
return roots;
}
/*
* Compute roots of a degree 3 polynomial over GF(2^m)
*/
public int[] find_poly_deg3_roots(GFPoly poly)
{
int[] roots = new int[poly.mDegree];
int i, n = 0;
int a, b, c, a2, b2, c2, e3;
int[] tmp = new int[4];
if(poly.mC[0] != 0)
{
/* transform polynomial into monic X^3 + a2X^2 + b2X + c2 */
e3 = poly.mC[3];
c2 = gf_div(poly.mC[0], e3);
b2 = gf_div(poly.mC[1], e3);
a2 = gf_div(poly.mC[2], e3);
/* (X+a2)(X^3+a2X^2+b2X+c2) = X^4+aX^2+bX+c (affine) */
c = gf_mul(a2, c2); /* c = a2c2 */
b = gf_mul(a2, b2) ^ c2; /* b = a2b2 + c2 */
a = gf_sqr(a2) ^ b2; /* a = a2^2 + b2 */
/* find the 4 roots of this affine polynomial */
if(find_affine4_roots(a, b, c, tmp) == 4)
{
/* remove a2 from final list of roots */
for(i = 0; i < 4; i++)
{
if(tmp[i] != a2)
{
roots[n++] = a_ilog(tmp[i]);
}
}
}
}
return roots;
}
/*
* Compute roots of a degree 4 polynomial over GF(2^m)
*/
public int[] find_poly_deg4_roots(GFPoly poly)
{
int[] roots = new int[poly.mDegree];
int i, l, n = 0;
int a, b, c, d, e = 0, f, a2, b2, c2, e4;
if (poly.mC[0] == 0)
{
return new int[0];
}
/* transform polynomial into monic X^4 + aX^3 + bX^2 + cX + d */
e4 = poly.mC[4];
d = gf_div(poly.mC[0], e4);
c = gf_div(poly.mC[1], e4);
b = gf_div(poly.mC[2], e4);
a = gf_div(poly.mC[3], e4);
/* use Y=1/X transformation to get an affine polynomial */
if (a != 0)
{
/* first, eliminate cX by using z=X+e with ae^2+c=0 */
if (c != 0)
{
/* compute e such that e^2 = c/a */
f = gf_div(c, a);
l = a_log(f);
l += ((l & 1) != 0) ? mN : 0;
e = a_pow(l / 2);
/*
* use transformation z=X+e:
* z^4+e^4 + a(z^3+ez^2+e^2z+e^3) + b(z^2+e^2) +cz+ce+d
* z^4 + az^3 + (ae+b)z^2 + (ae^2+c)z+e^4+be^2+ae^3+ce+d
* z^4 + az^3 + (ae+b)z^2 + e^4+be^2+d
* z^4 + az^3 + b'z^2 + d'
*/
d = a_pow(2 * l) ^ gf_mul(b, f) ^ d;
b = gf_mul(a, e) ^ b;
}
/* now, use Y=1/X to get Y^4 + b/dY^2 + a/dY + 1/d */
if (d == 0)
{
/* assume all roots have multiplicity 1 */
return new int[0];
}
c2 = gf_inv(d);
b2 = gf_div(a, d);
a2 = gf_div(b, d);
} else {
/* polynomial is already affine */
c2 = d;
b2 = c;
a2 = b;
}
/* find the 4 roots of this affine polynomial */
if (find_affine4_roots(a2, b2, c2, roots) == 4)
{
for (i = 0; i < 4; i++)
{
/* post-process roots (reverse transformations) */
f = a != 0 ? gf_inv(roots[i]) : roots[i];
roots[i] = a_ilog(f ^ e);
}
}
return roots;
}
/*
* This function builds and solves a linear system for finding roots of a degree
* 4 affine monic polynomial X^4+aX^2+bX+c over GF(2^m).
*/
public int find_affine4_roots(int a, int b, int c, int[] roots)
{
int i, j, k;
int m = mM;
int mask = 0xff, t;
int[] rows = new int[16];
j = a_log(b);
k = a_log(a);
rows[0] = c;
/* buid linear system to solve X^4+aX^2+bX+c = 0 */
for(i = 0; i < m; i++)
{
rows[i + 1] = a_pow_tab[4 * i] ^ (a != 0 ? a_pow_tab[mod_s(k)] : 0) ^ (b != 0 ? a_pow_tab[mod_s(j)] : 0);
j++;
k += 2;
}
/*
* transpose 16x16 matrix before passing it to linear solver
* warning: this code assumes m < 16
*/
for(j = 8; j != 0; j >>= 1, mask ^= (mask << j))
{
for(k = 0; k < 16; k = (k + j + 1) & ~j)
{
t = ((rows[k] >> j) ^ rows[k + j]) & mask;
rows[k] ^= (t << j);
rows[k + j] ^= t;
}
}
return solve_linear_system(rows, roots, 4);
}
/*
* Solve an m x m linear system in GF(2) with an expected number of solutions,
* and return the number of found solutions
*/
public int solve_linear_system(int[] rows, int[] sol, int nsol)
{
int m = mM;
int tmp, mask;
int rem, c, r, p, k;
int[] param = new int[m];
k = 0;
mask = 1 << m;
/* Gaussian elimination */
for(c = 0; c < m; c++)
{
rem = 0;
p = c - k;
/* find suitable row for elimination */
for(r = p; r < m; r++)
{
if((rows[r] & mask) != 0)
{
if(r != p)
{
tmp = rows[r];
rows[r] = rows[p];
rows[p] = tmp;
}
rem = r + 1;
break;
}
}
if(rem != 0)
{
/* perform elimination on remaining rows */
tmp = rows[p];
for(r = rem; r < m; r++)
{
if((rows[r] & mask) != 0)
{
rows[r] ^= tmp;
}
}
}
else
{
/* elimination not needed, store defective row index */
param[k++] = c;
}
mask >>= 1;
}
/* rewrite system, inserting fake parameter rows */
if(k > 0)
{
p = k;
for(r = m - 1; r >= 0; r--)
{
if((r > m - 1 - k) && rows[r] != 0)
{
/* system has no solution */
return 0;
}
if(p != 0 && (r == param[p - 1]))
{
p--;
rows[r] = 1 << (m - r);
}
else
{
rows[r] = rows[r - p];
}
}
}
if(nsol != (1 << k))
{
/* unexpected number of solutions */
return 0;
}
for(p = 0; p < nsol; p++)
{
/* set parameters for p-th solution */
for(c = 0; c < k; c++)
{
rows[param[c]] = (rows[param[c]] & ~1) | ((p >> c) & 1);
}
/* compute unique solution */
tmp = 0;
for(r = m - 1; r >= 0; r--)
{
mask = rows[r] & (tmp | 1);
tmp |= parity(mask) << (m - r);
}
sol[p] = tmp >> 1;
}
return nsol;
}
/**
* Calculates the parity of an integer as the number of set bits mod 2.
* @param x to calculate
* @return parity of x
*/
public static int parity(int x)
{
return Integer.bitCount(x) % 2;
}
/**
* Calculate the error locator polynomial from the syndromes.
*
* @param syn syndromes
* @return error locator polynomial (elp)
*/
public GFPoly compute_error_locator_polynomial(int[] syn)
{
int i, j, tmp, l, pd = 1, d = syn[0];
int k, pp = -1;
GFPoly elp = new GFPoly(2 * mT + 1);
GFPoly pelp = new GFPoly(2 * mT + 1);
GFPoly elp_copy = new GFPoly(2 * mT + 1);
pelp.mDegree = 0;
pelp.mC[0] = 1;
elp.mDegree = 0;
elp.mC[0] = 1;
/* use simplified binary Berlekamp-Massey algorithm */
for(i = 0; (i < mT) && (elp.mDegree <= mT); i++)
{
if(d != 0)
{
k = 2 * i - pp;
elp.copyTo(elp_copy); // reimplemented
/* e[i+1](X) = e[i](X)+di*dp^-1*X^2(i-p)*e[p](X) */
tmp = a_log(d) + mN - a_log(pd);
for(j = 0; j <= pelp.mDegree; j++)
{
if(pelp.mC[j] > 0)
{
l = a_log(pelp.mC[j]);
elp.mC[j + k] ^= a_pow(tmp + l);
}
}
/* compute l[i+1] = max(l[i]->c[l[p]+2*(i-p]) */
tmp = pelp.mDegree + k;
if(tmp > elp.mDegree)
{
elp.mDegree = tmp;
elp_copy.copyTo(pelp); //reimplemented
// gf_poly_copy(pelp, elp_copy);
pd = d;
pp = 2 * i;
}
}
/* di+1 = S(2i+3)+elp[i+1].1*S(2i+2)+...+elp[i+1].lS(2i+3-l) */
if(i < mT - 1)
{
d = syn[2 * i + 2];
for(j = 1; j <= elp.mDegree; j++)
{
d ^= mul(elp.mC[j], syn[2 * i + 2 - j]);
}
}
}
return elp;
}
/**
* Multiplies two integer values using mod N polynomial math.
*
* @param a first value
* @param b second value
* @return product of (a * b) % N
*/
private int mul(int a, int b)
{
if(a == 0 || b == 0)
{
return 0;
}
return a_pow_tab[(a_log_tab[a] + a_log_tab[b]) % mN];
}
private int gf_sqr(int a)
{
return a > 0 ? a_pow_tab[mod_s(2 * a_log_tab[a])] : 0;
}
private int gf_mul(int a, int b)
{
return (a != 0 && b != 0) ? a_pow_tab[mod_s(a_log_tab[a] + a_log_tab[b])] : 0;
}
private int gf_div(int a, int b)
{
return a != 0 ? a_pow_tab[mod_s(a_log_tab[a] + mN - a_log_tab[b])] : 0;
}
private int gf_inv(int a)
{
return a_pow_tab[mN - a_log_tab[a]];
}
/**
* Polynomial representation.
*/
public static class GFPoly
{
public int mDegree = 0;
public int[] mC; //Coefficients
/**
* Constructs an instance with the specified size, or number of coefficients.
* @param size of the coefficients array
*/
public GFPoly(int size)
{
mC = new int[size];
}
/**
* Copies the degree and coefficients of this polynomial onto the argument polynomial.
* @param copyTo target of the copy operation.
*/
public void copyTo(GFPoly copyTo)
{
copyTo.mDegree = mDegree;
copyTo.mC = Arrays.copyOf(mC, mC.length);
}
/**
* Pretty print of the contents of this polynomial.
*/
@Override
public String toString()
{
return "Polynomial Degree: " + mDegree + " Coefficients: " + Arrays.toString(mC);
}
}
/**
* Computes the syndromes for the message.
* @param message to check
* @return array of syndromes of size (2 * T)
*/
public int[] computeSyndromes(CorrectedBinaryMessage message)
{
int j;
int twoT = 2 * mT;
int nMinus1 = mN - 1;
int[] syndromes = new int[twoT];
//Use the binary message feature to iterate across the set bit positions to calculate the syndromes for each
//set bit. We iterate the bit positions in order from MSB to LSB, however we calculate the syndrome as if each
//set bit is accessed in reverse order by subtracting the iterated bit position from (N-1).
for(int i = message.nextSetBit(0); i >= 0 && i < mN; i = message.nextSetBit(i + 1))
{
for(j = 0; j < twoT; j += 2)
{
syndromes[j] ^= a_pow((j + 1) * (nMinus1 - i));
}
}
//Calculate the even syndromes as squaring of the odd syndromes: v(a^(2j)) = v(a^j)^2
for(j = 0; j < mT; j++)
{
syndromes[2 * j + 1] = gf_sqr(syndromes[j]);
}
return syndromes;
}
/*
* compute 2t syndromes of ecc polynomial, i.e. ecc(a^j) for j=1..2t ... this only works for 32-bit codeword
*
* Note: this is ported from the original implementation that used an array of one or more 32-bit integers to
* represent the codeword and was used during development and testing of the ported code. This method only works
* with a single 32-bit codeword formed for a BCH(31) finite set and does not work for larger BCH codes, so don't
* use this method.
*/
private int[] compute_syndromes(int codeword)
{
int i, j;
int poly;
int[] syn = new int[2 * mT];
int t = mT;
int s = mN - mK; //Should be 31 - 21 = 10, which is also <=m*t or 5 * 2;
/* compute v(a^j) for j=1 .. 2t-1 */
do
{
poly = codeword;
s -= 32;
s = 0;
while(poly != 0)
{
i = deg(poly);
for(j = 0; j < 2 * t; j += 2)
{
syn[j] ^= a_pow((j + 1) * (i + s));
}
poly ^= (1 << i);
}
}
while(s > 0);
// System.out.println("Syndromes Before Squaring: " + Arrays.toString(syn));
/* v(a^(2j)) = v(a^j)^2 */
for(j = 0; j < t; j++)
{
syn[2 * j + 1] = gf_sqr(syn[j]);
}
return syn;
}
/**
* Calculates the degree of the polynomial, represented as the most significant bit index
*
* @param poly to inspect
* @return degree
*/
public static int deg(int poly)
{
/* polynomial degree is the most-significant bit index */
int highestSetBit = Integer.highestOneBit(poly);
return Integer.numberOfTrailingZeros(highestSetBit);
//return fls(poly)-1; << why minus 1?
}
/**
* Initializes the Galois field lookup tables.
*
* Modeled on bch.1 build_gf_tables() method.
*/
public void initTables()
{
int i, x = 1;
int k = 1 << mM;
a_pow_tab = new int[k];
a_log_tab = new int[k];
for(i = 0; i < k - 1; i++)
{
a_pow_tab[i] = x;
a_log_tab[x] = i;
x <<= 1;
if((x & k) != 0)
{
x ^= mPrimitivePolynomial;
}
}
a_pow_tab[k - 1] = 1;
a_log_tab[0] = 0;
// System.out.println("a_pow_tab = " + Arrays.toString(a_pow_tab));
// System.out.println("a_log_tab = " + Arrays.toString(a_log_tab));
buildDegree2Base();
}
public int a_log(int value)
{
return a_log_tab[value];
}
public int a_ilog(int x)
{
return mod_s(mN - a_log_tab[x]);
}
public int a_pow(int value)
{
return a_pow_tab[modulo(value)];
}
public int modulo(int v)
{
while(v >= mN)
{
v -= mN;
v = (v & mN) + (v >> mM);
}
return v;
}
public int mod_s(int v)
{
return (v < mN ? v : v - mN);
}
/**
* Decodes the BCH protected message.
* @param message where the BCH protected codeword is located at bit indices 0 to (N-1)
*/
public void decode(CorrectedBinaryMessage message)
{
int[] syndromes = computeSyndromes(message);
GFPoly elp = compute_error_locator_polynomial(syndromes);
int elpDegree = elp.mDegree;
// System.out.println(elp);
int k = 1; //Recursive call argument
int[] roots = find_poly_roots(elp, k);
// System.out.println(elp);
if(roots.length != elpDegree)
{
// System.out.println("Error roots " + Arrays.toString(roots) + " do not match ELP degree [" + elpDegree + "]");
message.setCorrectedBitCount(MESSAGE_NOT_CORRECTED);
return;
}
//Create a set from the roots to eliminate and then detect if there are duplicates
Set rootSet = new HashSet<>(Arrays.asList(ArrayUtils.toObject(roots)));
if(rootSet.size() != roots.length)
{
// System.out.println("Error roots were not distinct: " + Arrays.toString(roots));
message.setCorrectedBitCount(MESSAGE_NOT_CORRECTED);
return;
}
//Invert the error roots because we process the message backwards/inverted, so the calculated roots then also
//need to be un-inverted to reference the correct message indices.
for(int x = 0; x < roots.length; x++)
{
roots[x] = mN - 1 - roots[x];
}
// System.out.println("Error Roots:" + Arrays.toString(roots));
//Correct the errors in the original message.
for(int error: roots)
{
message.flip(error);
}
//Set the number of errors corrected on the original message.
message.setCorrectedBitCount(roots.length);
}
}