#include "normEncoderRS8.h"
#include "protoDebug.h"
#ifdef SIMULATE
#include "normMessage.h"
#endif
#define GF_BITS 8
#if (GF_BITS < 2) || (GF_BITS > 16)
#error "GF_BITS must be 2 .. 16"
#endif
#if (GF_BITS <= 8)
typedef UINT8 gf;
#else
typedef UINT16 gf;
#endif
#define GF_SIZE ((1 << GF_BITS) - 1)
static const char *allPp[] =
{ NULL, NULL, "111", "1101", "11001", "101001", "1100001", "10010001", "101110001", "1000100001", "10010000001", "101000000001", "1100101000001", "11011000000001", "110000100010001", "1100000000000001", "11010000000010001" };
static gf gf_exp[2*GF_SIZE]; static int gf_log[GF_SIZE + 1]; static gf inverse[GF_SIZE+1];
static inline gf modnn(int x)
{
while (x >= GF_SIZE)
{
x -= GF_SIZE;
x = (x >> GF_BITS) + (x & GF_SIZE);
}
return x;
}
#define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
#if (GF_BITS <= 8)
static gf gf_mul_table[GF_SIZE + 1][GF_SIZE + 1];
#define gf_mul(x,y) gf_mul_table[x][y]
#define USE_GF_MULC gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = gf_mul_table[c]
#define GF_ADDMULC(dst, x) dst ^= __gf_mulc_[x]
static void init_mul_table()
{
for (int i = 0; i <= GF_SIZE; i++)
{
for (int j = 0; j <= GF_SIZE; j++)
gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j]) ] ;
}
for (int j = 0; j <= GF_SIZE; j++)
gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
}
#else
inline gf gf_mul(int x, int y)
{
if ((0 == x) || (0 == y)) return 0;
return gf_exp[gf_log[x] + gf_log[y] ] ;
}
#define init_mul_table()
#define USE_GF_MULC register gf * __gf_mulc_
#define GF_MULC0(c) __gf_mulc_ = &gf_exp[ gf_log[c] ]
#define GF_ADDMULC(dst, x) { if (x) dst ^= __gf_mulc_[ gf_log[x] ] ; }
#endif
#define NEW_GF_MATRIX(rows, cols) (new gf[rows*cols])
static void generate_gf()
{
const char *Pp = allPp[GF_BITS] ;
gf mask = 1;
gf_exp[GF_BITS] = 0;
for (int i = 0; i < GF_BITS; i++, mask <<= 1 )
{
gf_exp[i] = mask;
gf_log[gf_exp[i]] = i;
if ( Pp[i] == '1' )
gf_exp[GF_BITS] ^= mask;
}
gf_log[gf_exp[GF_BITS]] = GF_BITS;
mask = 1 << (GF_BITS - 1 ) ;
for (int i = GF_BITS + 1; i < GF_SIZE; i++)
{
if (gf_exp[i - 1] >= mask)
gf_exp[i] = gf_exp[GF_BITS] ^ ((gf_exp[i - 1] ^ mask) << 1);
else
gf_exp[i] = gf_exp[i - 1] << 1;
gf_log[gf_exp[i]] = i;
}
gf_log[0] = GF_SIZE ;
for (int i = 0 ; i < GF_SIZE ; i++)
gf_exp[i + GF_SIZE] = gf_exp[i] ;
inverse[0] = 0 ;
inverse[1] = 1;
for (int i = 2; i <= GF_SIZE; i++)
inverse[i] = gf_exp[GF_SIZE-gf_log[i]];
}
#define addmul(dst, src, c, sz) \
if (c != 0) addmul1(dst, src, c, sz)
#define UNROLL 16
static void addmul1(gf* dst1, gf* src1, gf c, int sz)
{
USE_GF_MULC ;
gf* dst = dst1;
gf* src = src1 ;
gf* lim = &dst[sz - UNROLL + 1] ;
GF_MULC0(c) ;
#if (UNROLL > 1)
for (; dst < lim ; dst += UNROLL, src += UNROLL )
{
GF_ADDMULC( dst[0] , src[0] );
GF_ADDMULC( dst[1] , src[1] );
GF_ADDMULC( dst[2] , src[2] );
GF_ADDMULC( dst[3] , src[3] );
#if (UNROLL > 4)
GF_ADDMULC( dst[4] , src[4] );
GF_ADDMULC( dst[5] , src[5] );
GF_ADDMULC( dst[6] , src[6] );
GF_ADDMULC( dst[7] , src[7] );
#endif
#if (UNROLL > 8)
GF_ADDMULC( dst[8] , src[8] );
GF_ADDMULC( dst[9] , src[9] );
GF_ADDMULC( dst[10] , src[10] );
GF_ADDMULC( dst[11] , src[11] );
GF_ADDMULC( dst[12] , src[12] );
GF_ADDMULC( dst[13] , src[13] );
GF_ADDMULC( dst[14] , src[14] );
GF_ADDMULC( dst[15] , src[15] );
#endif
}
#endif
lim += UNROLL - 1 ;
for (; dst < lim; dst++, src++ )
GF_ADDMULC( *dst , *src );
}
static void matmul(gf* a, gf* b, gf* c, int n, int k, int m)
{
int row, col, i ;
for (row = 0; row < n ; row++)
{
for (col = 0; col < m ; col++)
{
gf* pa = &a[ row * k ];
gf* pb = &b[ col ];
gf acc = 0 ;
for (i = 0; i < k ; i++, pa++, pb += m)
acc ^= gf_mul( *pa, *pb ) ;
c[row * m + col] = acc ;
}
}
}
static int invert_vdm(gf* src, int k)
{
gf t, xx;
if (k == 1)return 0;
gf* c = NEW_GF_MATRIX(1, k);
gf* b = NEW_GF_MATRIX(1, k);
gf* p = NEW_GF_MATRIX(1, k);
int i, j;
for (j = 1, i = 0 ; i < k ; i++, j+=k )
{
c[i] = 0 ;
p[i] = src[j] ;
}
c[k-1] = p[0] ;
for (i = 1 ; i < k ; i++)
{
gf p_i = p[i] ;
for (j = k - 1 - ( i - 1 ) ; j < k-1 ; j++ )
c[j] ^= gf_mul( p_i, c[j+1] ) ;
c[k-1] ^= p_i ;
}
for (int row = 0 ; row < k ; row++)
{
xx = p[row] ;
t = 1 ;
b[k-1] = 1 ;
for (i = k - 2 ; i >= 0 ; i-- )
{
b[i] = c[i+1] ^ gf_mul(xx, b[i+1]) ;
t = gf_mul(xx, t) ^ b[i] ;
}
for (int col = 0 ; col < k ; col++ )
src[col*k + row] = gf_mul(inverse[t], b[col] );
}
delete[] c;
delete[] b;
delete[] p;
return 0 ;
}
static bool fec_initialized = false;
static void init_fec()
{
if (!fec_initialized)
{
generate_gf();
init_mul_table();
fec_initialized = true;
}
}
NormEncoderRS8::NormEncoderRS8()
: enc_matrix(NULL)
{
}
NormEncoderRS8::~NormEncoderRS8()
{
Destroy();
}
bool NormEncoderRS8::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
{
#ifdef SIMULATE
vecSizeMax = MIN(SIM_PAYLOAD_MAX, vecSizeMax);
#endif if ((numData + numParity) > GF_SIZE)
{
PLOG(PL_FATAL, "NormEncoderRS8::Init() error: numData/numParity exceeds code limits\n");
return false;
}
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
init_fec();
int n = numData + numParity;
int k = numData;
enc_matrix = (UINT8*)NEW_GF_MATRIX(n, k);
if (NULL != enc_matrix)
{
gf* tmpMatrix = NEW_GF_MATRIX(n, k);
if (NULL == tmpMatrix)
{
PLOG(PL_FATAL, "NormEncoderRS8::Init() error: new tmpMatrix error: %s\n", GetErrorString());
delete[] enc_matrix;
enc_matrix = NULL;
return false;
}
tmpMatrix[0] = 1 ;
for (int col = 1; col < k ; col++)
tmpMatrix[col] = 0 ;
for (gf* p = tmpMatrix + k, row = 0; row < n-1 ; row++, p += k)
{
for (int col = 0 ; col < k ; col ++ )
p[col] = gf_exp[modnn(row*col)];
}
invert_vdm(tmpMatrix, k);
matmul(tmpMatrix + k*k, tmpMatrix, ((gf*)enc_matrix) + k*k, n - k, k, k);
memset(enc_matrix, 0, k*k*sizeof(gf));
for (gf* p = (gf*)enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
*p = 1 ;
delete[] tmpMatrix;
ndata = numData;
npar = numParity;
vector_size = vecSizeMax;
return true;
}
else
{
PLOG(PL_FATAL, "NormEncoderRS8::Init() error: new enc_matrix error: %s\n", GetErrorString());
return false;
}
}
void NormEncoderRS8::Destroy()
{
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
}
void NormEncoderRS8::Encode(unsigned int segmentId, const char* dataVector, char** parityVectorList)
{
for (unsigned int i = 0; i < npar; i++)
{
gf* fec = (gf*)parityVectorList[i];
gf* p = ((gf*)enc_matrix) + ((i+ndata)*ndata);
unsigned int nelements = (GF_BITS > 8) ? vector_size / 2 : vector_size;
addmul(fec, (gf*)dataVector, p[segmentId], nelements);
}
}
NormDecoderRS8::NormDecoderRS8()
: enc_matrix(NULL), dec_matrix(NULL),
parity_loc(NULL), inv_ndxc(NULL), inv_ndxr(NULL),
inv_pivt(NULL), inv_id_row(NULL), inv_temp_row(NULL)
{
}
NormDecoderRS8::~NormDecoderRS8()
{
Destroy();
}
void NormDecoderRS8::Destroy()
{
if (NULL != enc_matrix)
{
delete[] enc_matrix;
enc_matrix = NULL;
}
if (NULL != dec_matrix)
{
delete[] dec_matrix;
dec_matrix = NULL;
}
if (NULL != parity_loc)
{
delete[] parity_loc;
parity_loc = NULL;
}
if (NULL != inv_ndxc)
{
delete[] inv_ndxc;
inv_ndxc = NULL;
}
if (NULL != inv_ndxr)
{
delete[] inv_ndxr;
inv_ndxr = NULL;
}
if (NULL != inv_pivt)
{
delete[] inv_pivt;
inv_pivt = NULL;
}
if (NULL != inv_id_row)
{
delete[] inv_id_row;
inv_id_row = NULL;
}
if (NULL != inv_temp_row)
{
delete[] inv_temp_row;
inv_temp_row = NULL;
}
}
bool NormDecoderRS8::Init(unsigned int numData, unsigned int numParity, UINT16 vecSizeMax)
{
#ifdef SIMULATE
vecSizeMax = MIN(SIM_PAYLOAD_MAX, vecSizeMax);
#endif if ((numData + numParity) > GF_SIZE)
{
PLOG(PL_FATAL, "NormEncoderRS8::Init() error: numData/numParity exceeds code limits\n");
return false;
}
init_fec();
Destroy();
int n = numData + numParity;
int k = numData;
if (NULL == (inv_ndxc = new unsigned int[k]))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() new inv_indxc error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_ndxr = new unsigned int[k]))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() new inv_inv_ndxr error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_pivt = new unsigned int[k]))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() new inv_pivt error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_id_row = (UINT8*)NEW_GF_MATRIX(1, k)))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() new inv_id_row error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (inv_temp_row = (UINT8*)NEW_GF_MATRIX(1, k)))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() new inv_temp_row error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (parity_loc = new unsigned int[numParity]))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() error: new parity_loc error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (dec_matrix = (UINT8*)NEW_GF_MATRIX(k, k)))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() error: new dec_matrix error: %s\n", GetErrorString());
Destroy();
return false;
}
if (NULL == (enc_matrix = (UINT8*)NEW_GF_MATRIX(n, k)))
{
PLOG(PL_FATAL, "NormDecoderRS8::Init() error: new enc_matrix error: %s\n", GetErrorString());
Destroy();
return false;
}
gf* tmpMatrix = NEW_GF_MATRIX(n, k);
if (NULL == tmpMatrix)
{
PLOG(PL_FATAL, "NormEncoderRS8::Init() error: new tmpMatrix error: %s\n", GetErrorString());
delete[] enc_matrix;
enc_matrix = NULL;
return false;
}
tmpMatrix[0] = 1 ;
for (int col = 1; col < k ; col++)
tmpMatrix[col] = 0 ;
for (gf* p = tmpMatrix + k, row = 0; row < n-1 ; row++, p += k)
{
for (int col = 0 ; col < k ; col ++ )
p[col] = gf_exp[modnn(row*col)];
}
invert_vdm(tmpMatrix, k);
matmul(tmpMatrix + k*k, tmpMatrix, ((gf*)enc_matrix) + k*k, n - k, k, k);
memset(enc_matrix, 0, k*k*sizeof(gf));
for (gf* p = (gf*)enc_matrix, col = 0 ; col < k ; col++, p += k+1 )
*p = 1 ;
delete[] tmpMatrix;
ndata = numData;
npar = numParity;
vector_size = vecSizeMax;
return true;
}
int NormDecoderRS8::Decode(char** vectorList, unsigned int numData, unsigned int erasureCount, unsigned int* erasureLocs)
{
unsigned int bsz = ndata + npar;
unsigned int nextErasure = 0;
unsigned int ne = 0;
unsigned int sourceErasureCount = 0;
unsigned int parityCount = 0;
for (unsigned int i = 0; i < bsz; i++)
{
if (i < numData)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
sourceErasureCount++;
}
else
{
gf* p = ((gf*)dec_matrix) + ndata*i;
memset(p, 0, ndata*sizeof(gf));
p[i] = 1;
}
}
else if (i < ndata)
{
gf* p = ((gf*)dec_matrix) + ndata*i;
memset(p, 0, ndata*sizeof(gf));
p[i] = 1;
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
}
else if (parityCount < sourceErasureCount)
{
parity_loc[parityCount++] = i;
gf* p = ((gf*)dec_matrix) + ndata*erasureLocs[ne++];
memcpy(p, ((gf*)enc_matrix) + (ndata-numData+i)*ndata, ndata*sizeof(gf));
}
}
else if (parityCount < sourceErasureCount)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
nextErasure++;
}
else
{
ASSERT(parityCount < npar);
parity_loc[parityCount++] = i;
gf* p = ((gf*)dec_matrix) + ndata*erasureLocs[ne++];
memcpy(p, ((gf*)enc_matrix) + (ndata-numData+i)*ndata, ndata*sizeof(gf));
}
}
else
{
break;
}
}
ASSERT(ne == sourceErasureCount);
if (!InvertDecodingMatrix())
{
PLOG(PL_FATAL, "NormDecoderRS8::Decode() error: couldn't invert dec_matrix ?!\n");
return 0;
}
for (unsigned int e = 0; e < erasureCount; e++)
{
unsigned int row = erasureLocs[e];
if (row >= numData) break; unsigned int col = 0;
unsigned int nextErasure = 0;
unsigned int nelements = (GF_BITS > 8) ? vector_size/2 : vector_size;
for (unsigned int i = 0; i < numData; i++)
{
if ((nextErasure < erasureCount) && (i == erasureLocs[nextErasure]))
{
addmul((gf*)vectorList[row], (gf*)vectorList[parity_loc[nextErasure]], ((gf*)dec_matrix)[row*ndata + col], nelements);
col++;
nextErasure++; }
else if (i < numData)
{
addmul((gf*)vectorList[row], (gf*)vectorList[i], ((gf*)dec_matrix)[row*ndata + col], nelements);
col++;
}
else
{
ASSERT(0);
}
}
}
return erasureCount ;
}
bool NormDecoderRS8::InvertDecodingMatrix()
{
gf* src = (gf*)dec_matrix;
unsigned int k = ndata;
memset(inv_id_row, 0, k*sizeof(gf));
memset(inv_pivt, 0, k*sizeof(unsigned int));
for (unsigned int col = 0; col < k ; col++)
{
int irow = -1;
int icol = -1 ;
if (inv_pivt[col] != 1 && src[col*k + col] != 0)
{
irow = col ;
icol = col ;
goto found_piv ;
}
for (unsigned int row = 0 ; row < k ; row++)
{
if (inv_pivt[row] != 1)
{
for (unsigned int ix = 0 ; ix < k ; ix++)
{
if (inv_pivt[ix] == 0)
{
if (src[row*k + ix] != 0)
{
irow = row ;
icol = ix ;
goto found_piv ;
}
}
else if (inv_pivt[ix] > 1)
{
PLOG(PL_FATAL, "NormDecoderRS8::InvertDecodingMatrix() error: singular matrix!\n");
return false;
}
}
}
}
if (icol == -1)
{
PLOG(PL_FATAL, "NormDecoderRS8::InvertDecodingMatrix() error: pivot not found!\n");
return false;
}
found_piv:
++(inv_pivt[icol]) ;
if (irow != icol)
{
for (unsigned int ix = 0 ; ix < k ; ix++ )
SWAP(src[irow*k + ix], src[icol*k + ix], gf);
}
inv_ndxr[col] = irow ;
inv_ndxc[col] = icol ;
gf* pivotRow = &src[icol*k] ;
gf c = pivotRow[icol] ;
if (c == 0)
{
PLOG(PL_FATAL, "NormDecoderRS8::InvertDecodingMatrix() error: singular matrix!\n");
return false;
}
if (c != 1 )
{
c = inverse[ c ] ;
pivotRow[icol] = 1 ;
for (unsigned int ix = 0 ; ix < k ; ix++ )
pivotRow[ix] = gf_mul(c, pivotRow[ix] );
}
inv_id_row[icol] = 1;
if (0 != memcmp(pivotRow, inv_id_row, k*sizeof(gf)))
{
for (gf* p = src, ix = 0 ; ix < k ; ix++, p += k )
{
if (ix != icol)
{
c = p[icol] ;
p[icol] = 0 ;
addmul(p, pivotRow, c, k );
}
}
}
inv_id_row[icol] = 0;
}
for (int col = k - 1 ; col >= 0 ; col-- )
{
if (inv_ndxr[col] >= k)
{
PLOG(PL_ERROR, "NormDecoderRS8::InvertDecodingMatrix() error: AARGH, inv_ndxr[col] %d\n", inv_ndxr[col]);
}
else if (inv_ndxc[col] >= k)
{
PLOG(PL_ERROR, "NormDecoderRS8::InvertDecodingMatrix() error: AARGH, indxc[col] %d\n", inv_ndxc[col]);
}
else if (inv_ndxr[col] != inv_ndxc[col] )
{
for (unsigned int row = 0 ; row < k ; row++ )
SWAP( src[row*k + inv_ndxr[col]], src[row*k + inv_ndxc[col]], gf) ;
}
}
return true;
}