code:
// main.c
// Word Error Rate test example for Q-ary RA codes over GF(64)
//
// (c) 2016 - Nico Palermo, IV3NWV
//
// Thanks to Andrea Montefusco IW0HDV for his help on adapting the sources
// to OSs other than MS Windows
//
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
// Files in this package:
//    main.c		 - this file
//    normrnd.c/.h   - random gaussian number generator
//    npfwht.c/.h    - Fast Walsh-Hadamard Transforms
//    pdmath.c/.h    - Elementary math on probability distributions
//    qra12_63_64_irr_b.c/.h - Tables for a QRA(12,63) irregular RA code over GF(64)
//    qra13_64_64_irr_e.c/.h - Tables for a QRA(13,64) irregular RA code "     "
//    qracodes.c/.h  - QRA codes encoding/decoding functions
//
// -------------------------------------------------------------------------------
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

// -----------------------------------------------------------------------------

// Two codes are available for simulations in this sowftware release:

// QRA12_63_64_IRR_B: K=12 N=63 Q=64 irregular QRA code (defined in qra12_63_64_irr_b.h /.c)
// QRA13_64_64_IRR_E: K=13 N=64 Q=64 irregular QRA code (defined in qra13_64_64_irr_b.h /.c)

// Codes with K=13 are designed to include a CRC as the 13th information symbol
// and improve the code UER (Undetected Error Rate).
// The CRC symbol is not sent along the channel (the codes are punctured) and the
// resulting code is still a (12,63) code with an effective code rate of R = 12/63.

// ------------------------------------------------------------------------------

// OS dependent defines and includes --------------------------------------------

#if _WIN32 // note the underscore: without it, it's not msdn official!
	// Windows (x64 and x86)
	#include <windows.h>   // required only for GetTickCount(...)
	#include <process.h>   // _beginthread
#endif

#if defined(__linux__)

// remove unwanted macros
#define __cdecl

// implements Windows API
#include <time.h>

 unsigned int GetTickCount(void) {
    struct timespec ts;
    unsigned int theTick = 0U;
    clock_gettime( CLOCK_REALTIME, &ts );
    theTick  = ts.tv_nsec / 1000000;
    theTick += ts.tv_sec * 1000;
    return theTick;
}

// Convert Windows millisecond sleep
//
// VOID WINAPI Sleep(_In_ DWORD dwMilliseconds);
//
// to Posix usleep (in microseconds)
//
// int usleep(useconds_t usec);
//
#include <unistd.h>
#define Sleep(x)  usleep(x*1000)

#endif

#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )
#include <pthread.h>
#endif

#if __APPLE__
#endif

#include <stdlib.h>
#include <stdio.h>

#include "qracodes.h"
#include "normrnd.h"		   // gaussian numbers generator
#include "pdmath.h"			   // operations on probability distributions

// defined codes
#include "qra12_63_64_irr_b.h"
#include "qra13_64_64_irr_e.h"

// -----------------------------------------------------------------------------------

#define NTHREADS_MAX 160

// channel types
#define CHANNEL_AWGN     0
#define CHANNEL_RAYLEIGH 1

// amount of a-priori information provided to the decoder
#define AP_NONE 0
#define AP_28   1
#define AP_44   2
#define AP_56   3

const char ap_str[4][16] = {
	"None",
	"28 bit",
	"44 bit",
	"56 bit"
};

const char fnameout_pfx[2][64] = {
	"wer-awgn-",
	"wer-rayleigh-"
};
const char fnameout_sfx[4][64] = {
	"-ap00.txt",
	"-ap28.txt",
	"-ap44.txt",
	"-ap56.txt"
};

const int ap_masks_jt65[4][13] = {
// Each row must be 13 entries long (to handle puntc. codes 13,64)
// The mask of 13th symbol (crc) is alway initializated to 0
	// AP0  - no a-priori knowledge
	{   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0},
	// AP28 - 1st field known [cq ? ?] or [dst ? ?]
	{0x3F,0x3F,0x3F,0x3F,0x3C,   0,   0,   0,   0,   0,   0,   0},
	// AP44 - 1st and 3rd fields known [cq ? 0] or [dst ? 0]
	{0x3F,0x3F,0x3F,0x3F,0x3C,   0,   0,   0,   0,0x0F,0x3F,0x3F},
	// AP56 - 1st and 2nd fields known [dst src ?]
    {0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,0x30,   0,   0}
};

void ix_mask(const qracode *pcode, float *r, const int *mask, const int *x);

void printword(char *msg, int *x, int size)
{
	int k;
	printf("\n%s ",msg);
	for (k=0;k<size;k++)
		printf("%02hx ",x[k]);
	printf("\n");
}

typedef struct {
	int channel_type;
	float EbNodB;
	volatile int nt;
	volatile int nerrs;
	volatile int nerrsu;
	volatile int stop;
	volatile int done;
	int		ap_index;		// index to the a priori knowledge mask
	const qracode	*pcode;	// pointer to the code descriptor
	int	*x;				//[qra_K];		    input message buffer
	int	*y, *ydec;		//[qra_N];			encoded/decoded codewords buffers
	float	*qra_v2cmsg;	//[qra_NMSG*qra_M]; MP decoder v->c msg buffer
	float	*qra_c2vmsg;	//[qra_NMSG*qra_M]; MP decoder c->v msg buffer
	float	*rp;			// [qra_N*qra_M];	received samples (real component) buffer
	float	*rq;			// [qra_N*qra_M];	received samples (imag component) buffer
	float   *chp;			//[qra_N];			channel gains (real component) buffer
	float	*chq;			//[qra_N];			channel gains (imag component) buffer
	float   *r;				//[qra_N*qra_M];	received samples (amplitude)   buffer
	float   *ix;			// [qra_N*qra_M];	// intrinsic information to the MP algorithm
	float   *ex;			// [qra_N*qra_M];	// extrinsic information from the MP algorithm

} wer_test_ds;

typedef void( __cdecl *pwer_test_thread)(wer_test_ds*);

// crc-6 generator polynomial
// g(x) = x^6 + a5*x^5 + ... + a1*x + a0

// g(x) = x^6 + x + 1
#define CRC6_GEN_POL 0x30  // MSB=a0 LSB=a5

// g(x) = x^6 + x^2 + x + 1 (as suggested by Joe. See:  https://users.ece.cmu.edu/~koopman/crc/)
// #define CRC6_GEN_POL 0x38  // MSB=a0 LSB=a5. Simulation results are similar

int calc_crc6(int *x, int sz)
{
	int k,j,t,sr = 0;
	for (k=0;k<sz;k++) {
		t = x[k];
		for (j=0;j<6;j++) {
			if ((t^sr)&0x01)
				sr = (sr>>1) ^ CRC6_GEN_POL;
			else
				sr = (sr>>1);
			t>>=1;
			}
		}
	return sr;
}

void wer_test_thread(wer_test_ds *pdata)
{
	const qracode *pcode=pdata->pcode;
	const int qra_K = pcode->K;
	const int qra_N = pcode->N;
	const int qra_M = pcode->M;
	const int qra_m = pcode->m;
	const int NSAMPLES = pcode->N*pcode->M;

	const float No = 1.0f;		// noise spectral density
	const float sigma   = (float)sqrt(No/2.0f);	// std dev of noise I/Q components
	const float sigmach = (float)sqrt(1/2.0f);	// std dev of channel I/Q gains

	// Eb/No value for which we optimize the bessel metric
	const float EbNodBMetric = 2.8f;
	const float EbNoMetric   = (float)pow(10,EbNodBMetric/10);

	int k,t,j,diff;
	float R;
	float EsNoMetric;
	float EbNo, EsNo, Es, A;
	int channel_type, code_type;
	int nt=0;				// transmitted codewords
	int nerrs  = 0;			// total number of errors
	int nerrsu = 0;			// number of undetected errors
	int rc;

	// inizialize pointer to required buffers
	int *x=pdata->x;			// message buffer
	int *y=pdata->y, *ydec=pdata->ydec;	// encoded/decoded codeword buffers
	float *qra_v2cmsg=pdata->qra_v2cmsg; // table of the v->c messages
	float *qra_c2vmsg=pdata->qra_c2vmsg; // table of the c->v messages
	float *rp=pdata->rp;		// received samples (real component)
	float *rq=pdata->rq;		// received samples (imag component)
	float *chp=pdata->chp;		// channel gains (real component)
	float *chq=pdata->chq;		// channel gains (imag component)
	float *r=pdata->r;			// received samples amplitudes
	float *ix=pdata->ix;		// intrinsic information to the MP algorithm
	float *ex=pdata->ex;		// extrinsic information from the MP algorithm

	channel_type = pdata->channel_type;
	code_type    = pcode->type;

	// define the (true) code rate accordingly to the code type
	switch(code_type) {
		case QRATYPE_CRC:
			R = 1.0f*(qra_K-1)/qra_N;
			break;
		case QRATYPE_CRCPUNCTURED:
			R = 1.0f*(qra_K-1)/(qra_N-1);
			break;
		case QRATYPE_NORMAL:
		default:
			R = 1.0f*(qra_K)/(qra_N);
		}

	EsNoMetric   = 1.0f*qra_m*R*EbNoMetric;

	EbNo = (float)pow(10,pdata->EbNodB/10);
	EsNo = 1.0f*qra_m*R*EbNo;
	Es = EsNo*No;
	A = (float)sqrt(Es);

	// encode the input
	if (code_type==QRATYPE_CRC || code_type==QRATYPE_CRCPUNCTURED) {
		// compute the information message symbol check as the (negated) xor of all the
		// information message symbols
		for (k=0;k<(qra_K-1);k++)
			x[k]=k%qra_M;
		x[k]=calc_crc6(x,qra_K-1);
		}
	else
		for (k=0;k<qra_K;k++)
			x[k]=k%qra_M;

	qra_encode(pcode,y,x);

	while (pdata->stop==0) {

		// simulate the channel
		// NOTE: in the case that the code is punctured, for simplicity
		// we compute the channel outputs and the metric also for the crc symbol
		// then we ignore its observation.
		normrnd_s(rp,NSAMPLES,0,sigma);
		normrnd_s(rq,NSAMPLES,0,sigma);

		if (channel_type == CHANNEL_AWGN) {
			for (k=0;k<qra_N;k++)
				rp[k*qra_M+y[k]]+=A;
			}
		else if (channel_type == CHANNEL_RAYLEIGH) {
			normrnd_s(chp,qra_N,0,sigmach);
			normrnd_s(chq,qra_N,0,sigmach);
			for (k=0;k<qra_N;k++) {
				rp[k*qra_M+y[k]]+=A*chp[k];
				rq[k*qra_M+y[k]]+=A*chq[k];
				}
			}
		else {
			pdata->done = 1;
			return;	// unknown channel type
			}

		// compute the squares of the amplitudes of the received samples
		for (k=0;k<NSAMPLES;k++)
			r[k] = rp[k]*rp[k] + rq[k]*rq[k];

		// compute the intrinsic symbols probabilities
		qra_mfskbesselmetric(ix,r,pcode->m,pcode->N,EsNoMetric);

		if (code_type==QRATYPE_CRCPUNCTURED) {
			// ignore observations of the CRC symbol as it is not actually sent
			// over the channel
			pd_init(PD_ROWADDR(ix,qra_M,qra_K),pd_uniform(qra_m),qra_M);
			}

		if (pdata->ap_index!=0)
			// mask channel observations with a priori knowledge
			ix_mask(pcode,ix,ap_masks_jt65[pdata->ap_index],x);

		// compute the extrinsic symbols probabilities with the message-passing algorithm
		// stop if extrinsic information does not converges to 1 within the given number of iterations
		rc = qra_extrinsic(pcode,ex,ix,100,qra_v2cmsg,qra_c2vmsg);

		if (rc>=0) { // the MP algorithm converged to Iex~1 in rc iterations

			// decode the codeword
			qra_mapdecode(pcode,ydec,ex,ix);

			// look for undetected errors
			if (code_type==QRATYPE_CRC || code_type==QRATYPE_CRCPUNCTURED) {

				j = 0; diff = 0;
				for (k=0;k<(qra_K-1);k++)
					diff |= (ydec[k]!=x[k]);
				t = calc_crc6(ydec,qra_K-1);
				if (t!=ydec[k]) // error detected - crc doesn't matches
					nerrs  += 1;
				else
					if (diff) {	// decoded message is not equal to the transmitted one but
						        // the crc test passed
						// add as undetected error
						nerrsu += 1;
						nerrs  += 1;
						// uncomment to see what the undetected error pattern looks like
						//printword("U", ydec);
						}
				}
			else
				for (k=0;k<qra_K;k++)
					if (ydec[k]!=x[k]) {	// decoded msg differs from the transmitted one
						nerrsu += 1;		// it's a false decode
						nerrs  += 1;
						// uncomment to see what the undetected error pattern looks like
						// printword("U", ydec);
						break;
						}

			}
		else // failed to converge to a solution within the given number of iterations
			nerrs++;

		nt = nt+1;

		pdata->nt=nt;
		pdata->nerrs=nerrs;
		pdata->nerrsu=nerrsu;

		}

	pdata->done=1;

	#if _WIN32
	_endthread();
	#endif
}

#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )

void *wer_test_pthread(void *p)
{
	wer_test_thread ((wer_test_ds *)p);
	return 0;
}

#endif

void ix_mask(const qracode *pcode, float *r, const int *mask, const int *x)
{
	// mask intrinsic information (channel observations) with a priori knowledge

	int k,kk, smask;
	const int qra_K=pcode->K;
	const int qra_M=pcode->M;
	const int qra_m=pcode->m;

	for (k=0;k<qra_K;k++) {
		smask = mask[k];
		if (smask) {
			for (kk=0;kk<qra_M;kk++)
				if (((kk^x[k])&smask)!=0)
					*(PD_ROWADDR(r,qra_M,k)+kk) = 0.f;

			pd_norm(PD_ROWADDR(r,qra_M,k),qra_m);
			}
		}
}

int wer_test_proc(const qracode *pcode, int nthreads, int chtype, int ap_index, float *EbNodB, int *nerrstgt, int nitems)
{
	int k,nn,j,nt,nerrs,nerrsu,nd;
	int cini,cend;
	char fnameout[128];
	FILE *fout;
	wer_test_ds wt[NTHREADS_MAX];
	float pe,avgt;

	nn = sizeof(EbNodB)/sizeof(float);	// size of the EbNo array to test

	if (nthreads>NTHREADS_MAX) {
		printf("Error: nthreads should be <=%d\n",NTHREADS_MAX);
		return -1;
		}

	sprintf(fnameout,"%s%s%s",
				fnameout_pfx[chtype],
				pcode->name,
				fnameout_sfx[ap_index]);

	fout = fopen(fnameout,"w");
	fprintf(fout,"# Channel (0=AWGN,1=Rayleigh), Eb/No (dB), Transmitted codewords, Errors, Undetected Errors, Avg dec. time (ms), WER\n");

	printf("\nTesting the code %s over the %s channel\nSimulation data will be saved to %s\n",
			pcode->name,
			chtype==CHANNEL_AWGN?"AWGN":"Rayleigh",
			fnameout);
	fflush (stdout);

	// init fixed thread parameters and preallocate buffers
	for (j=0;j<nthreads;j++)  {
			wt[j].channel_type=chtype;
			wt[j].ap_index = ap_index;
			wt[j].pcode    = pcode;
			wt[j].x        = (int*)malloc(pcode->K*sizeof(int));
			wt[j].y        = (int*)malloc(pcode->N*sizeof(int));
			wt[j].ydec     = (int*)malloc(pcode->N*sizeof(int));
			wt[j].qra_v2cmsg = (float*)malloc(pcode->NMSG*pcode->M*sizeof(float));
			wt[j].qra_c2vmsg = (float*)malloc(pcode->NMSG*pcode->M*sizeof(float));
			wt[j].rp       = (float*)malloc(pcode->N*pcode->M*sizeof(float));
			wt[j].rq       = (float*)malloc(pcode->N*pcode->M*sizeof(float));
			wt[j].chp      = (float*)malloc(pcode->N*sizeof(float));
			wt[j].chq      = (float*)malloc(pcode->N*sizeof(float));
			wt[j].r        = (float*)malloc(pcode->N*pcode->M*sizeof(float));
			wt[j].ix       = (float*)malloc(pcode->N*pcode->M*sizeof(float));
			wt[j].ex       = (float*)malloc(pcode->N*pcode->M*sizeof(float));
		}

	for (k=0;k<nitems;k++) {

		printf("\nTesting at Eb/No=%4.1f dB...",EbNodB[k]);
		fflush (stdout);

		for (j=0;j<nthreads;j++)  {
			wt[j].EbNodB=EbNodB[k];
			wt[j].nt=0;
			wt[j].nerrs=0;
			wt[j].nerrsu=0;
			wt[j].done = 0;
			wt[j].stop = 0;
			#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )
			if (pthread_create (&wt[j].thread, 0, wer_test_pthread, &wt[j])) {
				perror ("Creating thread: ");
				exit (255);
			}
			#else
			_beginthread((void*)(void*)wer_test_thread,0,&wt[j]);
			#endif
			}

		nd = 0;
		cini = GetTickCount();

		while (1) {
			// count errors
			nerrs = 0;
			for (j=0;j<nthreads;j++)
				nerrs += wt[j].nerrs;
			// stop the working threads
			// if number of errors reached at this Eb/No value
			if (nerrs>=nerrstgt[k]) {
				for (j=0;j<nthreads;j++)
					wt[j].stop = 1;
				break;
				}
			else { // continue with the simulation
				Sleep(2);
				nd = (nd+1)%100;
				if (nd==0) {
					printf(".");
					fflush (stdout);
					}
				}

			}

		cend = GetTickCount();

		// wait for the working threads to exit
		for (j=0;j<nthreads;j++)
		#if defined(__linux__) || ( defined(__MINGW32__) || defined (__MIGW64__) )
		{
			void *rc;
			if (pthread_join (wt[j].thread, &rc)) {
				perror ("Waiting working threads to exit");
				exit (255);
			}
		}
		#else
			while(wt[j].done==0)
				Sleep(1);

		#endif
		printf("\n");
		fflush (stdout);

		// compute the total number of transmitted codewords
		// the total number of errors and the total number of undetected errors
		nt = 0;
		nerrs =0;
		nerrsu = 0;
		for (j=0;j<nthreads;j++) {
			nt += wt[j].nt;
			nerrs += wt[j].nerrs;
			nerrsu += wt[j].nerrsu;
			}

		pe = 1.0f*nerrs/nt;			// word error rate
		avgt = 1.0f*(cend-cini)/nt; // average time per decode (ms)

		printf("Elapsed Time=%6.1fs (%5.2fms/word)\nTransmitted=%8d - Errors=%6d - Undetected=%3d - WER=%.2e\n",
				0.001f*(cend-cini),
				avgt, nt, nerrs, nerrsu, pe);
		fflush (stdout);

		// save simulation data to output file
		fprintf(fout,"%01d %.2f %d %d %d %.2f %.2e\n",
					chtype,
					EbNodB[k],
					nt,
					nerrs,
					nerrsu,
					avgt,
					pe);

		}

	fclose(fout);

	return 0;
}

const qracode *codetotest[] = {
	&qra_12_63_64_irr_b,
	&qra_13_64_64_irr_e
};

void syntax(void)
{
	printf("\nQ-ary Repeat-Accumulate Code Word Error Rate Simulator\n");
	printf("2016, Nico Palermo - IV3NWV\n\n");
	printf("Syntax: qracodes [-q<code_index>] [-t<threads>] [-c<ch_type>] [-a<ap_index>] [-f<fnamein>[-h]\n");
	printf("Options: \n");
	printf("       -q<code_index>: code to simulate. 0=qra_12_63_64_irr_b\n");
	printf("                                         1=qra_13_64_64_irr_e (default)\n");
	printf("       -t<threads>   : number of threads to be used for the simulation [1..24]\n");
	printf("                       (default=8)\n");
	printf("       -c<ch_type>   : channel_type. 0=AWGN 1=Rayleigh \n");
	printf("                       (default=AWGN)\n");
	printf("       -a<ap_index>  : amount of a-priori information provided to decoder. \n");
	printf("                       0= No a-priori (default)\n");
	printf("                       1= 28 bit \n");
	printf("                       2= 44 bit \n");
	printf("                       3= 56 bit \n");
	printf("       -f<fnamein>   : name of the file containing the Eb/No values to be simulated\n");
	printf("                       (default=ebnovalues.txt)\n");
	printf("                       This file should contain lines in this format:\n");
	printf("                       # Eb/No(dB) Target Errors\n");
	printf("                       0.1 5000\n");
	printf("                       0.6 5000\n");
	printf("                       1.1 1000\n");
	printf("                       1.6 1000\n");
	printf("                       ...\n");
	printf("                       (lines beginning with a # are treated as comments\n\n");
}

#define SIM_POINTS_MAX 20

int main(int argc, char* argv[])
{

	float EbNodB[SIM_POINTS_MAX];
	int  nerrstgt[SIM_POINTS_MAX];
	FILE *fin;

	char fnamein[128]= "ebnovalues.txt";
	char buf[128];

	int nitems   = 0;
	int code_idx = 1;
	int nthreads = 8;
	int ch_type  = CHANNEL_AWGN;
	int ap_index = AP_NONE;

	// parse command line
	while(--argc) {
		argv++;
		if (strncmp(*argv,"-h",2)==0) {
			syntax();
			return 0;
			}
		else
		if (strncmp(*argv,"-q",2)==0) {
			code_idx = (int)atoi((*argv)+2);
			if (code_idx>1) {
				printf("Invalid code index\n");
				syntax();
				return -1;
				}
			}
		else
		if (strncmp(*argv,"-t",2)==0) {
			nthreads = (int)atoi((*argv)+2);
			printf("nthreads = %d\n",nthreads);
			if (nthreads>NTHREADS_MAX) {
				printf("Invalid number of threads\n");
				syntax();
				return -1;
				}
			}
		else
		if (strncmp(*argv,"-c",2)==0) {
			ch_type = (int)atoi((*argv)+2);
			if (ch_type>CHANNEL_RAYLEIGH) {
				printf("Invalid channel type\n");
				syntax();
				return -1;
				}
			}
		else
		if (strncmp(*argv,"-a",2)==0) {
			ap_index = (int)atoi((*argv)+2);
			if (ap_index>AP_56) {
				printf("Invalid a-priori information index\n");
				syntax();
				return -1;
				}
			}
		else
		if (strncmp(*argv,"-f",2)==0) {
			strncpy(fnamein,(*argv)+2,127);
			}
		else
		if (strncmp(*argv,"-h",2)==0) {
			syntax();
			return -1;
			}
		else {
			printf("Invalid option\n");
			syntax();
			return -1;
			}
		}

	// parse points to be simulated from the input file
	fin = fopen(fnamein,"r");
	if (!fin) {
		printf("Can't open file: %s\n",fnamein);
		syntax();
		}

	while (fgets(buf,128,fin)!=0)
		if (*buf=='#' || *buf=='\n' )
			continue;
		else
			if (nitems==SIM_POINTS_MAX)
				break;
			else
				if (sscanf(buf,"%f %u",&EbNodB[nitems],&nerrstgt[nitems])!=2) {
					printf("Invalid input file format\n");
					syntax();
					return -1;
					}
				else
					nitems++;

	fclose(fin);

	if (nitems==0) {
		printf("No Eb/No point specified in file %s\n",fnamein);
		syntax();
		return -1;
		}

	printf("\nQ-ary Repeat-Accumulate Code Word Error Rate Simulator\n");
	printf("2016, Nico Palermo - IV3NWV\n\n");

	printf("Nthreads   = %d\n",nthreads);
	printf("Channel    = %s\n",ch_type==CHANNEL_AWGN?"AWGN":"Rayleigh");
	printf("Codename   = %s\n",codetotest[code_idx]->name);
	printf("A-priori   = %s\n",ap_str[ap_index]);
	printf("Eb/No input file = %s\n\n",fnamein);

	wer_test_proc(codetotest[code_idx], nthreads, ch_type, ap_index, EbNodB, nerrstgt, nitems);

	return 0;
}


code:
// normrnd.c
// functions to generate gaussian distributed numbers
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
//
// Credits to Andrea Montefusco - IW0HDV for his help on adapting the sources
// to OSs other than MS Windows
//
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#include "normrnd.h"

#if _WIN32 // note the underscore: without it, it's not msdn official!
	// Windows (x64 and x86)
	#include <windows.h>   // required only for GetTickCount(...)
	#define K_RAND_MAX UINT_MAX
#elif _SVID_SOURCE || _XOPEN_SOURCE || __unix__ || (defined (__APPLE__) && defined(__MACH__)) /* POSIX or Unix or Apple */
	#include <stdlib.h>
	#define rand_s(x) (*x)=(unsigned int)lrand48()	// returns unsigned integers in the range 0..0x7FFFFFFF
	#define K_RAND_MAX 0x7FFFFFFF					// that's the max number
																				// generated by lrand48
#else
  #error "No good quality PRNG found"
#endif

// use MS rand_s(...) function
void normrnd_s(float *dst, int nitems, float mean, float stdev)
{
	unsigned int r;
	float phi=0, u=0;
	int set = 0;

	while (nitems--)
		if (set==1) {
			*dst++ = (float)sin(phi)*u*stdev+mean;
			set = 0;
			}
		else {
			rand_s((unsigned int*)&r); phi = (M_2PI/(1.0f+K_RAND_MAX))*r;
			rand_s((unsigned int*)&r); u   = (float)sqrt(-2.0f* log( (1.0f/(1.0f+K_RAND_MAX))*(1.0f+r) ) );
			*dst++ = (float)cos(phi)*u*stdev+mean;
			set=1;
			}
}

/* NOT USED
// use MS rand() function
void normrnd(float *dst, int nitems, float mean, float stdev)
{
	float phi=0, u=0;
	int set = 0;

	while (nitems--)
		if (set==1) {
			*dst++ = (float)sin(phi)*u*stdev+mean;
			set = 0;
			}
		else {
			phi = (M_2PI/(1.0f+RAND_MAX))*rand();
			u   = (float)sqrt(-2.0f* log( (1.0f/(1.0f+RAND_MAX))*(1.0f+rand()) ) );
			*dst++ = (float)cos(phi)*u*stdev+mean;
			set=1;
			}
}
*/

license: mit


code:
// normrnd.h
// Functions to generate gaussian distributed numbers
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#ifndef _normrnd_h_
#define _normrnd_h_

#define _CRT_RAND_S
#include <stdlib.h>

#define _USE_MATH_DEFINES
#include <math.h>
#define M_2PI (2.0f*(float)M_PI)

#ifdef __cplusplus
extern "C" {
#endif

void normrnd_s(float *dst, int nitems, float mean, float stdev);
// generate a random array of numbers with a gaussian distribution of given mean and stdev
// use MS rand_s(...) function

/* not used
void normrnd(float *dst, int nitems, float mean, float stdev);
// generate a random array of numbers with a gaussian distribution of given mean and stdev
// use MS rand() function
*/

#ifdef __cplusplus
}
#endif

#endif // _normrnd_h_


code:
// npfwht.c
// Basic implementation of the Fast Walsh-Hadamard Transforms
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#include "npfwht.h"

#define WHBFY(dst,src,base,offs,dist) { dst[base+offs]=src[base+offs]+src[base+offs+dist]; dst[base+offs+dist]=src[base+offs]-src[base+offs+dist]; }

typedef void (*pnp_fwht)(float*,float*);

static void np_fwht2(float *dst, float *src);

static void np_fwht1(float *dst, float *src);
static void np_fwht2(float *dst, float *src);
static void np_fwht4(float *dst, float *src);
static void np_fwht8(float *dst, float *src);
static void np_fwht16(float *dst, float *src);
static void np_fwht32(float *dst, float *src);
static void np_fwht64(float *dst, float *src);

static pnp_fwht np_fwht_tab[7] = {
	np_fwht1,
	np_fwht2,
	np_fwht4,
	np_fwht8,
	np_fwht16,
	np_fwht32,
	np_fwht64
};

void np_fwht(int nlogdim, float *dst, float *src)
{
	np_fwht_tab[nlogdim](dst,src);
}

static void np_fwht1(float *dst, float *src)
{
	dst[0] = src[0];
}

static void np_fwht2(float *dst, float *src)
{
	float t[2];

	WHBFY(t,src,0,0,1);
	dst[0]= t[0];
	dst[1]= t[1];
}

static void np_fwht4(float *dst, float *src)
{
	float t[4];

	// group 1
	WHBFY(t,src,0,0,2); WHBFY(t,src,0,1,2);
	// group 2
	WHBFY(dst,t,0,0,1); WHBFY(dst,t,2,0,1);
};

static void np_fwht8(float *dst, float *src)
{
	float t[16];
	float *t1=t, *t2=t+8;

	// group 1
	WHBFY(t1,src,0,0,4); WHBFY(t1,src,0,1,4); WHBFY(t1,src,0,2,4); WHBFY(t1,src,0,3,4);
	// group 2
	WHBFY(t2,t1,0,0,2);  WHBFY(t2,t1,0,1,2);  WHBFY(t2,t1,4,0,2);	WHBFY(t2,t1,4,1,2);
	// group 3
	WHBFY(dst,t2,0,0,1); WHBFY(dst,t2,2,0,1); WHBFY(dst,t2,4,0,1); WHBFY(dst,t2,6,0,1);
};

static void np_fwht16(float *dst, float *src)
{
	float t[32];
	float *t1=t, *t2=t+16;

	// group 1
	WHBFY(t1,src,0,0,8); WHBFY(t1,src,0,1,8);  WHBFY(t1,src,0,2,8);  WHBFY(t1,src,0,3,8);
	WHBFY(t1,src,0,4,8); WHBFY(t1,src,0,5,8);  WHBFY(t1,src,0,6,8);  WHBFY(t1,src,0,7,8);
	// group 2
	WHBFY(t2,t1,0,0,4);  WHBFY(t2,t1,0,1,4);   WHBFY(t2,t1,0,2,4);	 WHBFY(t2,t1,0,3,4);
	WHBFY(t2,t1,8,0,4);  WHBFY(t2,t1,8,1,4);   WHBFY(t2,t1,8,2,4);   WHBFY(t2,t1,8,3,4);
	// group 3
	WHBFY(t1,t2,0,0,2);  WHBFY(t1,t2,0,1,2);   WHBFY(t1,t2,4,0,2);   WHBFY(t1,t2,4,1,2);
	WHBFY(t1,t2,8,0,2);  WHBFY(t1,t2,8,1,2);   WHBFY(t1,t2,12,0,2);  WHBFY(t1,t2,12,1,2);
	// group 4
	WHBFY(dst,t1,0,0,1); WHBFY(dst,t1,2,0,1);  WHBFY(dst,t1,4,0,1);  WHBFY(dst,t1,6,0,1);
	WHBFY(dst,t1,8,0,1); WHBFY(dst,t1,10,0,1); WHBFY(dst,t1,12,0,1); WHBFY(dst,t1,14,0,1);

}

static void np_fwht32(float *dst, float *src)
{
	float t[64];
	float *t1=t, *t2=t+32;

	// group 1
	WHBFY(t1,src,0,0,16);  WHBFY(t1,src,0,1,16);  WHBFY(t1,src,0,2,16);  WHBFY(t1,src,0,3,16);
	WHBFY(t1,src,0,4,16);  WHBFY(t1,src,0,5,16);  WHBFY(t1,src,0,6,16);  WHBFY(t1,src,0,7,16);
	WHBFY(t1,src,0,8,16);  WHBFY(t1,src,0,9,16);  WHBFY(t1,src,0,10,16); WHBFY(t1,src,0,11,16);
	WHBFY(t1,src,0,12,16); WHBFY(t1,src,0,13,16); WHBFY(t1,src,0,14,16); WHBFY(t1,src,0,15,16);

	// group 2
	WHBFY(t2,t1,0,0,8);  WHBFY(t2,t1,0,1,8);  WHBFY(t2,t1,0,2,8);  WHBFY(t2,t1,0,3,8);
	WHBFY(t2,t1,0,4,8);  WHBFY(t2,t1,0,5,8);  WHBFY(t2,t1,0,6,8);  WHBFY(t2,t1,0,7,8);
	WHBFY(t2,t1,16,0,8); WHBFY(t2,t1,16,1,8); WHBFY(t2,t1,16,2,8); WHBFY(t2,t1,16,3,8);
	WHBFY(t2,t1,16,4,8); WHBFY(t2,t1,16,5,8); WHBFY(t2,t1,16,6,8); WHBFY(t2,t1,16,7,8);

	// group 3
	WHBFY(t1,t2,0,0,4);  WHBFY(t1,t2,0,1,4);  WHBFY(t1,t2,0,2,4);  WHBFY(t1,t2,0,3,4);
	WHBFY(t1,t2,8,0,4);  WHBFY(t1,t2,8,1,4);  WHBFY(t1,t2,8,2,4);  WHBFY(t1,t2,8,3,4);
	WHBFY(t1,t2,16,0,4); WHBFY(t1,t2,16,1,4); WHBFY(t1,t2,16,2,4); WHBFY(t1,t2,16,3,4);
	WHBFY(t1,t2,24,0,4); WHBFY(t1,t2,24,1,4); WHBFY(t1,t2,24,2,4); WHBFY(t1,t2,24,3,4);

	// group 4
	WHBFY(t2,t1,0,0,2);  WHBFY(t2,t1,0,1,2);  WHBFY(t2,t1,4,0,2);  WHBFY(t2,t1,4,1,2);
	WHBFY(t2,t1,8,0,2);  WHBFY(t2,t1,8,1,2);  WHBFY(t2,t1,12,0,2); WHBFY(t2,t1,12,1,2);
	WHBFY(t2,t1,16,0,2); WHBFY(t2,t1,16,1,2); WHBFY(t2,t1,20,0,2); WHBFY(t2,t1,20,1,2);
	WHBFY(t2,t1,24,0,2); WHBFY(t2,t1,24,1,2); WHBFY(t2,t1,28,0,2); WHBFY(t2,t1,28,1,2);

	// group 5
	WHBFY(dst,t2,0,0,1);   WHBFY(dst,t2,2,0,1);  WHBFY(dst,t2,4,0,1);   WHBFY(dst,t2,6,0,1);
	WHBFY(dst,t2,8,0,1);   WHBFY(dst,t2,10,0,1); WHBFY(dst,t2,12,0,1);  WHBFY(dst,t2,14,0,1);
	WHBFY(dst,t2,16,0,1);  WHBFY(dst,t2,18,0,1); WHBFY(dst,t2,20,0,1);  WHBFY(dst,t2,22,0,1);
	WHBFY(dst,t2,24,0,1);  WHBFY(dst,t2,26,0,1); WHBFY(dst,t2,28,0,1);  WHBFY(dst,t2,30,0,1);

}

static void np_fwht64(float *dst, float *src)
{
	float t[128];
	float *t1=t, *t2=t+64;

	// group 1
	WHBFY(t1,src,0,0,32);  WHBFY(t1,src,0,1,32);  WHBFY(t1,src,0,2,32);  WHBFY(t1,src,0,3,32);
	WHBFY(t1,src,0,4,32);  WHBFY(t1,src,0,5,32);  WHBFY(t1,src,0,6,32);  WHBFY(t1,src,0,7,32);
	WHBFY(t1,src,0,8,32);  WHBFY(t1,src,0,9,32);  WHBFY(t1,src,0,10,32); WHBFY(t1,src,0,11,32);
	WHBFY(t1,src,0,12,32); WHBFY(t1,src,0,13,32); WHBFY(t1,src,0,14,32); WHBFY(t1,src,0,15,32);
	WHBFY(t1,src,0,16,32); WHBFY(t1,src,0,17,32); WHBFY(t1,src,0,18,32); WHBFY(t1,src,0,19,32);
	WHBFY(t1,src,0,20,32); WHBFY(t1,src,0,21,32); WHBFY(t1,src,0,22,32); WHBFY(t1,src,0,23,32);
	WHBFY(t1,src,0,24,32); WHBFY(t1,src,0,25,32); WHBFY(t1,src,0,26,32); WHBFY(t1,src,0,27,32);
	WHBFY(t1,src,0,28,32); WHBFY(t1,src,0,29,32); WHBFY(t1,src,0,30,32); WHBFY(t1,src,0,31,32);

	// group 2
	WHBFY(t2,t1,0,0,16);   WHBFY(t2,t1,0,1,16);  WHBFY(t2,t1,0,2,16);   WHBFY(t2,t1,0,3,16);
	WHBFY(t2,t1,0,4,16);   WHBFY(t2,t1,0,5,16);  WHBFY(t2,t1,0,6,16);   WHBFY(t2,t1,0,7,16);
	WHBFY(t2,t1,0,8,16);   WHBFY(t2,t1,0,9,16);  WHBFY(t2,t1,0,10,16);  WHBFY(t2,t1,0,11,16);
	WHBFY(t2,t1,0,12,16);  WHBFY(t2,t1,0,13,16); WHBFY(t2,t1,0,14,16);  WHBFY(t2,t1,0,15,16);

	WHBFY(t2,t1,32,0,16);  WHBFY(t2,t1,32,1,16);  WHBFY(t2,t1,32,2,16);  WHBFY(t2,t1,32,3,16);
	WHBFY(t2,t1,32,4,16);  WHBFY(t2,t1,32,5,16);  WHBFY(t2,t1,32,6,16);  WHBFY(t2,t1,32,7,16);
	WHBFY(t2,t1,32,8,16);  WHBFY(t2,t1,32,9,16);  WHBFY(t2,t1,32,10,16); WHBFY(t2,t1,32,11,16);
	WHBFY(t2,t1,32,12,16); WHBFY(t2,t1,32,13,16); WHBFY(t2,t1,32,14,16); WHBFY(t2,t1,32,15,16);

	// group 3
	WHBFY(t1,t2,0,0,8);    WHBFY(t1,t2,0,1,8); 	 WHBFY(t1,t2,0,2,8);    WHBFY(t1,t2,0,3,8);
	WHBFY(t1,t2,0,4,8);    WHBFY(t1,t2,0,5,8); 	 WHBFY(t1,t2,0,6,8);    WHBFY(t1,t2,0,7,8);
	WHBFY(t1,t2,16,0,8);   WHBFY(t1,t2,16,1,8);  WHBFY(t1,t2,16,2,8);   WHBFY(t1,t2,16,3,8);
	WHBFY(t1,t2,16,4,8);   WHBFY(t1,t2,16,5,8);  WHBFY(t1,t2,16,6,8);   WHBFY(t1,t2,16,7,8);
	WHBFY(t1,t2,32,0,8);   WHBFY(t1,t2,32,1,8);  WHBFY(t1,t2,32,2,8);   WHBFY(t1,t2,32,3,8);
	WHBFY(t1,t2,32,4,8);   WHBFY(t1,t2,32,5,8);  WHBFY(t1,t2,32,6,8);   WHBFY(t1,t2,32,7,8);
	WHBFY(t1,t2,48,0,8);   WHBFY(t1,t2,48,1,8);  WHBFY(t1,t2,48,2,8);   WHBFY(t1,t2,48,3,8);
	WHBFY(t1,t2,48,4,8);   WHBFY(t1,t2,48,5,8);  WHBFY(t1,t2,48,6,8);   WHBFY(t1,t2,48,7,8);

	// group 4
	WHBFY(t2,t1,0,0,4);   WHBFY(t2,t1,0,1,4); 	WHBFY(t2,t1,0,2,4);  WHBFY(t2,t1,0,3,4);
	WHBFY(t2,t1,8,0,4);   WHBFY(t2,t1,8,1,4); 	WHBFY(t2,t1,8,2,4);  WHBFY(t2,t1,8,3,4);
	WHBFY(t2,t1,16,0,4);  WHBFY(t2,t1,16,1,4); 	WHBFY(t2,t1,16,2,4); WHBFY(t2,t1,16,3,4);
	WHBFY(t2,t1,24,0,4);  WHBFY(t2,t1,24,1,4); 	WHBFY(t2,t1,24,2,4); WHBFY(t2,t1,24,3,4);
	WHBFY(t2,t1,32,0,4);  WHBFY(t2,t1,32,1,4); 	WHBFY(t2,t1,32,2,4); WHBFY(t2,t1,32,3,4);
	WHBFY(t2,t1,40,0,4);  WHBFY(t2,t1,40,1,4); 	WHBFY(t2,t1,40,2,4); WHBFY(t2,t1,40,3,4);
	WHBFY(t2,t1,48,0,4);  WHBFY(t2,t1,48,1,4); 	WHBFY(t2,t1,48,2,4); WHBFY(t2,t1,48,3,4);
	WHBFY(t2,t1,56,0,4);  WHBFY(t2,t1,56,1,4); 	WHBFY(t2,t1,56,2,4); WHBFY(t2,t1,56,3,4);

	// group 5
	WHBFY(t1,t2,0,0,2);  WHBFY(t1,t2,0,1,2);  WHBFY(t1,t2,4,0,2);  WHBFY(t1,t2,4,1,2);
	WHBFY(t1,t2,8,0,2);  WHBFY(t1,t2,8,1,2);  WHBFY(t1,t2,12,0,2); WHBFY(t1,t2,12,1,2);
	WHBFY(t1,t2,16,0,2); WHBFY(t1,t2,16,1,2); WHBFY(t1,t2,20,0,2); WHBFY(t1,t2,20,1,2);
	WHBFY(t1,t2,24,0,2); WHBFY(t1,t2,24,1,2); WHBFY(t1,t2,28,0,2); WHBFY(t1,t2,28,1,2);
	WHBFY(t1,t2,32,0,2); WHBFY(t1,t2,32,1,2); WHBFY(t1,t2,36,0,2); WHBFY(t1,t2,36,1,2);
	WHBFY(t1,t2,40,0,2); WHBFY(t1,t2,40,1,2); WHBFY(t1,t2,44,0,2); WHBFY(t1,t2,44,1,2);
	WHBFY(t1,t2,48,0,2); WHBFY(t1,t2,48,1,2); WHBFY(t1,t2,52,0,2); WHBFY(t1,t2,52,1,2);
	WHBFY(t1,t2,56,0,2); WHBFY(t1,t2,56,1,2); WHBFY(t1,t2,60,0,2); WHBFY(t1,t2,60,1,2);

	// group 6
	WHBFY(dst,t1,0,0,1);   WHBFY(dst,t1,2,0,1);  WHBFY(dst,t1,4,0,1);  WHBFY(dst,t1,6,0,1);
	WHBFY(dst,t1,8,0,1);   WHBFY(dst,t1,10,0,1); WHBFY(dst,t1,12,0,1); WHBFY(dst,t1,14,0,1);
	WHBFY(dst,t1,16,0,1);  WHBFY(dst,t1,18,0,1); WHBFY(dst,t1,20,0,1); WHBFY(dst,t1,22,0,1);
	WHBFY(dst,t1,24,0,1);  WHBFY(dst,t1,26,0,1); WHBFY(dst,t1,28,0,1); WHBFY(dst,t1,30,0,1);
	WHBFY(dst,t1,32,0,1);  WHBFY(dst,t1,34,0,1); WHBFY(dst,t1,36,0,1); WHBFY(dst,t1,38,0,1);
	WHBFY(dst,t1,40,0,1);  WHBFY(dst,t1,42,0,1); WHBFY(dst,t1,44,0,1); WHBFY(dst,t1,46,0,1);
	WHBFY(dst,t1,48,0,1);  WHBFY(dst,t1,50,0,1); WHBFY(dst,t1,52,0,1); WHBFY(dst,t1,54,0,1);
	WHBFY(dst,t1,56,0,1);  WHBFY(dst,t1,58,0,1); WHBFY(dst,t1,60,0,1); WHBFY(dst,t1,62,0,1);
}


code:
// np_fwht.h
// Basic implementation of the Fast Walsh-Hadamard Transforms
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#ifndef _npfwht_h_
#define _npfwht_h_

#ifdef __cplusplus
extern "C" {
#endif

void np_fwht(int nlogdim, float *dst, float *src);
// Compute the Walsh-Hadamard transform of the given data up to a
// 64-dimensional transform
//
// Input parameters:
//		nlogdim:  log2 of the transform size. Must be in the range [0..6]
//      src    :  pointer to the input  data buffer.
//      dst    :  pointer to the output data buffer.
//
// src and dst must point to preallocated data buffers of size 2^nlogdim*sizeof(float)
// src and dst buffers can overlap

#ifdef __cplusplus
}
#endif

#endif // _npfwht_


code:
// pdmath.c
// Elementary math on probability distributions
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#include "pdmath.h"

typedef const float *ppd_uniform;
typedef void  (*ppd_imul)(float*,const float*);
typedef float (*ppd_norm)(float*);

// define vector size in function of its logarithm in base 2
static const int pd_log2dim[7] = {
	1,2,4,8,16,32,64
};

// define uniform distributions of given size
static const float pd_uniform1[1] = {
	1.
};
static const float pd_uniform2[2] = {
	1./2., 1./2.
};
static const float pd_uniform4[4] = {
	1./4., 1./4.,1./4., 1./4.
};
static const float pd_uniform8[8] = {
	1./8., 1./8.,1./8., 1./8.,1./8., 1./8.,1./8., 1./8.
};
static const float pd_uniform16[16] = {
	1./16., 1./16., 1./16., 1./16.,1./16., 1./16.,1./16., 1./16.,
	1./16., 1./16., 1./16., 1./16.,1./16., 1./16.,1./16., 1./16.
};
static const float pd_uniform32[32] = {
	1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32.,
	1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32.,
	1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32.,
	1./32., 1./32., 1./32., 1./32.,1./32., 1./32.,1./32., 1./32.
};
static const float pd_uniform64[64] = {
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.,
	1./64., 1./64., 1./64., 1./64.,1./64., 1./64.,1./64., 1./64.

};

static const ppd_uniform pd_uniform_tab[7] = {
	pd_uniform1,
	pd_uniform2,
	pd_uniform4,
	pd_uniform8,
	pd_uniform16,
	pd_uniform32,
	pd_uniform64
};

// returns a pointer to the uniform distribution of the given logsize
const float *pd_uniform(int nlogdim)
{
	return pd_uniform_tab[nlogdim];
}

// in-place multiplication functions
// compute dst = dst*src for any element of the distrib

static void pd_imul1(float *dst, const float *src)
{
		dst[0] *= src[0];
}

static void pd_imul2(float *dst, const float *src)
{
		dst[0] *= src[0]; dst[1] *= src[1];
}
static void pd_imul4(float *dst, const float *src)
{
		dst[0] *= src[0]; dst[1] *= src[1];
		dst[2] *= src[2]; dst[3] *= src[3];
}
static void pd_imul8(float *dst, const float *src)
{
		dst[0] *= src[0]; dst[1] *= src[1]; dst[2] *= src[2]; dst[3] *= src[3];
		dst[4] *= src[4]; dst[5] *= src[5]; dst[6] *= src[6]; dst[7] *= src[7];
}
static void pd_imul16(float *dst, const float *src)
{
		dst[0] *= src[0];  dst[1] *= src[1];  dst[2] *= src[2];  dst[3] *= src[3];
		dst[4] *= src[4];  dst[5] *= src[5];  dst[6] *= src[6];  dst[7] *= src[7];
		dst[8] *= src[8];  dst[9] *= src[9];  dst[10]*= src[10]; dst[11]*= src[11];
		dst[12]*= src[12]; dst[13]*= src[13]; dst[14]*= src[14]; dst[15]*= src[15];
}
static void pd_imul32(float *dst, const float *src)
{
	pd_imul16(dst,src);
	pd_imul16(dst+16,src+16);
}
static void pd_imul64(float *dst, const float *src)
{
	pd_imul16(dst, src);
	pd_imul16(dst+16, src+16);
	pd_imul16(dst+32, src+32);
	pd_imul16(dst+48, src+48);
}

static const ppd_imul pd_imul_tab[7] = {
	pd_imul1,
	pd_imul2,
	pd_imul4,
	pd_imul8,
	pd_imul16,
	pd_imul32,
	pd_imul64
};

// in place multiplication
// compute dst = dst*src for any element of the distrib give their log2 size
// arguments must be pointers to array of floats of the given size
void pd_imul(float *dst, const float *src, int nlogdim)
{
	pd_imul_tab[nlogdim](dst,src);
}

static float pd_norm1(float *ppd)
{
	float t = ppd[0];
	ppd[0] = 1.f;
	return t;
}

static float pd_norm2(float *ppd)
{
	float t,to;

	t =ppd[0]; t +=ppd[1];

	if (t<=0) {
		pd_init(ppd,pd_uniform(1),pd_log2dim[1]);
		return t;
		}

	to = t;
	t = 1.f/t;
	ppd[0] *=t; ppd[1] *=t;
	return to;

}

static float pd_norm4(float *ppd)
{
	float t,to;

	t =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3];

	if (t<=0) {
		pd_init(ppd,pd_uniform(2),pd_log2dim[2]);
		return t;
		}

	to = t;
	t = 1.f/t;
	ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t;
	return to;
}

static float pd_norm8(float *ppd)
{
	float t,to;

	t  =ppd[0]; t +=ppd[1]; t +=ppd[2]; t +=ppd[3];
	t +=ppd[4]; t +=ppd[5]; t +=ppd[6]; t +=ppd[7];

	if (t<=0) {
		pd_init(ppd,pd_uniform(3),pd_log2dim[3]);
		return t;
		}

	to = t;
	t = 1.f/t;
	ppd[0] *=t; ppd[1] *=t; ppd[2] *=t; ppd[3] *=t;
	ppd[4] *=t; ppd[5] *=t; ppd[6] *=t; ppd[7] *=t;
	return to;
}
static float pd_norm16(float *ppd)
{
	float t,to;

	t  =ppd[0];  t +=ppd[1];  t +=ppd[2];  t +=ppd[3];
	t +=ppd[4];  t +=ppd[5];  t +=ppd[6];  t +=ppd[7];
	t +=ppd[8];  t +=ppd[9];  t +=ppd[10]; t +=ppd[11];
	t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15];

	if (t<=0) {
		pd_init(ppd,pd_uniform(4),pd_log2dim[4]);
		return t;
		}

	to = t;
	t = 1.f/t;
	ppd[0]  *=t; ppd[1]  *=t; ppd[2]  *=t; ppd[3]  *=t;
	ppd[4]  *=t; ppd[5]  *=t; ppd[6]  *=t; ppd[7]  *=t;
	ppd[8]  *=t; ppd[9]  *=t; ppd[10] *=t; ppd[11] *=t;
	ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t;

	return to;
}
static float pd_norm32(float *ppd)
{
	float t,to;

	t  =ppd[0];  t +=ppd[1];  t +=ppd[2];  t +=ppd[3];
	t +=ppd[4];  t +=ppd[5];  t +=ppd[6];  t +=ppd[7];
	t +=ppd[8];  t +=ppd[9];  t +=ppd[10]; t +=ppd[11];
	t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15];
	t +=ppd[16]; t +=ppd[17]; t +=ppd[18]; t +=ppd[19];
	t +=ppd[20]; t +=ppd[21]; t +=ppd[22]; t +=ppd[23];
	t +=ppd[24]; t +=ppd[25]; t +=ppd[26]; t +=ppd[27];
	t +=ppd[28]; t +=ppd[29]; t +=ppd[30]; t +=ppd[31];

	if (t<=0) {
		pd_init(ppd,pd_uniform(5),pd_log2dim[5]);
		return t;
		}

	to = t;
	t = 1.f/t;
	ppd[0]  *=t; ppd[1]  *=t; ppd[2]  *=t; ppd[3]  *=t;
	ppd[4]  *=t; ppd[5]  *=t; ppd[6]  *=t; ppd[7]  *=t;
	ppd[8]  *=t; ppd[9]  *=t; ppd[10] *=t; ppd[11] *=t;
	ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t;
	ppd[16] *=t; ppd[17] *=t; ppd[18] *=t; ppd[19] *=t;
	ppd[20] *=t; ppd[21] *=t; ppd[22] *=t; ppd[23] *=t;
	ppd[24] *=t; ppd[25] *=t; ppd[26] *=t; ppd[27] *=t;
	ppd[28] *=t; ppd[29] *=t; ppd[30] *=t; ppd[31] *=t;

	return to;
}

static float pd_norm64(float *ppd)
{
	float t,to;

	t  =ppd[0];  t +=ppd[1];  t +=ppd[2];  t +=ppd[3];
	t +=ppd[4];  t +=ppd[5];  t +=ppd[6];  t +=ppd[7];
	t +=ppd[8];  t +=ppd[9];  t +=ppd[10]; t +=ppd[11];
	t +=ppd[12]; t +=ppd[13]; t +=ppd[14]; t +=ppd[15];
	t +=ppd[16]; t +=ppd[17]; t +=ppd[18]; t +=ppd[19];
	t +=ppd[20]; t +=ppd[21]; t +=ppd[22]; t +=ppd[23];
	t +=ppd[24]; t +=ppd[25]; t +=ppd[26]; t +=ppd[27];
	t +=ppd[28]; t +=ppd[29]; t +=ppd[30]; t +=ppd[31];

	t +=ppd[32]; t +=ppd[33]; t +=ppd[34]; t +=ppd[35];
	t +=ppd[36]; t +=ppd[37]; t +=ppd[38]; t +=ppd[39];
	t +=ppd[40]; t +=ppd[41]; t +=ppd[42]; t +=ppd[43];
	t +=ppd[44]; t +=ppd[45]; t +=ppd[46]; t +=ppd[47];
	t +=ppd[48]; t +=ppd[49]; t +=ppd[50]; t +=ppd[51];
	t +=ppd[52]; t +=ppd[53]; t +=ppd[54]; t +=ppd[55];
	t +=ppd[56]; t +=ppd[57]; t +=ppd[58]; t +=ppd[59];
	t +=ppd[60]; t +=ppd[61]; t +=ppd[62]; t +=ppd[63];

	if (t<=0) {
		pd_init(ppd,pd_uniform(6),pd_log2dim[6]);
		return t;
		}

	to = t;
	t = 1.0f/t;
	ppd[0]  *=t; ppd[1]  *=t; ppd[2]  *=t; ppd[3]  *=t;
	ppd[4]  *=t; ppd[5]  *=t; ppd[6]  *=t; ppd[7]  *=t;
	ppd[8]  *=t; ppd[9]  *=t; ppd[10] *=t; ppd[11] *=t;
	ppd[12] *=t; ppd[13] *=t; ppd[14] *=t; ppd[15] *=t;
	ppd[16] *=t; ppd[17] *=t; ppd[18] *=t; ppd[19] *=t;
	ppd[20] *=t; ppd[21] *=t; ppd[22] *=t; ppd[23] *=t;
	ppd[24] *=t; ppd[25] *=t; ppd[26] *=t; ppd[27] *=t;
	ppd[28] *=t; ppd[29] *=t; ppd[30] *=t; ppd[31] *=t;

	ppd[32] *=t; ppd[33] *=t; ppd[34] *=t; ppd[35] *=t;
	ppd[36] *=t; ppd[37] *=t; ppd[38] *=t; ppd[39] *=t;
	ppd[40] *=t; ppd[41] *=t; ppd[42] *=t; ppd[43] *=t;
	ppd[44] *=t; ppd[45] *=t; ppd[46] *=t; ppd[47] *=t;
	ppd[48] *=t; ppd[49] *=t; ppd[50] *=t; ppd[51] *=t;
	ppd[52] *=t; ppd[53] *=t; ppd[54] *=t; ppd[55] *=t;
	ppd[56] *=t; ppd[57] *=t; ppd[58] *=t; ppd[59] *=t;
	ppd[60] *=t; ppd[61] *=t; ppd[62] *=t; ppd[63] *=t;

	return to;
}

static const ppd_norm pd_norm_tab[7] = {
	pd_norm1,
	pd_norm2,
	pd_norm4,
	pd_norm8,
	pd_norm16,
	pd_norm32,
	pd_norm64
};

float pd_norm(float *pd, int nlogdim)
{
	return pd_norm_tab[nlogdim](pd);
}

void pd_memset(float *dst, const float *src, int ndim, int nitems)
{
	int size = PD_SIZE(ndim);
	while(nitems--) {
		memcpy(dst,src,size);
		dst +=ndim;
		}
}

void  pd_fwdperm(float *dst, float *src, const  int *perm, int ndim)
{
	// TODO: non-loop implementation
	while (ndim--)
		dst[ndim] = src[perm[ndim]];
}

void  pd_bwdperm(float *dst, float *src, const  int *perm, int ndim)
{
	// TODO: non-loop implementation
	while (ndim--)
		dst[perm[ndim]] = src[ndim];
}

float pd_max(float *src, int ndim)
{
	// TODO: faster implementation

	float cmax=0;  // we assume that prob distributions are always positive
	float cval;

	while (ndim--) {
		cval = src[ndim];
		if (cval>=cmax) {
			cmax = cval;
			}
		}

	return cmax;
}

int pd_argmax(float *pmax, float *src, int ndim)
{
	// TODO: faster implementation

	float cmax=0;  // we assume that prob distributions are always positive
	float cval;
	int idxmax=-1; // indicates that all pd elements are <0

	while (ndim--) {
		cval = src[ndim];
		if (cval>=cmax) {
			cmax = cval;
			idxmax = ndim;
			}
		}

	if (pmax)
		*pmax = cmax;

	return idxmax;
}


code:
// pdmath.h
// Elementary math on probability distributions
//
// (c) 2016 - Nico Palermo, IV3NWV - Microtelecom Srl, Italy
// ------------------------------------------------------------------------------
// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (repeat and accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#ifndef _pdmath_h_
#define _pdmath_h_

#include <memory.h>

#ifdef __cplusplus
extern "C" {
#endif

#define PD_NDIM(nlogdim)              ((1<<(nlogdim))
#define PD_SIZE(ndim)                 ((ndim)*sizeof(float))
#define PD_ROWADDR(fp,ndim,idx)       (fp+((ndim)*(idx)))

const float *pd_uniform(int nlogdim);
// Returns a pointer to a (constant) uniform distribution of the given log2 size

#define      pd_init(dst,src,ndim) memcpy(dst,src,PD_SIZE(ndim))
// Distribution copy

void         pd_memset(float *dst, const float *src, int ndim, int nitems);
// Copy the distribution pointed by src to the array of distributions dst
// src is a pointer to the input distribution (a vector of size ndim)
// dst is a pointer to a linear array of distributions (a vector of size ndim*nitems)

void         pd_imul(float *dst, const float *src, int nlogdim);
// In place multiplication
// Compute dst = dst*src for any element of the distrib give their log2 size
// src and dst arguments must be pointers to array of floats of the given size

float        pd_norm(float *pd, int nlogdim);
// In place normalizazion
// Normalizes the input vector so that the sum of its components are one
// pd must be a pointer to an array of floats of the given size.
// If the norm of the input vector is non-positive the vector components
// are replaced with a uniform distribution
// Returns the norm of the distribution prior to the normalization

void         pd_fwdperm(float *dst, float *src, const  int *perm, int ndim);
// Forward permutation of a distribution
// Computes dst[k] = src[perm[k]] for every element in the distribution
// perm must be a pointer to an array of integers of length ndim

void         pd_bwdperm(float *dst, float *src, const  int *perm, int ndim);
// Backward permutation of a distribution
// Computes dst[perm[k]] = src[k] for every element in the distribution
// perm must be a pointer to an array of integers of length ndim

float        pd_max(float *src, int ndim);
// Return the maximum of the elements of the given distribution
// Assumes that the input vector is a probability distribution and that each element in the
// distribution is non negative

int          pd_argmax(float *pmax, float *src, int ndim);
// Return the index of the maximum element of the given distribution
// The maximum is stored in the variable pointed by pmax if pmax is not null
// Same note of pd_max applies.
// Return -1 if all the elements in the distribution are negative

#ifdef __cplusplus
}
#endif

#endif // _pdmath_h_


code:
// qra12_63_64_irr_b.c
// Encoding/Decoding tables for Q-ary RA code (12,63) over GF(64)
// Code Name: qra12_63_64_irr_b
// (12,63) RA Code over GF(64) - RF=333344455567

// (c) 2016 - Nico Palermo - IV3NWV - Microtelecom Srl, Italy

// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#include "qra12_63_64_irr_b.h"

#define qra_K       12 // number of information symbols
#define qra_N       63 // codeword length in symbols
#define qra_m        6 // bits/symbol
#define qra_M       64 // Symbol alphabet cardinality
#define qra_a		 1 // grouping factor
#define qra_NC      51 // number of check symbols (N-K)

// Defines used by the message passing decoder --------

#define qra_V       63 // number of variables in the code graph (N)
#define qra_C      115 // number of factors in the code graph (N +(N-K)+1)
#define qra_NMSG   217 // number of msgs in the code graph
#define qra_MAXVDEG    8 // maximum variable degree
#define qra_MAXCDEG    3 // maximum factor degree
#define qra_R     0.19048f // code rate (K/N)
#define CODE_NAME "qra_12_63_64_irr_b"

// table of the systematic symbols indexes in the accumulator chain
static const int qra_acc_input_idx[qra_NC+1] = {
  3,  11,   0,   1,   7,   8,   6,   5,  10,   4,
 11,   9,   0,   2,   6,   7,   8,   4,  11,   5,
 10,   2,   1,   9,   3,   8,   4,  11,   5,   7,
 10,   9,   6,   3,  11,   5,   8,  10,   0,   7,
  9,  11,   4,   2,  10,   6,   8,   1,   9,   7,
 11,  10
};

// table of the systematic symbols weight logarithms over GF(M)
static const  int qra_acc_input_wlog[qra_NC+1] = {
 39,   0,  34,  16,  25,   0,  34,  48,  19,  13,
 29,  56,   0,   5,  39,  42,  31,   0,  10,   0,
 57,  62,  33,  43,   0,  14,  22,  48,  28,  20,
  5,  45,  16,  43,  17,   4,  32,   0,  31,   0,
  0,  28,  57,   0,  18,   0,  60,   0,  10,  31,
 57,  27
};

// table of the logarithms of the elements of GF(M) (log(0) never used)
static const int qra_log[qra_M] = {
 -1,   0,   1,   6,   2,  12,   7,  26,   3,  32,
 13,  35,   8,  48,  27,  18,   4,  24,  33,  16,
 14,  52,  36,  54,   9,  45,  49,  38,  28,  41,
 19,  56,   5,  62,  25,  11,  34,  31,  17,  47,
 15,  23,  53,  51,  37,  44,  55,  40,  10,  61,
 46,  30,  50,  22,  39,  43,  29,  60,  42,  21,
 20,  59,  57,  58
};

// table of GF(M) elements given their logarithm
static const  int qra_exp[qra_M-1] = {
  1,   2,   4,   8,  16,  32,   3,   6,  12,  24,
 48,  35,   5,  10,  20,  40,  19,  38,  15,  30,
 60,  59,  53,  41,  17,  34,   7,  14,  28,  56,
 51,  37,   9,  18,  36,  11,  22,  44,  27,  54,
 47,  29,  58,  55,  45,  25,  50,  39,  13,  26,
 52,  43,  21,  42,  23,  46,  31,  62,  63,  61,
 57,  49,  33
};

// table of the messages weight logarithms over GF(M)
static const  int qra_msgw[qra_NMSG] = {
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,  39,   0,  34,  16,  25,   0,  34,
 48,  19,  13,  29,  56,   0,   5,  39,  42,  31,
  0,  10,   0,  57,  62,  33,  43,   0,  14,  22,
 48,  28,  20,   5,  45,  16,  43,  17,   4,  32,
  0,  31,   0,   0,  28,  57,   0,  18,   0,  60,
  0,  10,  31,  57,  27,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
  0,   0,   0,   0,   0,   0,   0
};

// table of the degrees of the variable nodes
static const  int qra_vdeg[qra_V] = {
  4,   4,   4,   4,   5,   5,   5,   6,   6,   6,
  7,   8,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3
};

// table of the degrees of the factor nodes
static const  int qra_cdeg[qra_C] = {
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   1,   1,   1,   1,   1,   1,   1,
  1,   1,   1,   2,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
  3,   3,   3,   3,   2
};

// table (uncompressed) of the v->c message indexes (-1=unused entry)
static const int qra_v2cmidx[qra_V*qra_MAXVDEG] = {
  0,  65,  75, 101,  -1,  -1,  -1,  -1,
  1,  66,  85, 110,  -1,  -1,  -1,  -1,
  2,  76,  84, 106,  -1,  -1,  -1,  -1,
  3,  63,  87,  96,  -1,  -1,  -1,  -1,
  4,  72,  80,  89, 105,  -1,  -1,  -1,
  5,  70,  82,  91,  98,  -1,  -1,  -1,
  6,  69,  77,  95, 108,  -1,  -1,  -1,
  7,  67,  78,  92, 102, 112,  -1,  -1,
  8,  68,  79,  88,  99, 109,  -1,  -1,
  9,  74,  86,  94, 103, 111,  -1,  -1,
 10,  71,  83,  93, 100, 107, 114,  -1,
 11,  64,  73,  81,  90,  97, 104, 113,
 12, 115, 116,  -1,  -1,  -1,  -1,  -1,
 13, 117, 118,  -1,  -1,  -1,  -1,  -1,
 14, 119, 120,  -1,  -1,  -1,  -1,  -1,
 15, 121, 122,  -1,  -1,  -1,  -1,  -1,
 16, 123, 124,  -1,  -1,  -1,  -1,  -1,
 17, 125, 126,  -1,  -1,  -1,  -1,  -1,
 18, 127, 128,  -1,  -1,  -1,  -1,  -1,
 19, 129, 130,  -1,  -1,  -1,  -1,  -1,
 20, 131, 132,  -1,  -1,  -1,  -1,  -1,
 21, 133, 134,  -1,  -1,  -1,  -1,  -1,
 22, 135, 136,  -1,  -1,  -1,  -1,  -1,
 23, 137, 138,  -1,  -1,  -1,  -1,  -1,
 24, 139, 140,  -1,  -1,  -1,  -1,  -1,
 25, 141, 142,  -1,  -1,  -1,  -1,  -1,
 26, 143, 144,  -1,  -1,  -1,  -1,  -1,
 27, 145, 146,  -1,  -1,  -1,  -1,  -1,
 28, 147, 148,  -1,  -1,  -1,  -1,  -1,
 29, 149, 150,  -1,  -1,  -1,  -1,  -1,
 30, 151, 152,  -1,  -1,  -1,  -1,  -1,
 31, 153, 154,  -1,  -1,  -1,  -1,  -1,
 32, 155, 156,  -1,  -1,  -1,  -1,  -1,
 33, 157, 158,  -1,  -1,  -1,  -1,  -1,
 34, 159, 160,  -1,  -1,  -1,  -1,  -1,
 35, 161, 162,  -1,  -1,  -1,  -1,  -1,
 36, 163, 164,  -1,  -1,  -1,  -1,  -1,
 37, 165, 166,  -1,  -1,  -1,  -1,  -1,
 38, 167, 168,  -1,  -1,  -1,  -1,  -1,
 39, 169, 170,  -1,  -1,  -1,  -1,  -1,
 40, 171, 172,  -1,  -1,  -1,  -1,  -1,
 41, 173, 174,  -1,  -1,  -1,  -1,  -1,
 42, 175, 176,  -1,  -1,  -1,  -1,  -1,
 43, 177, 178,  -1,  -1,  -1,  -1,  -1,
 44, 179, 180,  -1,  -1,  -1,  -1,  -1,
 45, 181, 182,  -1,  -1,  -1,  -1,  -1,
 46, 183, 184,  -1,  -1,  -1,  -1,  -1,
 47, 185, 186,  -1,  -1,  -1,  -1,  -1,
 48, 187, 188,  -1,  -1,  -1,  -1,  -1,
 49, 189, 190,  -1,  -1,  -1,  -1,  -1,
 50, 191, 192,  -1,  -1,  -1,  -1,  -1,
 51, 193, 194,  -1,  -1,  -1,  -1,  -1,
 52, 195, 196,  -1,  -1,  -1,  -1,  -1,
 53, 197, 198,  -1,  -1,  -1,  -1,  -1,
 54, 199, 200,  -1,  -1,  -1,  -1,  -1,
 55, 201, 202,  -1,  -1,  -1,  -1,  -1,
 56, 203, 204,  -1,  -1,  -1,  -1,  -1,
 57, 205, 206,  -1,  -1,  -1,  -1,  -1,
 58, 207, 208,  -1,  -1,  -1,  -1,  -1,
 59, 209, 210,  -1,  -1,  -1,  -1,  -1,
 60, 211, 212,  -1,  -1,  -1,  -1,  -1,
 61, 213, 214,  -1,  -1,  -1,  -1,  -1,
 62, 215, 216,  -1,  -1,  -1,  -1,  -1
};

// table (uncompressed) of the c->v message indexes (-1=unused entry)
static const int qra_c2vmidx[qra_C*qra_MAXCDEG] = {
  0,  -1,  -1,   1,  -1,  -1,   2,  -1,  -1,   3,  -1,  -1,
  4,  -1,  -1,   5,  -1,  -1,   6,  -1,  -1,   7,  -1,  -1,
  8,  -1,  -1,   9,  -1,  -1,  10,  -1,  -1,  11,  -1,  -1,
 12,  -1,  -1,  13,  -1,  -1,  14,  -1,  -1,  15,  -1,  -1,
 16,  -1,  -1,  17,  -1,  -1,  18,  -1,  -1,  19,  -1,  -1,
 20,  -1,  -1,  21,  -1,  -1,  22,  -1,  -1,  23,  -1,  -1,
 24,  -1,  -1,  25,  -1,  -1,  26,  -1,  -1,  27,  -1,  -1,
 28,  -1,  -1,  29,  -1,  -1,  30,  -1,  -1,  31,  -1,  -1,
 32,  -1,  -1,  33,  -1,  -1,  34,  -1,  -1,  35,  -1,  -1,
 36,  -1,  -1,  37,  -1,  -1,  38,  -1,  -1,  39,  -1,  -1,
 40,  -1,  -1,  41,  -1,  -1,  42,  -1,  -1,  43,  -1,  -1,
 44,  -1,  -1,  45,  -1,  -1,  46,  -1,  -1,  47,  -1,  -1,
 48,  -1,  -1,  49,  -1,  -1,  50,  -1,  -1,  51,  -1,  -1,
 52,  -1,  -1,  53,  -1,  -1,  54,  -1,  -1,  55,  -1,  -1,
 56,  -1,  -1,  57,  -1,  -1,  58,  -1,  -1,  59,  -1,  -1,
 60,  -1,  -1,  61,  -1,  -1,  62,  -1,  -1,  63, 115,  -1,
 64, 116, 117,  65, 118, 119,  66, 120, 121,  67, 122, 123,
 68, 124, 125,  69, 126, 127,  70, 128, 129,  71, 130, 131,
 72, 132, 133,  73, 134, 135,  74, 136, 137,  75, 138, 139,
 76, 140, 141,  77, 142, 143,  78, 144, 145,  79, 146, 147,
 80, 148, 149,  81, 150, 151,  82, 152, 153,  83, 154, 155,
 84, 156, 157,  85, 158, 159,  86, 160, 161,  87, 162, 163,
 88, 164, 165,  89, 166, 167,  90, 168, 169,  91, 170, 171,
 92, 172, 173,  93, 174, 175,  94, 176, 177,  95, 178, 179,
 96, 180, 181,  97, 182, 183,  98, 184, 185,  99, 186, 187,
100, 188, 189, 101, 190, 191, 102, 192, 193, 103, 194, 195,
104, 196, 197, 105, 198, 199, 106, 200, 201, 107, 202, 203,
108, 204, 205, 109, 206, 207, 110, 208, 209, 111, 210, 211,
112, 212, 213, 113, 214, 215, 114, 216,  -1
};

// permutation matrix to compute Prob(x*alfa^logw)
static const  int qra_pmat[qra_M*qra_M] = {
  0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15,
 16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,
 32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,
 48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  63,
  0,  33,   1,  32,   2,  35,   3,  34,   4,  37,   5,  36,   6,  39,   7,  38,
  8,  41,   9,  40,  10,  43,  11,  42,  12,  45,  13,  44,  14,  47,  15,  46,
 16,  49,  17,  48,  18,  51,  19,  50,  20,  53,  21,  52,  22,  55,  23,  54,
 24,  57,  25,  56,  26,  59,  27,  58,  28,  61,  29,  60,  30,  63,  31,  62,
  0,  49,  33,  16,   1,  48,  32,  17,   2,  51,  35,  18,   3,  50,  34,  19,
  4,  53,  37,  20,   5,  52,  36,  21,   6,  55,  39,  22,   7,  54,  38,  23,
  8,  57,  41,  24,   9,  56,  40,  25,  10,  59,  43,  26,  11,  58,  42,  27,
 12,  61,  45,  28,  13,  60,  44,  29,  14,  63,  47,  30,  15,  62,  46,  31,
  0,  57,  49,   8,  33,  24,  16,  41,   1,  56,  48,   9,  32,  25,  17,  40,
  2,  59,  51,  10,  35,  26,  18,  43,   3,  58,  50,  11,  34,  27,  19,  42,
  4,  61,  53,  12,  37,  28,  20,  45,   5,  60,  52,  13,  36,  29,  21,  44,
  6,  63,  55,  14,  39,  30,  22,  47,   7,  62,  54,  15,  38,  31,  23,  46,
  0,  61,  57,   4,  49,  12,   8,  53,  33,  28,  24,  37,  16,  45,  41,  20,
  1,  60,  56,   5,  48,  13,   9,  52,  32,  29,  25,  36,  17,  44,  40,  21,
  2,  63,  59,   6,  51,  14,  10,  55,  35,  30,  26,  39,  18,  47,  43,  22,
  3,  62,  58,   7,  50,  15,  11,  54,  34,  31,  27,  38,  19,  46,  42,  23,
  0,  63,  61,   2,  57,   6,   4,  59,  49,  14,  12,  51,   8,  55,  53,  10,
 33,  30,  28,  35,  24,  39,  37,  26,  16,  47,  45,  18,  41,  22,  20,  43,
  1,  62,  60,   3,  56,   7,   5,  58,  48,  15,  13,  50,   9,  54,  52,  11,
 32,  31,  29,  34,  25,  38,  36,  27,  17,  46,  44,  19,  40,  23,  21,  42,
  0,  62,  63,   1,  61,   3,   2,  60,  57,   7,   6,  56,   4,  58,  59,   5,
 49,  15,  14,  48,  12,  50,  51,  13,   8,  54,  55,   9,  53,  11,  10,  52,
 33,  31,  30,  32,  28,  34,  35,  29,  24,  38,  39,  25,  37,  27,  26,  36,
 16,  46,  47,  17,  45,  19,  18,  44,  41,  23,  22,  40,  20,  42,  43,  21,
  0,  31,  62,  33,  63,  32,   1,  30,  61,  34,   3,  28,   2,  29,  60,  35,
 57,  38,   7,  24,   6,  25,  56,  39,   4,  27,  58,  37,  59,  36,   5,  26,
 49,  46,  15,  16,  14,  17,  48,  47,  12,  19,  50,  45,  51,  44,  13,  18,
  8,  23,  54,  41,  55,  40,   9,  22,  53,  42,  11,  20,  10,  21,  52,  43,
  0,  46,  31,  49,  62,  16,  33,  15,  63,  17,  32,  14,   1,  47,  30,  48,
 61,  19,  34,  12,   3,  45,  28,  50,   2,  44,  29,  51,  60,  18,  35,  13,
 57,  23,  38,   8,   7,  41,  24,  54,   6,  40,  25,  55,  56,  22,  39,   9,
  4,  42,  27,  53,  58,  20,  37,  11,  59,  21,  36,  10,   5,  43,  26,  52,
  0,  23,  46,  57,  31,   8,  49,  38,  62,  41,  16,   7,  33,  54,  15,  24,
 63,  40,  17,   6,  32,  55,  14,  25,   1,  22,  47,  56,  30,   9,  48,  39,
 61,  42,  19,   4,  34,  53,  12,  27,   3,  20,  45,  58,  28,  11,  50,  37,
  2,  21,  44,  59,  29,  10,  51,  36,  60,  43,  18,   5,  35,  52,  13,  26,
  0,  42,  23,  61,  46,   4,  57,  19,  31,  53,   8,  34,  49,  27,  38,  12,
 62,  20,  41,   3,  16,  58,   7,  45,  33,  11,  54,  28,  15,  37,  24,  50,
 63,  21,  40,   2,  17,  59,   6,  44,  32,  10,  55,  29,  14,  36,  25,  51,
  1,  43,  22,  60,  47,   5,  56,  18,  30,  52,   9,  35,  48,  26,  39,  13,
  0,  21,  42,  63,  23,   2,  61,  40,  46,  59,   4,  17,  57,  44,  19,   6,
 31,  10,  53,  32,   8,  29,  34,  55,  49,  36,  27,  14,  38,  51,  12,  25,
 62,  43,  20,   1,  41,  60,   3,  22,  16,   5,  58,  47,   7,  18,  45,  56,
 33,  52,  11,  30,  54,  35,  28,   9,  15,  26,  37,  48,  24,  13,  50,  39,
  0,  43,  21,  62,  42,   1,  63,  20,  23,  60,   2,  41,  61,  22,  40,   3,
 46,   5,  59,  16,   4,  47,  17,  58,  57,  18,  44,   7,  19,  56,   6,  45,
 31,  52,  10,  33,  53,  30,  32,  11,   8,  35,  29,  54,  34,   9,  55,  28,
 49,  26,  36,  15,  27,  48,  14,  37,  38,  13,  51,  24,  12,  39,  25,  50,
  0,  52,  43,  31,  21,  33,  62,  10,  42,  30,   1,  53,  63,  11,  20,  32,
 23,  35,  60,   8,   2,  54,  41,  29,  61,   9,  22,  34,  40,  28,   3,  55,
 46,  26,   5,  49,  59,  15,  16,  36,   4,  48,  47,  27,  17,  37,  58,  14,
 57,  13,  18,  38,  44,  24,   7,  51,  19,  39,  56,  12,   6,  50,  45,  25,
  0,  26,  52,  46,  43,  49,  31,   5,  21,  15,  33,  59,  62,  36,  10,  16,
 42,  48,  30,   4,   1,  27,  53,  47,  63,  37,  11,  17,  20,  14,  32,  58,
 23,  13,  35,  57,  60,  38,   8,  18,   2,  24,  54,  44,  41,  51,  29,   7,
 61,  39,   9,  19,  22,  12,  34,  56,  40,  50,  28,   6,   3,  25,  55,  45,
  0,  13,  26,  23,  52,  57,  46,  35,  43,  38,  49,  60,  31,  18,   5,   8,
 21,  24,  15,   2,  33,  44,  59,  54,  62,  51,  36,  41,  10,   7,  16,  29,
 42,  39,  48,  61,  30,  19,   4,   9,   1,  12,  27,  22,  53,  56,  47,  34,
 63,  50,  37,  40,  11,   6,  17,  28,  20,  25,  14,   3,  32,  45,  58,  55,
  0,  39,  13,  42,  26,  61,  23,  48,  52,  19,  57,  30,  46,   9,  35,   4,
 43,  12,  38,   1,  49,  22,  60,  27,  31,  56,  18,  53,   5,  34,   8,  47,
 21,  50,  24,  63,  15,  40,   2,  37,  33,   6,  44,  11,  59,  28,  54,  17,
 62,  25,  51,  20,  36,   3,  41,  14,  10,  45,   7,  32,  16,  55,  29,  58,
  0,  50,  39,  21,  13,  63,  42,  24,  26,  40,  61,  15,  23,  37,  48,   2,
 52,   6,  19,  33,  57,  11,  30,  44,  46,  28,   9,  59,  35,  17,   4,  54,
 43,  25,  12,  62,  38,  20,   1,  51,  49,   3,  22,  36,  60,  14,  27,  41,
 31,  45,  56,  10,  18,  32,  53,   7,   5,  55,  34,  16,   8,  58,  47,  29,
  0,  25,  50,  43,  39,  62,  21,  12,  13,  20,  63,  38,  42,  51,  24,   1,
 26,   3,  40,  49,  61,  36,  15,  22,  23,  14,  37,  60,  48,  41,   2,  27,
 52,  45,   6,  31,  19,  10,  33,  56,  57,  32,  11,  18,  30,   7,  44,  53,
 46,  55,  28,   5,   9,  16,  59,  34,  35,  58,  17,   8,   4,  29,  54,  47,
  0,  45,  25,  52,  50,  31,  43,   6,  39,  10,  62,  19,  21,  56,  12,  33,
 13,  32,  20,  57,  63,  18,  38,  11,  42,   7,  51,  30,  24,  53,   1,  44,
 26,  55,   3,  46,  40,   5,  49,  28,  61,  16,  36,   9,  15,  34,  22,  59,
 23,  58,  14,  35,  37,   8,  60,  17,  48,  29,  41,   4,   2,  47,  27,  54,
  0,  55,  45,  26,  25,  46,  52,   3,  50,   5,  31,  40,  43,  28,   6,  49,
 39,  16,  10,  61,  62,   9,  19,  36,  21,  34,  56,  15,  12,  59,  33,  22,
 13,  58,  32,  23,  20,  35,  57,  14,  63,   8,  18,  37,  38,  17,  11,  60,
 42,  29,   7,  48,  51,   4,  30,  41,  24,  47,  53,   2,   1,  54,  44,  27,
  0,  58,  55,  13,  45,  23,  26,  32,  25,  35,  46,  20,  52,  14,   3,  57,
 50,   8,   5,  63,  31,  37,  40,  18,  43,  17,  28,  38,   6,  60,  49,  11,
 39,  29,  16,  42,  10,  48,  61,   7,  62,   4,   9,  51,  19,  41,  36,  30,
 21,  47,  34,  24,  56,   2,  15,  53,  12,  54,  59,   1,  33,  27,  22,  44,
  0,  29,  58,  39,  55,  42,  13,  16,  45,  48,  23,  10,  26,   7,  32,  61,
 25,   4,  35,  62,  46,  51,  20,   9,  52,  41,  14,  19,   3,  30,  57,  36,
 50,  47,   8,  21,   5,  24,  63,  34,  31,   2,  37,  56,  40,  53,  18,  15,
 43,  54,  17,  12,  28,   1,  38,  59,   6,  27,  60,  33,  49,  44,  11,  22,
  0,  47,  29,  50,  58,  21,  39,   8,  55,  24,  42,   5,  13,  34,  16,  63,
 45,   2,  48,  31,  23,  56,  10,  37,  26,  53,   7,  40,  32,  15,  61,  18,
 25,  54,   4,  43,  35,  12,  62,  17,  46,   1,  51,  28,  20,  59,   9,  38,
 52,  27,  41,   6,  14,  33,  19,  60,   3,  44,  30,  49,  57,  22,  36,  11,
  0,  54,  47,  25,  29,  43,  50,   4,  58,  12,  21,  35,  39,  17,   8,  62,
 55,   1,  24,  46,  42,  28,   5,  51,  13,  59,  34,  20,  16,  38,  63,   9,
 45,  27,   2,  52,  48,   6,  31,  41,  23,  33,  56,  14,  10,  60,  37,  19,
 26,  44,  53,   3,   7,  49,  40,  30,  32,  22,  15,  57,  61,  11,  18,  36,
  0,  27,  54,  45,  47,  52,  25,   2,  29,   6,  43,  48,  50,  41,   4,  31,
 58,  33,  12,  23,  21,  14,  35,  56,  39,  60,  17,  10,   8,  19,  62,  37,
 55,  44,   1,  26,  24,   3,  46,  53,  42,  49,  28,   7,   5,  30,  51,  40,
 13,  22,  59,  32,  34,  57,  20,  15,  16,  11,  38,  61,  63,  36,   9,  18,
  0,  44,  27,  55,  54,  26,  45,   1,  47,   3,  52,  24,  25,  53,   2,  46,
 29,  49,   6,  42,  43,   7,  48,  28,  50,  30,  41,   5,   4,  40,  31,  51,
 58,  22,  33,  13,  12,  32,  23,  59,  21,  57,  14,  34,  35,  15,  56,  20,
 39,  11,  60,  16,  17,  61,  10,  38,   8,  36,  19,  63,  62,  18,  37,   9,
  0,  22,  44,  58,  27,  13,  55,  33,  54,  32,  26,  12,  45,  59,   1,  23,
 47,  57,   3,  21,  52,  34,  24,  14,  25,  15,  53,  35,   2,  20,  46,  56,
 29,  11,  49,  39,   6,  16,  42,  60,  43,  61,   7,  17,  48,  38,  28,  10,
 50,  36,  30,   8,  41,  63,   5,  19,   4,  18,  40,  62,  31,   9,  51,  37,
  0,  11,  22,  29,  44,  39,  58,  49,  27,  16,  13,   6,  55,  60,  33,  42,
 54,  61,  32,  43,  26,  17,  12,   7,  45,  38,  59,  48,   1,  10,  23,  28,
 47,  36,  57,  50,   3,   8,  21,  30,  52,  63,  34,  41,  24,  19,  14,   5,
 25,  18,  15,   4,  53,  62,  35,  40,   2,   9,  20,  31,  46,  37,  56,  51,
  0,  36,  11,  47,  22,  50,  29,  57,  44,   8,  39,   3,  58,  30,  49,  21,
 27,  63,  16,  52,  13,  41,   6,  34,  55,  19,  60,  24,  33,   5,  42,  14,
 54,  18,  61,  25,  32,   4,  43,  15,  26,  62,  17,  53,  12,  40,   7,  35,
 45,   9,  38,   2,  59,  31,  48,  20,   1,  37,  10,  46,  23,  51,  28,  56,
  0,  18,  36,  54,  11,  25,  47,  61,  22,   4,  50,  32,  29,  15,  57,  43,
 44,  62,   8,  26,  39,  53,   3,  17,  58,  40,  30,  12,  49,  35,  21,   7,
 27,   9,  63,  45,  16,   2,  52,  38,  13,  31,  41,  59,   6,  20,  34,  48,
 55,  37,  19,   1,  60,  46,  24,  10,  33,  51,   5,  23,  42,  56,  14,  28,
  0,   9,  18,  27,  36,  45,  54,  63,  11,   2,  25,  16,  47,  38,  61,  52,
 22,  31,   4,  13,  50,  59,  32,  41,  29,  20,  15,   6,  57,  48,  43,  34,
 44,  37,  62,  55,   8,   1,  26,  19,  39,  46,  53,  60,   3,  10,  17,  24,
 58,  51,  40,  33,  30,  23,  12,   5,  49,  56,  35,  42,  21,  28,   7,  14,
  0,  37,   9,  44,  18,  55,  27,  62,  36,   1,  45,   8,  54,  19,  63,  26,
 11,  46,   2,  39,  25,  60,  16,  53,  47,  10,  38,   3,  61,  24,  52,  17,
 22,  51,  31,  58,   4,  33,  13,  40,  50,  23,  59,  30,  32,   5,  41,  12,
 29,  56,  20,  49,  15,  42,   6,  35,  57,  28,  48,  21,  43,  14,  34,   7,
  0,  51,  37,  22,   9,  58,  44,  31,  18,  33,  55,   4,  27,  40,  62,  13,
 36,  23,   1,  50,  45,  30,   8,  59,  54,   5,  19,  32,  63,  12,  26,  41,
 11,  56,  46,  29,   2,  49,  39,  20,  25,  42,  60,  15,  16,  35,  53,   6,
 47,  28,  10,  57,  38,  21,   3,  48,  61,  14,  24,  43,  52,   7,  17,  34,
  0,  56,  51,  11,  37,  29,  22,  46,   9,  49,  58,   2,  44,  20,  31,  39,
 18,  42,  33,  25,  55,  15,   4,  60,  27,  35,  40,  16,  62,   6,  13,  53,
 36,  28,  23,  47,   1,  57,  50,  10,  45,  21,  30,  38,   8,  48,  59,   3,
 54,  14,   5,  61,  19,  43,  32,  24,  63,   7,  12,  52,  26,  34,  41,  17,
  0,  28,  56,  36,  51,  47,  11,  23,  37,  57,  29,   1,  22,  10,  46,  50,
  9,  21,  49,  45,  58,  38,   2,  30,  44,  48,  20,   8,  31,   3,  39,  59,
 18,  14,  42,  54,  33,  61,  25,   5,  55,  43,  15,  19,   4,  24,  60,  32,
 27,   7,  35,  63,  40,  52,  16,  12,  62,  34,   6,  26,  13,  17,  53,  41,
  0,  14,  28,  18,  56,  54,  36,  42,  51,  61,  47,  33,  11,   5,  23,  25,
 37,  43,  57,  55,  29,  19,   1,  15,  22,  24,  10,   4,  46,  32,  50,  60,
  9,   7,  21,  27,  49,  63,  45,  35,  58,  52,  38,  40,   2,  12,  30,  16,
 44,  34,  48,  62,  20,  26,   8,   6,  31,  17,   3,  13,  39,  41,  59,  53,
  0,   7,  14,   9,  28,  27,  18,  21,  56,  63,  54,  49,  36,  35,  42,  45,
 51,  52,  61,  58,  47,  40,  33,  38,  11,  12,   5,   2,  23,  16,  25,  30,
 37,  34,  43,  44,  57,  62,  55,  48,  29,  26,  19,  20,   1,   6,  15,   8,
 22,  17,  24,  31,  10,  13,   4,   3,  46,  41,  32,  39,  50,  53,  60,  59,
  0,  34,   7,  37,  14,  44,   9,  43,  28,  62,  27,  57,  18,  48,  21,  55,
 56,  26,  63,  29,  54,  20,  49,  19,  36,   6,  35,   1,  42,   8,  45,  15,
 51,  17,  52,  22,  61,  31,  58,  24,  47,  13,  40,  10,  33,   3,  38,   4,
 11,  41,  12,  46,   5,  39,   2,  32,  23,  53,  16,  50,  25,  59,  30,  60,
  0,  17,  34,  51,   7,  22,  37,  52,  14,  31,  44,  61,   9,  24,  43,  58,
 28,  13,  62,  47,  27,  10,  57,  40,  18,   3,  48,  33,  21,   4,  55,  38,
 56,  41,  26,  11,  63,  46,  29,  12,  54,  39,  20,   5,  49,  32,  19,   2,
 36,  53,   6,  23,  35,  50,   1,  16,  42,  59,   8,  25,  45,  60,  15,  30,
  0,  41,  17,  56,  34,  11,  51,  26,   7,  46,  22,  63,  37,  12,  52,  29,
 14,  39,  31,  54,  44,   5,  61,  20,   9,  32,  24,  49,  43,   2,  58,  19,
 28,  53,  13,  36,  62,  23,  47,   6,  27,  50,  10,  35,  57,  16,  40,   1,
 18,  59,   3,  42,  48,  25,  33,   8,  21,  60,   4,  45,  55,  30,  38,  15,
  0,  53,  41,  28,  17,  36,  56,  13,  34,  23,  11,  62,  51,   6,  26,  47,
  7,  50,  46,  27,  22,  35,  63,  10,  37,  16,  12,  57,  52,   1,  29,  40,
 14,  59,  39,  18,  31,  42,  54,   3,  44,  25,   5,  48,  61,   8,  20,  33,
  9,  60,  32,  21,  24,  45,  49,   4,  43,  30,   2,  55,  58,  15,  19,  38,
  0,  59,  53,  14,  41,  18,  28,  39,  17,  42,  36,  31,  56,   3,  13,  54,
 34,  25,  23,  44,  11,  48,  62,   5,  51,   8,   6,  61,  26,  33,  47,  20,
  7,  60,  50,   9,  46,  21,  27,  32,  22,  45,  35,  24,  63,   4,  10,  49,
 37,  30,  16,  43,  12,  55,  57,   2,  52,  15,   1,  58,  29,  38,  40,  19,
  0,  60,  59,   7,  53,   9,  14,  50,  41,  21,  18,  46,  28,  32,  39,  27,
 17,  45,  42,  22,  36,  24,  31,  35,  56,   4,   3,  63,  13,  49,  54,  10,
 34,  30,  25,  37,  23,  43,  44,  16,  11,  55,  48,  12,  62,   2,   5,  57,
 51,  15,   8,  52,   6,  58,  61,   1,  26,  38,  33,  29,  47,  19,  20,  40,
  0,  30,  60,  34,  59,  37,   7,  25,  53,  43,   9,  23,  14,  16,  50,  44,
 41,  55,  21,  11,  18,  12,  46,  48,  28,   2,  32,  62,  39,  57,  27,   5,
 17,  15,  45,  51,  42,  52,  22,   8,  36,  58,  24,   6,  31,   1,  35,  61,
 56,  38,   4,  26,   3,  29,  63,  33,  13,  19,  49,  47,  54,  40,  10,  20,
  0,  15,  30,  17,  60,  51,  34,  45,  59,  52,  37,  42,   7,   8,  25,  22,
 53,  58,  43,  36,   9,   6,  23,  24,  14,   1,  16,  31,  50,  61,  44,  35,
 41,  38,  55,  56,  21,  26,  11,   4,  18,  29,  12,   3,  46,  33,  48,  63,
 28,  19,   2,  13,  32,  47,  62,  49,  39,  40,  57,  54,  27,  20,   5,  10,
  0,  38,  15,  41,  30,  56,  17,  55,  60,  26,  51,  21,  34,   4,  45,  11,
 59,  29,  52,  18,  37,   3,  42,  12,   7,  33,   8,  46,  25,  63,  22,  48,
 53,  19,  58,  28,  43,  13,  36,   2,   9,  47,   6,  32,  23,  49,  24,  62,
 14,  40,   1,  39,  16,  54,  31,  57,  50,  20,  61,  27,  44,  10,  35,   5,
  0,  19,  38,  53,  15,  28,  41,  58,  30,  13,  56,  43,  17,   2,  55,  36,
 60,  47,  26,   9,  51,  32,  21,   6,  34,  49,   4,  23,  45,  62,  11,  24,
 59,  40,  29,  14,  52,  39,  18,   1,  37,  54,   3,  16,  42,  57,  12,  31,
  7,  20,  33,  50,   8,  27,  46,  61,  25,  10,  63,  44,  22,   5,  48,  35,
  0,  40,  19,  59,  38,  14,  53,  29,  15,  39,  28,  52,  41,   1,  58,  18,
 30,  54,  13,  37,  56,  16,  43,   3,  17,  57,   2,  42,  55,  31,  36,  12,
 60,  20,  47,   7,  26,  50,   9,  33,  51,  27,  32,   8,  21,  61,   6,  46,
 34,  10,  49,  25,   4,  44,  23,  63,  45,   5,  62,  22,  11,  35,  24,  48,
  0,  20,  40,  60,  19,   7,  59,  47,  38,  50,  14,  26,  53,  33,  29,   9,
 15,  27,  39,  51,  28,   8,  52,  32,  41,  61,   1,  21,  58,  46,  18,   6,
 30,  10,  54,  34,  13,  25,  37,  49,  56,  44,  16,   4,  43,  63,   3,  23,
 17,   5,  57,  45,   2,  22,  42,  62,  55,  35,  31,  11,  36,  48,  12,  24,
  0,  10,  20,  30,  40,  34,  60,  54,  19,  25,   7,  13,  59,  49,  47,  37,
 38,  44,  50,  56,  14,   4,  26,  16,  53,  63,  33,  43,  29,  23,   9,   3,
 15,   5,  27,  17,  39,  45,  51,  57,  28,  22,   8,   2,  52,  62,  32,  42,
 41,  35,  61,  55,   1,  11,  21,  31,  58,  48,  46,  36,  18,  24,   6,  12,
  0,   5,  10,  15,  20,  17,  30,  27,  40,  45,  34,  39,  60,  57,  54,  51,
 19,  22,  25,  28,   7,   2,  13,   8,  59,  62,  49,  52,  47,  42,  37,  32,
 38,  35,  44,  41,  50,  55,  56,  61,  14,  11,   4,   1,  26,  31,  16,  21,
 53,  48,  63,  58,  33,  36,  43,  46,  29,  24,  23,  18,   9,  12,   3,   6,
  0,  35,   5,  38,  10,  41,  15,  44,  20,  55,  17,  50,  30,  61,  27,  56,
 40,  11,  45,  14,  34,   1,  39,   4,  60,  31,  57,  26,  54,  21,  51,  16,
 19,  48,  22,  53,  25,  58,  28,  63,   7,  36,   2,  33,  13,  46,   8,  43,
 59,  24,  62,  29,  49,  18,  52,  23,  47,  12,  42,   9,  37,   6,  32,   3,
  0,  48,  35,  19,   5,  53,  38,  22,  10,  58,  41,  25,  15,  63,  44,  28,
 20,  36,  55,   7,  17,  33,  50,   2,  30,  46,  61,  13,  27,  43,  56,   8,
 40,  24,  11,  59,  45,  29,  14,  62,  34,  18,   1,  49,  39,  23,   4,  52,
 60,  12,  31,  47,  57,   9,  26,  42,  54,   6,  21,  37,  51,   3,  16,  32,
  0,  24,  48,  40,  35,  59,  19,  11,   5,  29,  53,  45,  38,  62,  22,  14,
 10,  18,  58,  34,  41,  49,  25,   1,  15,  23,  63,  39,  44,  52,  28,   4,
 20,  12,  36,  60,  55,  47,   7,  31,  17,   9,  33,  57,  50,  42,   2,  26,
 30,   6,  46,  54,  61,  37,  13,  21,  27,   3,  43,  51,  56,  32,   8,  16,
  0,  12,  24,  20,  48,  60,  40,  36,  35,  47,  59,  55,  19,  31,  11,   7,
  5,   9,  29,  17,  53,  57,  45,  33,  38,  42,  62,  50,  22,  26,  14,   2,
 10,   6,  18,  30,  58,  54,  34,  46,  41,  37,  49,  61,  25,  21,   1,  13,
 15,   3,  23,  27,  63,  51,  39,  43,  44,  32,  52,  56,  28,  16,   4,   8,
  0,   6,  12,  10,  24,  30,  20,  18,  48,  54,  60,  58,  40,  46,  36,  34,
 35,  37,  47,  41,  59,  61,  55,  49,  19,  21,  31,  25,  11,  13,   7,   1,
  5,   3,   9,  15,  29,  27,  17,  23,  53,  51,  57,  63,  45,  43,  33,  39,
 38,  32,  42,  44,  62,  56,  50,  52,  22,  16,  26,  28,  14,   8,   2,   4,
  0,   3,   6,   5,  12,  15,  10,   9,  24,  27,  30,  29,  20,  23,  18,  17,
 48,  51,  54,  53,  60,  63,  58,  57,  40,  43,  46,  45,  36,  39,  34,  33,
 35,  32,  37,  38,  47,  44,  41,  42,  59,  56,  61,  62,  55,  52,  49,  50,
 19,  16,  21,  22,  31,  28,  25,  26,  11,   8,  13,  14,   7,   4,   1,   2,
  0,  32,   3,  35,   6,  38,   5,  37,  12,  44,  15,  47,  10,  42,   9,  41,
 24,  56,  27,  59,  30,  62,  29,  61,  20,  52,  23,  55,  18,  50,  17,  49,
 48,  16,  51,  19,  54,  22,  53,  21,  60,  28,  63,  31,  58,  26,  57,  25,
 40,   8,  43,  11,  46,  14,  45,  13,  36,   4,  39,   7,  34,   2,  33,   1,
  0,  16,  32,  48,   3,  19,  35,  51,   6,  22,  38,  54,   5,  21,  37,  53,
 12,  28,  44,  60,  15,  31,  47,  63,  10,  26,  42,  58,   9,  25,  41,  57,
 24,   8,  56,  40,  27,  11,  59,  43,  30,  14,  62,  46,  29,  13,  61,  45,
 20,   4,  52,  36,  23,   7,  55,  39,  18,   2,  50,  34,  17,   1,  49,  33,
  0,   8,  16,  24,  32,  40,  48,  56,   3,  11,  19,  27,  35,  43,  51,  59,
  6,  14,  22,  30,  38,  46,  54,  62,   5,  13,  21,  29,  37,  45,  53,  61,
 12,   4,  28,  20,  44,  36,  60,  52,  15,   7,  31,  23,  47,  39,  63,  55,
 10,   2,  26,  18,  42,  34,  58,  50,   9,   1,  25,  17,  41,  33,  57,  49,
  0,   4,   8,  12,  16,  20,  24,  28,  32,  36,  40,  44,  48,  52,  56,  60,
  3,   7,  11,  15,  19,  23,  27,  31,  35,  39,  43,  47,  51,  55,  59,  63,
  6,   2,  14,  10,  22,  18,  30,  26,  38,  34,  46,  42,  54,  50,  62,  58,
  5,   1,  13,   9,  21,  17,  29,  25,  37,  33,  45,  41,  53,  49,  61,  57,
  0,   2,   4,   6,   8,  10,  12,  14,  16,  18,  20,  22,  24,  26,  28,  30,
 32,  34,  36,  38,  40,  42,  44,  46,  48,  50,  52,  54,  56,  58,  60,  62,
  3,   1,   7,   5,  11,   9,  15,  13,  19,  17,  23,  21,  27,  25,  31,  29,
 35,  33,  39,  37,  43,  41,  47,  45,  51,  49,  55,  53,  59,  57,  63,  61
};

const qracode qra_12_63_64_irr_b = {
		qra_K,
		qra_N,
		qra_m,
		qra_M,
		qra_a,
		qra_NC,
		qra_V,
		qra_C,
		qra_NMSG,
		qra_MAXVDEG,
		qra_MAXCDEG,
		QRATYPE_NORMAL,
		qra_R,
		CODE_NAME,
		qra_acc_input_idx,
		qra_acc_input_wlog,
		qra_log,
		qra_exp,
		qra_msgw,
		qra_vdeg,
		qra_cdeg,
		qra_v2cmidx,
		qra_c2vmidx,
		qra_pmat
};

#undef qra_K
#undef qra_N
#undef qra_m
#undef qra_M
#undef qra_a
#undef qra_NC
#undef qra_V
#undef qra_C
#undef qra_NMSG
#undef qra_MAXVDEG
#undef qra_MAXCDEG
#undef qra_R
#undef CODE_NAME


code:
// qra12_63_64_irr_b.h
// Code tables and defines for Q-ary RA code (12,63) over GF(64)
// Code Name: qra12_63_64_irr_b
// (12,63) RA Code over GF(64) - RF=333344455567

// (c) 2016 - Nico Palermo - IV3NWV - Microtelecom Srl, Italy

// This file is part of the qracodes project, a Forward Error Control
// encoding/decoding package based on Q-ary RA (Repeat and Accumulate) LDPC codes.
//
//    qracodes 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.
//    qracodes 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 qracodes source distribution.
//    If not, see <http://www.gnu.org/licenses/>.

#ifndef _qra12_63_64_irr_b_h
#define _qra12_63_64_irr_b_h

#include "qracodes.h"

#ifdef __cplusplus
extern "C" {
#endif

extern const qracode qra_12_63_64_irr_b;

#ifdef __cplusplus
}
#endif

#endif // _qra12_63_64_irr_b_h