void mod2sparse_add ( mod2sparse *m1, /* Left operand of add */ mod2sparse *m2, /* Right operand of add */ mod2sparse *r /* Place to store result of add */ ) { mod2entry *e1, *e2; int i; if (mod2sparse_rows(m1)!=mod2sparse_rows(r) || mod2sparse_cols(m1)!=mod2sparse_cols(r) || mod2sparse_rows(m2)!=mod2sparse_rows(r) || mod2sparse_cols(m2)!=mod2sparse_cols(r)) { fprintf(stderr,"mod2sparse_add: Matrices have different dimensions\n"); exit(1); } if (r==m1 || r==m2) { fprintf(stderr, "mod2sparse_add: Result matrix is the same as one of the operands\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i<mod2sparse_rows(r); i++) { e1 = mod2sparse_first_in_row(m1,i); e2 = mod2sparse_first_in_row(m2,i); while (!mod2sparse_at_end(e1) && !mod2sparse_at_end(e2)) { if (mod2sparse_col(e1)==mod2sparse_col(e2)) { e1 = mod2sparse_next_in_row(e1); e2 = mod2sparse_next_in_row(e2); } else if (mod2sparse_col(e1)<mod2sparse_col(e2)) { mod2sparse_insert(r,i,mod2sparse_col(e1)); e1 = mod2sparse_next_in_row(e1); } else { mod2sparse_insert(r,i,mod2sparse_col(e2)); e2 = mod2sparse_next_in_row(e2); } } while (!mod2sparse_at_end(e1)) { mod2sparse_insert(r,i,mod2sparse_col(e1)); e1 = mod2sparse_next_in_row(e1); } while (!mod2sparse_at_end(e2)) { mod2sparse_insert(r,i,mod2sparse_col(e2)); e2 = mod2sparse_next_in_row(e2); } } }
void mod2sparse_copyrows ( mod2sparse *m, /* Matrix to copy */ mod2sparse *r, /* Place to store copy of matrix */ int *rows /* Indexes of rows to copy, from 0 */ ) { mod2entry *e; int i; if (mod2sparse_cols(m)>mod2sparse_cols(r)) { fprintf(stderr, "mod2sparse_copyrows: Destination matrix has fewer columns than source\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i<mod2sparse_rows(r); i++) { if (rows[i]<0 || rows[i]>=mod2sparse_rows(m)) { fprintf(stderr,"mod2sparse_copyrows: Row index out of range\n"); exit(1); } e = mod2sparse_first_in_row(m,rows[i]); while (!mod2sparse_at_end(e)) { mod2sparse_insert(r,i,e->col); e = mod2sparse_next_in_row(e); } } }
double expected_parity_errors ( mod2sparse *H, /* Parity check matrix */ double *bpr /* Bit probabilities */ ) { mod2entry *f; double ee, p; int M, i, j; M = mod2sparse_rows(H); ee = 0; for (i = 0; i<M; i++) { p = 0; for (f = mod2sparse_first_in_row(H,i); !mod2sparse_at_end(f); f = mod2sparse_next_in_row(f)) { j = mod2sparse_col(f); p = p * (1-bpr[j]) + (1-p) * bpr[j]; } ee += p; } return ee; }
void mod2sparse_transpose ( mod2sparse *m, /* Matrix to compute transpose of (left unchanged) */ mod2sparse *r /* Result of transpose operation */ ) { mod2entry *e; int i; if (mod2sparse_rows(m)!=mod2sparse_cols(r) || mod2sparse_cols(m)!=mod2sparse_rows(r)) { fprintf(stderr, "mod2sparse_transpose: Matrices have incompatible dimensions\n"); exit(1); } if (r==m) { fprintf(stderr, "mod2sparse_transpose: Result matrix is the same as the operand\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i<mod2sparse_rows(m); i++) { e = mod2sparse_first_in_row(m,i); while (!mod2sparse_at_end(e)) { mod2sparse_insert(r,mod2sparse_col(e),i); e = mod2sparse_next_in_row(e); } } }
/****************************************************************************** * BuildParitySymbol: Builds a new parity symbol. * => See header file for more informations. */ ldpc_error_status BuildParitySymbol ( LDPCFecSession *Session, void* symbol_canvas[], int paritySymbol_index, void* paritySymbol) { uintptr_t *fec_buf; // buffer for this parity symbol uintptr_t *to_add_buf; // buffer for the source.parity symbol to add mod2entry *e; int seqno, k=0; static int n=0; fec_buf = (uintptr_t*)GetBufferPtrOnly(paritySymbol); e = mod2sparse_first_in_row(Session->m_pchkMatrix, paritySymbol_index); while (!mod2sparse_at_end(e)) { // paritySymbol_index in {0.. n-k-1} range, so this test is ok if (e->col != paritySymbol_index) { // don't add paritySymbol to itself seqno = GetSymbolSeqno(Session, e->col); //fprintf(stderr, "[%s:%d] total:%d now:%d seqno:%d, paritySymbol_index:%d, col:%d\n", __FILE__, __LINE__, n++, k++, seqno, paritySymbol_index, e->col); to_add_buf = (uintptr_t*) GetBuffer(symbol_canvas[seqno]); if (to_add_buf == NULL) { return LDPC_ERROR; } AddToSymbol(fec_buf, to_add_buf); } e = mod2sparse_next_in_row(e); } return LDPC_OK; }
void mod2sparse_copy ( mod2sparse *m, /* Matrix to copy */ mod2sparse *r /* Place to store copy of matrix */ ) { mod2entry *e, *f; int i; if (mod2sparse_rows(m)>mod2sparse_rows(r) || mod2sparse_cols(m)>mod2sparse_cols(r)) { fprintf(stderr,"mod2sparse_copy: Destination matrix is too small\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i<mod2sparse_rows(m); i++) { e = mod2sparse_first_in_row(m,i); while (!mod2sparse_at_end(e)) { f = mod2sparse_insert(r,e->row,e->col); #if 0 f->lr = e->lr; f->pr = e->pr; #endif e = mod2sparse_next_in_row(e); } } }
mod2entry *mod2sparse_find ( mod2sparse *m, int row, int col ) { mod2entry *re, *ce; if (row<0 || row>=mod2sparse_rows(m) || col<0 || col>=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_find: row or column index out of bounds\n"); exit(1); } /* Check last entries in row. */ re = mod2sparse_last_in_row(m,row); if (mod2sparse_at_end(re) || mod2sparse_col(re)<col) { return 0; } if (mod2sparse_col(re)==col) { return re; } #ifndef SPARSE_MATRIX_OPT_FOR_LDPC_STAIRCASE ce = mod2sparse_last_in_col(m,col); if (mod2sparse_at_end(ce) || mod2sparse_row(ce)<row) { return 0; } if (mod2sparse_row(ce)==row) { return ce; } #endif /* Search row and column in parallel, from the front. */ re = mod2sparse_first_in_row(m,row); ce = mod2sparse_first_in_col(m,col); for (;;) { if (mod2sparse_at_end(re) || mod2sparse_col(re)>col) { return 0; } if (mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)>row) { return 0; } if (mod2sparse_row(ce)==row) { return ce; } re = mod2sparse_next_in_row(re); ce = mod2sparse_next_in_col(ce); } }
void mod2sparse_add_row ( mod2sparse *m1, /* Matrix containing row to add to */ int row1, /* Index in this matrix of row to add to */ mod2sparse *m2, /* Matrix containing row to add from */ int row2 /* Index in this matrix of row to add from */ ) { mod2entry *f1, *f2, *ft; if (mod2sparse_cols(m1)<mod2sparse_cols(m2)) { fprintf (stderr, "mod2sparse_add_row: row added to is shorter than row added from\n"); exit(1); } if (row1<0 || row1>=mod2sparse_rows(m1) || row2<0 || row2>=mod2sparse_rows(m2)) { fprintf (stderr,"mod2sparse_add_row: row index out of range\n"); exit(1); } f1 = mod2sparse_first_in_row(m1,row1); f2 = mod2sparse_first_in_row(m2,row2); while (!mod2sparse_at_end(f1) && !mod2sparse_at_end(f2)) { if (mod2sparse_col(f1)>mod2sparse_col(f2)) { mod2sparse_insert(m1,row1,mod2sparse_col(f2)); f2 = mod2sparse_next_in_row(f2); } else { ft = mod2sparse_next_in_row(f1); if (mod2sparse_col(f1)==mod2sparse_col(f2)) { mod2sparse_delete(m1,f1); f2 = mod2sparse_next_in_row(f2); } f1 = ft; } } while (!mod2sparse_at_end(f2)) { mod2sparse_insert(m1,row1,mod2sparse_col(f2)); f2 = mod2sparse_next_in_row(f2); } }
int mod2sparse_equal ( mod2sparse *m1, mod2sparse *m2 ) { mod2entry *e1, *e2; int i; if (mod2sparse_rows(m1)!=mod2sparse_rows(m2) || mod2sparse_cols(m1)!=mod2sparse_cols(m2)) { fprintf(stderr,"mod2sparse_equal: Matrices have different dimensions\n"); exit(1); } for (i = 0; i<mod2sparse_rows(m1); i++) { e1 = mod2sparse_first_in_row(m1,i); e2 = mod2sparse_first_in_row(m2,i); while (!mod2sparse_at_end(e1) && !mod2sparse_at_end(e2)) { if (mod2sparse_col(e1)!=mod2sparse_col(e2)) { return 0; } e1 = mod2sparse_next_in_row(e1); e2 = mod2sparse_next_in_row(e2); } if (!mod2sparse_at_end(e1) || !mod2sparse_at_end(e2)) { return 0; } } return 1; }
int mod2sparse_write ( FILE *f, mod2sparse *m ) { mod2entry *e; int i; intio_write(f,m->n_rows); if (ferror(f)) return 0; intio_write(f,m->n_cols); if (ferror(f)) return 0; for (i = 0; i<mod2sparse_rows(m); i++) { e = mod2sparse_first_in_row(m,i); if (!mod2sparse_at_end(e)) { intio_write (f, -(i+1)); if (ferror(f)) return 0; while (!mod2sparse_at_end(e)) { intio_write (f, mod2sparse_col(e)+1); if (ferror(f)) return 0; e = mod2sparse_next_in_row(e); } } } intio_write(f,0); if (ferror(f)) return 0; return 1; }
int mod2sparse_count_row ( mod2sparse *m, int row ) { mod2entry *e; int count; if (row<0 || row>=mod2sparse_rows(m)) { fprintf(stderr,"mod2sparse_count_row: row index out of bounds\n"); exit(1); } count = 0; for (e = mod2sparse_first_in_row(m,row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { count += 1; } return count; }
void mod2sparse_print ( FILE *f, mod2sparse *m ) { int rdigits, cdigits; mod2entry *e; int i; rdigits = mod2sparse_rows(m)<=10 ? 1 : mod2sparse_rows(m)<=100 ? 2 : mod2sparse_rows(m)<=1000 ? 3 : mod2sparse_rows(m)<=10000 ? 4 : mod2sparse_rows(m)<=100000 ? 5 : 6; cdigits = mod2sparse_cols(m)<=10 ? 1 : mod2sparse_cols(m)<=100 ? 2 : mod2sparse_cols(m)<=1000 ? 3 : mod2sparse_cols(m)<=10000 ? 4 : mod2sparse_cols(m)<=100000 ? 5 : 6; for (i = 0; i<mod2sparse_rows(m); i++) { fprintf(f,"%*d:",rdigits,i); e = mod2sparse_first_in_row(m,i); while (!mod2sparse_at_end(e)) { fprintf(f," %*d",cdigits,mod2sparse_col(e)); e = mod2sparse_next_in_row(e); } fprintf(f,"\n"); } }
/****************************************************************************** * InitSession : Initializes the LDPC session. * => See header file for more informations. */ ldpc_error_status InitSession ( LDPCFecSession *Session, int nbSourceSymbols, int nbParitySymbols, int symbolSize, int flags, int seed, SessionType codecType, int leftDegree) { int row, seq; mod2entry *e; Session->m_initialized = false; Session->m_sessionFlags = flags; Session->m_sessionType = codecType; Session->m_symbolSize = symbolSize; if (nbSourceSymbols + nbParitySymbols > GetMaxN()) return LDPC_ERROR; Session->m_nbSourceSymbols = nbSourceSymbols; Session->m_nbParitySymbols = nbParitySymbols; // sanity check: setting a left degree != 3 is only meaningfull with // LDGM codes, not with LDPC-Staircase/Triangle codes. if ((Session->m_sessionType != TypeLDGM) && (leftDegree != 3)) { fprintf(stderr, "LDPCFecSession::InitSession: ERROR: setting a leftDegree!=3 (here %d) is only meaningfull with LDGM codes, not with LDPC-Staircase/Triangle codes!\n", leftDegree); return LDPC_ERROR; } Session->m_leftDegree = leftDegree; Session->m_firstNonDecoded = 0; Session->m_pchkMatrix = CreatePchkMatrix(Session->m_nbParitySymbols, Session->m_nbSourceSymbols + Session->m_nbParitySymbols, Evenboth, Session->m_leftDegree, seed, false, Session->m_sessionType); if (Session->m_pchkMatrix == NULL) return LDPC_ERROR; if (Session->m_sessionFlags & FLAG_CODER) { Session->m_nb_unknown_symbols_encoder = (int*)calloc(Session->m_nbParitySymbols, sizeof(int)); if (Session->m_nb_unknown_symbols_encoder == NULL) return LDPC_ERROR; for (row = 0; row < Session->m_nbParitySymbols; row++) { mod2entry *e; for (e = mod2sparse_first_in_row(Session->m_pchkMatrix, row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { Session->m_nb_unknown_symbols_encoder[row]++; } } } else { Session->m_nb_unknown_symbols_encoder = NULL; } if (Session->m_sessionFlags & FLAG_DECODER) { if (((Session->m_checkValues = (void**)calloc(Session->m_nbParitySymbols, sizeof(void*))) == NULL) || ((Session->m_nbSymbols_in_equ = (int*)calloc(Session->m_nbParitySymbols, sizeof(int))) == NULL) || ((Session->m_nb_unknown_symbols = (int*)calloc(Session->m_nbParitySymbols, sizeof(int))) == NULL) || ((Session->m_nbEqu_for_parity = (int*)calloc(Session->m_nbParitySymbols, sizeof(int))) == NULL) || ((Session->m_parity_symbol_canvas = (void**)calloc(Session->m_nbParitySymbols, sizeof(void*))) == NULL)) { return LDPC_ERROR; } // and update the various tables now for (row = 0; row < Session->m_nbParitySymbols; row++) { for (e = mod2sparse_first_in_row(Session->m_pchkMatrix, row); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { Session->m_nbSymbols_in_equ[row]++; Session->m_nb_unknown_symbols[row]++; } } for (seq = Session->m_nbSourceSymbols; seq < (Session->m_nbParitySymbols+Session->m_nbSourceSymbols); seq++) { for (e = mod2sparse_first_in_col(Session->m_pchkMatrix, GetMatrixCol(Session, seq)); !mod2sparse_at_end(e); e = mod2sparse_next_in_col(e)) { Session->m_nbEqu_for_parity[seq - Session->m_nbSourceSymbols]++; } } } else { // CODER session Session->m_checkValues = NULL; Session->m_nbSymbols_in_equ = NULL; Session->m_nb_unknown_symbols = NULL; Session->m_nbEqu_for_parity = NULL; Session->m_parity_symbol_canvas = NULL; } if ((Session->m_sessionType == TypeTRIANGLE) && (((Session->m_nbParitySymbols+Session->m_nbSourceSymbols)/Session->m_nbSourceSymbols) < 2.0)) { Session->m_triangleWithSmallFECRatio = true; } else { Session->m_triangleWithSmallFECRatio = false; } Session->m_initialized = true; return LDPC_OK; }
void mod2sparse_multiply ( mod2sparse *m1, /* Left operand of multiply */ mod2sparse *m2, /* Right operand of multiply */ mod2sparse *r /* Place to store result of multiply */ ) { mod2entry *e1, *e2; int i, j, b; if (mod2sparse_cols(m1)!=mod2sparse_rows(m2) || mod2sparse_rows(m1)!=mod2sparse_rows(r) || mod2sparse_cols(m2)!=mod2sparse_cols(r)) { fprintf (stderr, "mod2sparse_multiply: Matrices have incompatible dimensions\n"); exit(1); } if (r==m1 || r==m2) { fprintf(stderr, "mod2sparse_multiply: Result matrix is the same as one of the operands\n"); exit(1); } mod2sparse_clear(r); for (i = 0; i<mod2sparse_rows(m1); i++) { if (mod2sparse_at_end(mod2sparse_first_in_row(m1,i))) { continue; } for (j = 0; j<mod2sparse_cols(m2); j++) { b = 0; e1 = mod2sparse_first_in_row(m1,i); e2 = mod2sparse_first_in_col(m2,j); while (!mod2sparse_at_end(e1) && !mod2sparse_at_end(e2)) { if (mod2sparse_col(e1)==mod2sparse_row(e2)) { b ^= 1; e1 = mod2sparse_next_in_row(e1); e2 = mod2sparse_next_in_col(e2); } else if (mod2sparse_col(e1)<mod2sparse_row(e2)) { e1 = mod2sparse_next_in_row(e1); } else { e2 = mod2sparse_next_in_col(e2); } } if (b) { mod2sparse_insert(r,i,j); } } } }
mod2sparse* CreatePchkMatrix ( int nbRows, int nbCols, make_method makeMethod, int leftDegree, int seed, bool no4cycle, SessionType type, int verbosity ) { mod2entry *e; #if 0 mod2entry *f, *g, *h; /* using by no4cycle mode */ int elim4; #endif int added, uneven; int i, j, k, t; int *u; mod2sparse *pchkMatrix = NULL; int skipCols = 0; // avoid warning int nbDataCols = 0; // avoid warning if (type != TypeLDGM && type != TypeSTAIRS && type != TypeTRIANGLE) { fprintf(stderr, "unsupported code type (%d)\n", type); return NULL; } skipCols = nbRows; nbDataCols = nbCols-skipCols; // Check for some problems. if (leftDegree>nbRows) { fprintf(stderr, "ERROR, Number of checks per bit (%d) is greater than total checks (%d)\n", leftDegree, nbRows); return NULL; } #if 0 if (leftDegree==nbRows && nbCols>1 && no4cycle) { fprintf(stderr, "ERROR, Can't eliminate cycles of length four with this many checks per bit\n"); return NULL; } #endif if (no4cycle) { fprintf(stderr, "ERROR: no4cycle mode is no longer supported!\n"); exit(-1); } ldpc_srand(seed); pchkMatrix = mod2sparse_allocate(nbRows, nbCols); /* Create the initial version of the parity check matrix. */ switch (makeMethod) { case Evencol: for(j=skipCols; j<nbCols; j++) { for(k=0; k<leftDegree; k++) { do { i = ldpc_rand(nbRows); } while (mod2sparse_find(pchkMatrix,i,j)); mod2sparse_insert(pchkMatrix,i,j); } } break; case Evenboth: u = (int*)chk_alloc (leftDegree*nbDataCols, sizeof *u); /* initialize a list of possible choices to guarantee a homogeneous "1" distribution */ for(k = leftDegree*nbDataCols-1; k>=0; k--) { u[k] = k%nbRows; } uneven = 0; t = 0; /* left limit within the list of possible choices, u[] */ for(j = skipCols; j<nbCols; j++) /* for each source symbol column */ { for(k = 0; k<leftDegree; k++) /* add left_degree "1s" */ { /* check that valid available choices remain */ for(i = t; i<leftDegree*nbDataCols && mod2sparse_find(pchkMatrix,u[i],j); i++) ; if(i < leftDegree*nbDataCols) { /* choose one index within the list of possible choices */ do { i = t + ldpc_rand(leftDegree*nbDataCols-t); } while (mod2sparse_find(pchkMatrix,u[i],j)); mod2sparse_insert(pchkMatrix,u[i],j); /* replace with u[t] which has never been chosen */ u[i] = u[t]; t++; } else { /* no choice left, choose one randomly */ uneven += 1; do { i = ldpc_rand(nbRows); } while (mod2sparse_find(pchkMatrix,i,j)); mod2sparse_insert(pchkMatrix,i,j); } } } if(uneven > 0 && verbosity >= 1) { fprintf(stderr,"Had to place %d checks in rows unevenly\n",uneven); } free(u); /* VR: added */ break; default: abort(); } /* Add extra bits to avoid rows with less than two checks. */ added = 0; for(i = 0; i<nbRows; i++) { e = mod2sparse_first_in_row(pchkMatrix,i); if(mod2sparse_at_end(e)) { j = (ldpc_rand(nbDataCols))+skipCols; e = mod2sparse_insert(pchkMatrix,i,j); added ++; } e = mod2sparse_first_in_row(pchkMatrix,i); if(mod2sparse_at_end(mod2sparse_next_in_row(e)) && nbDataCols>1) { do { j = (ldpc_rand(nbDataCols))+skipCols; } while (j==mod2sparse_col(e)); mod2sparse_insert(pchkMatrix,i,j); added ++; } } if(added > 0 && verbosity >= 1) { fprintf(stderr, "Added %d extra bit-checks to make row counts at least two\n", added); } /* Add extra bits to try to avoid problems with even column counts. */ if(leftDegree%2==0 && leftDegree<nbRows && nbDataCols>1 && added<2) { int a; for(a = 0; added+a<2; a++) { do { i = ldpc_rand(nbRows); j = (ldpc_rand(nbDataCols))+skipCols; } while (mod2sparse_find(pchkMatrix,i,j)); mod2sparse_insert(pchkMatrix,i,j); } if (verbosity >= 1) { fprintf(stderr, "Added %d extra bit-checks to try to avoid problems from even column counts\n", a); } } #if 0 /* Eliminate cycles of length four, if asked, and if possible. */ if(no4cycle) { elim4 = 0; for(t = 0; t<10; t++) { k = 0; for(j = 0; j<nbCols; j++) { for( e=mod2sparse_first_in_col(pchkMatrix,j); !mod2sparse_at_end(e); e=mod2sparse_next_in_col(e) ) { for( f=mod2sparse_first_in_row(pchkMatrix,mod2sparse_row(e)); !mod2sparse_at_end(f); f=mod2sparse_next_in_row(f) ) { if(f==e) continue; for(g=mod2sparse_first_in_col(pchkMatrix,mod2sparse_col(f)); !mod2sparse_at_end(g); g=mod2sparse_next_in_col(g) ) { if(g==f) continue; for( h=mod2sparse_first_in_row(pchkMatrix,mod2sparse_row(g)); !mod2sparse_at_end(h); h = mod2sparse_next_in_row(h) ) { if(mod2sparse_col(h)==j) { do { i = ldpc_rand(nbRows); } while (mod2sparse_find(pchkMatrix,i,j)); mod2sparse_delete(pchkMatrix,e); mod2sparse_insert(pchkMatrix,i,j); elim4 += 1; k += 1; goto nextj; } } } } } nextj: ; } if(k==0) break; } if(elim4>0) { fprintf(stderr, "Eliminated %d cycles of length four by moving checks within column\n", elim4); } if(t==10) { fprintf(stderr, "Couldn't eliminate all cycles of length four in 10 passes\n"); } } #endif switch (type) { case TypeLDGM: for (i = 0; i < nbRows; i++) { /* identity */ mod2sparse_insert(pchkMatrix, i, i); } break; case TypeSTAIRS: mod2sparse_insert(pchkMatrix, 0, 0); /* 1st row */ for (i = 1; i < nbRows; i++) { /* for all other rows */ /* identity */ mod2sparse_insert(pchkMatrix, i, i); /* staircase */ mod2sparse_insert(pchkMatrix, i, i-1); } break; case TypeTRIANGLE: mod2sparse_insert(pchkMatrix, 0, 0); /* 1st row */ for (i = 1; i < nbRows; i++) { /* for all other rows */ /* identity */ mod2sparse_insert(pchkMatrix, i, i); /* staircase */ mod2sparse_insert(pchkMatrix, i, i-1); /* triangle */ j = i-1; for (int l = 0; l < j; l++) { /* limit the # of "1s" added */ j = ldpc_rand(j); mod2sparse_insert(pchkMatrix, i, j); } } break; } return pchkMatrix; }
void make_ldpc ( int seed, /* Random number seed */ make_method method, /* How to make it */ distrib *d, /* Distribution list specified */ int no4cycle /* Eliminate cycles of length four? */ ) { mod2entry *e, *f, *g, *h; int added, uneven, elim4, all_even, n_full, left; int i, j, k, t, z, cb_N; int *part, *u; rand_seed(10*seed+1); H = mod2sparse_allocate(M,N); part = column_partition(d,N); /* Create the initial version of the parity check matrix. */ switch (method) { case Evencol: { z = 0; left = part[z]; for (j = 0; j<N; j++) { while (left==0) { z += 1; if (z>distrib_size(d)) { abort(); } left = part[z]; } for (k = 0; k<distrib_num(d,z); k++) { do { i = rand_int(M); } while (mod2sparse_find(H,i,j)); mod2sparse_insert(H,i,j); } left -= 1; } break; } case Evenboth: { cb_N = 0; for (z = 0; z<distrib_size(d); z++) { cb_N += distrib_num(d,z) * part[z]; } u = chk_alloc (cb_N, sizeof *u); for (k = cb_N-1; k>=0; k--) { u[k] = k%M; } uneven = 0; t = 0; z = 0; left = part[z]; for (j = 0; j<N; j++) { while (left==0) { z += 1; if (z>distrib_size(d)) { abort(); } left = part[z]; } for (k = 0; k<distrib_num(d,z); k++) { for (i = t; i<cb_N && mod2sparse_find(H,u[i],j); i++) ; if (i==cb_N) { uneven += 1; do { i = rand_int(M); } while (mod2sparse_find(H,i,j)); mod2sparse_insert(H,i,j); } else { do { i = t + rand_int(cb_N-t); } while (mod2sparse_find(H,u[i],j)); mod2sparse_insert(H,u[i],j); u[i] = u[t]; t += 1; } } left -= 1; } if (uneven>0) { fprintf(stderr,"Had to place %d checks in rows unevenly\n",uneven); } break; } default: abort(); } /* Add extra bits to avoid rows with less than two checks. */ added = 0; for (i = 0; i<M; i++) { e = mod2sparse_first_in_row(H,i); if (mod2sparse_at_end(e)) { j = rand_int(N); e = mod2sparse_insert(H,i,j); added += 1; } e = mod2sparse_first_in_row(H,i); if (mod2sparse_at_end(mod2sparse_next_in_row(e)) && N>1) { do { j = rand_int(N); } while (j==mod2sparse_col(e)); mod2sparse_insert(H,i,j); added += 1; } } if (added>0) { fprintf(stderr, "Added %d extra bit-checks to make row counts at least two\n", added); } /* Add extra bits to try to avoid problems with even column counts. */ n_full = 0; all_even = 1; for (z = 0; z<distrib_size(d); z++) { if (distrib_num(d,z)==M) { n_full += part[z]; } if (distrib_num(d,z)%2==1) { all_even = 0; } } if (all_even && N-n_full>1 && added<2) { int a; for (a = 0; added+a<2; a++) { do { i = rand_int(M); j = rand_int(N); } while (mod2sparse_find(H,i,j)); mod2sparse_insert(H,i,j); } fprintf(stderr, "Added %d extra bit-checks to try to avoid problems from even column counts\n", a); } /* Eliminate cycles of length four, if asked, and if possible. */ if (no4cycle) { elim4 = 0; for (t = 0; t<10; t++) { k = 0; for (j = 0; j<N; j++) { for (e = mod2sparse_first_in_col(H,j); !mod2sparse_at_end(e); e = mod2sparse_next_in_col(e)) { for (f = mod2sparse_first_in_row(H,mod2sparse_row(e)); !mod2sparse_at_end(f); f = mod2sparse_next_in_row(f)) { if (f==e) continue; for (g = mod2sparse_first_in_col(H,mod2sparse_col(f)); !mod2sparse_at_end(g); g = mod2sparse_next_in_col(g)) { if (g==f) continue; for (h = mod2sparse_first_in_row(H,mod2sparse_row(g)); !mod2sparse_at_end(h); h = mod2sparse_next_in_row(h)) { if (mod2sparse_col(h)==j) { do { i = rand_int(M); } while (mod2sparse_find(H,i,j)); mod2sparse_delete(H,e); mod2sparse_insert(H,i,j); elim4 += 1; k += 1; goto nextj; } } } } } nextj: ; } if (k==0) break; } if (elim4>0) { fprintf(stderr, "Eliminated %d cycles of length four by moving checks within column\n", elim4); } if (t==10) { fprintf(stderr, "Couldn't eliminate all cycles of length four in 10 passes\n"); } } }
mod2entry *mod2sparse_insert ( mod2sparse *m, int row, int col ) { #ifndef SPARSE_MATRIX_OPT_FOR_LDPC_STAIRCASE mod2entry *re, *ce, *ne; #else mod2entry *re, *ce, *ne, *ce2; #endif if (row<0 || row>=mod2sparse_rows(m) || col<0 || col>=mod2sparse_cols(m)) { fprintf(stderr,"mod2sparse_insert: row or column index out of bounds\n"); exit(1); } /* Find old entry and return it, or allocate new entry and insert into row. */ re = mod2sparse_last_in_row(m,row); if (!mod2sparse_at_end(re) && mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(re) || mod2sparse_col(re)<col) { re = re->right; } else { re = mod2sparse_first_in_row(m,row); for (;;) { if (!mod2sparse_at_end(re) && mod2sparse_col(re)==col) { return re; } if (mod2sparse_at_end(re) || mod2sparse_col(re)>col) { break; } re = mod2sparse_next_in_row(re); } } ne = alloc_entry(m); ne->row = row; ne->col = col; ne->left = re->left; ne->right = re; ne->left->right = ne; ne->right->left = ne; /* Insert new entry into column. */ #ifndef SPARSE_MATRIX_OPT_FOR_LDPC_STAIRCASE /* If we find an existing entry here, the matrix must be garbled, since we didn't find it in the row. */ ce = mod2sparse_last_in_col(m,col); if (!mod2sparse_at_end(ce) && mod2sparse_row(ce)==row) { fprintf(stderr,"mod2sparse_insert: Garbled matrix\n"); exit(1); } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)<row) { ce = ce->down; } else { #else ce2 = &(m->cols[col]); #endif ce = mod2sparse_first_in_col(m,col); for (;;) { if (!mod2sparse_at_end(ce) && mod2sparse_row(ce)==row) { fprintf(stderr,"mod2sparse_insert: Garbled matrix\n"); exit(1); } if (mod2sparse_at_end(ce) || mod2sparse_row(ce)>row) { break; } #ifdef SPARSE_MATRIX_OPT_FOR_LDPC_STAIRCASE ce2 = ce; #endif ce = mod2sparse_next_in_col(ce); } #ifndef SPARSE_MATRIX_OPT_FOR_LDPC_STAIRCASE } ne->up = ce->up; ne->down = ce; ne->up->down = ne; ne->down->up = ne; #else ne->down = ce; ce2->down = ne; #endif /* Return the new entry. */ return ne; }
int mod2sparse_tunnel_sub ( mod2sparse *L, /* Matrix that is lower triangular after reordering */ int *rows, /* Array of indexes (from 0) of rows for new order */ char *x, /* Vector on right of equation, also reordered */ char *y /* Place to store solution */ ) { int K, i, j, ii, b, d; mod2entry *e; K = mod2sparse_cols(L); /* Make sure that L is lower-triangular, after row re-ordering. */ for (i = 0; i<K; i++) { ii = rows ? rows[i] : i; e = mod2sparse_last_in_row(L,ii); if (!mod2sparse_at_end(e) && mod2sparse_col(e)>i) { fprintf(stderr, "mod2sparse_tunnel_sub: Matrix is not lower-triangular\n"); exit(1); } } /* Solve system by tunnel substitution. */ for (i = 0; i<K; i++) { ii = rows ? rows[i] : i; /* Look at bits in this row, forming inner product with partial solution, and seeing if the diagonal is 1. */ d = 0; b = 0; for (e = mod2sparse_first_in_row(L,ii); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { j = mod2sparse_col(e); if (j==i) { d = 1; } else { b ^= y[j]; } } /* Check for no solution if the diagonal isn't 1. */ if (!d && b!=x[ii]) { return 0; } /* Set bit of solution, zero if arbitrary. */ y[i] = b^x[ii]; } return 1; }
int mod2sparse_backward_sub ( mod2sparse *U, /* Matrix that is upper triangular after reordering */ int *cols, /* Array of indexes (from 0) of columns for new order */ char *y, /* Vector on right of equation */ char *z /* Place to store solution, also reordered */ ) { int K, i, j, ii, b, d; mod2entry *e; K = mod2sparse_rows(U); /* Make sure that U is upper-triangular, after column re-ordering. */ for (i = 0; i<K; i++) { ii = cols ? cols[i] : i; e = mod2sparse_last_in_col(U,ii); if (!mod2sparse_at_end(e) && mod2sparse_row(e)>i) { fprintf(stderr, "mod2sparse_backward_sub: Matrix is not upper-triangular\n"); exit(1); } } /* Solve system by backward substitution. */ for (i = K-1; i>=0; i--) { ii = cols ? cols[i] : i; /* Look at bits in this row, forming inner product with partial solution, and seeing if the diagonal is 1. */ d = 0; b = 0; for (e = mod2sparse_first_in_row(U,i); !mod2sparse_at_end(e); e = mod2sparse_next_in_row(e)) { j = mod2sparse_col(e); if (j==ii) { d = 1; } else { b ^= z[j]; } } /* Check for no solution if the diagonal isn't 1. */ if (!d && b!=y[i]) { return 0; } /* Set bit of solution, zero if arbitrary. */ z[ii] = b^y[i]; } return 1; }
int mod2sparse_decomp ( mod2sparse *A, /* Input matrix, M by N */ int K, /* Size of sub-matrix to find LU decomposition of */ mod2sparse *L, /* Matrix in which L is stored, M by K */ mod2sparse *U, /* Matrix in which U is stored, K by N */ int *rows, /* Array where row indexes are stored, M long */ int *cols, /* Array where column indexes are stored, N long */ mod2sparse_strategy strategy, /* Strategy to follow in picking rows/columns */ int abandon_number, /* Number of columns to abandon at some point */ int abandon_when /* When to abandon these columns */ ) { int *rinv=NULL, *cinv=NULL, *acnt=NULL, *rcnt=NULL; mod2sparse *B=NULL; int M, N; mod2entry *e=NULL, *f=NULL, *fn=NULL, *e2=NULL; int i=0, j=0, k=0, cc=0, cc2=0, cc3=0, cr2=0, pr=0; int found, nnf; M = mod2sparse_rows(A); N = mod2sparse_cols(A); if (mod2sparse_cols(L)!=K || mod2sparse_rows(L)!=M || mod2sparse_cols(U)!=N || mod2sparse_rows(U)!=K) { fprintf (stderr, "mod2sparse_decomp: Matrices have incompatible dimensions\n"); exit(1); } if (abandon_number>N-K) { fprintf(stderr,"Trying to abandon more columns than allowed\n"); exit(1); } rinv = (int*)chk_alloc (M, sizeof *rinv); cinv = (int*)chk_alloc (N, sizeof *cinv); if (abandon_number>0) { acnt = (int*)chk_alloc (M+1, sizeof *acnt); } if (strategy==Mod2sparse_minprod) { rcnt = (int*)chk_alloc (M, sizeof *rcnt); } mod2sparse_clear(L); mod2sparse_clear(U); /* Copy A to B. B will be modified, then discarded. */ B = mod2sparse_allocate(M,N); mod2sparse_copy(A,B); /* Count 1s in rows of B, if using minprod strategy. */ if (strategy==Mod2sparse_minprod) { for (i = 0; i<M; i++) { rcnt[i] = mod2sparse_count_row(B,i); } } /* Set up initial row and column choices. */ for (i = 0; i<M; i++) rows[i] = rinv[i] = i; for (j = 0; j<N; j++) cols[j] = cinv[j] = j; /* Find L and U one column at a time. */ nnf = 0; for (i = 0; i<K; i++) { /* Choose the next row and column of B. */ switch (strategy) { case Mod2sparse_first: { found = 0; for (k = i; k<N; k++) { e = mod2sparse_first_in_col(B,cols[k]); while (!mod2sparse_at_end(e)) { if (rinv[mod2sparse_row(e)]>=i) { found = 1; goto out_first; } e = mod2sparse_next_in_col(e); } } out_first: break; } case Mod2sparse_mincol: { found = 0; for (j = i; j<N; j++) { cc2 = mod2sparse_count_col(B,cols[j]); if (!found || cc2<cc) { e2 = mod2sparse_first_in_col(B,cols[j]); while (!mod2sparse_at_end(e2)) { if (rinv[mod2sparse_row(e2)]>=i) { found = 1; cc = cc2; e = e2; k = j; break; } e2 = mod2sparse_next_in_col(e2); } } } break; } case Mod2sparse_minprod: { found = 0; for (j = i; j<N; j++) { cc2 = mod2sparse_count_col(B,cols[j]); e2 = mod2sparse_first_in_col(B,cols[j]); while (!mod2sparse_at_end(e2)) { if (rinv[mod2sparse_row(e2)]>=i) { cr2 = rcnt[mod2sparse_row(e2)]; if (!found || cc2==1 || (cc2-1)*(cr2-1)<pr) { found = 1; pr = cc2==1 ? 0 : (cc2-1)*(cr2-1); e = e2; k = j; } } e2 = mod2sparse_next_in_col(e2); } } break; } default: { fprintf(stderr,"mod2sparse_decomp: Unknown stategy\n"); exit(1); } } if (!found) { nnf += 1; } /* Update 'rows' and 'cols'. Looks at 'k' and 'e' found above. */ if (found) { if (cinv[mod2sparse_col(e)]!=k) abort(); cols[k] = cols[i]; cols[i] = mod2sparse_col(e); cinv[cols[k]] = k; cinv[cols[i]] = i; k = rinv[mod2sparse_row(e)]; if (k<i) abort(); rows[k] = rows[i]; rows[i] = mod2sparse_row(e); rinv[rows[k]] = k; rinv[rows[i]] = i; } /* Update L, U, and B. */ f = mod2sparse_first_in_col(B,cols[i]); while (!mod2sparse_at_end(f)) { fn = mod2sparse_next_in_col(f); k = mod2sparse_row(f); if (rinv[k]>i) { mod2sparse_add_row(B,k,B,mod2sparse_row(e)); if (strategy==Mod2sparse_minprod) { rcnt[k] = mod2sparse_count_row(B,k); } mod2sparse_insert(L,k,i); } else if (rinv[k]<i) { mod2sparse_insert(U,rinv[k],cols[i]); } else { mod2sparse_insert(L,k,i); mod2sparse_insert(U,i,cols[i]); } f = fn; } /* Get rid of all entries in the current column of B, just to save space. */ for (;;) { f = mod2sparse_first_in_col(B,cols[i]); if (mod2sparse_at_end(f)) break; mod2sparse_delete(B,f); } /* Abandon columns of B with lots of entries if it's time for that. */ if (abandon_number>0 && i==abandon_when) { for (k = 0; k<M+1; k++) { acnt[k] = 0; } for (j = 0; j<N; j++) { k = mod2sparse_count_col(B,j); acnt[k] += 1; } cc = abandon_number; k = M; while (acnt[k]<cc) { cc -= acnt[k]; k -= 1; if (k<0) abort(); } cc2 = 0; for (j = 0; j<N; j++) { cc3 = mod2sparse_count_col(B,j); if (cc3>k || cc3==k && cc>0) { if (cc3==k) cc -= 1; for (;;) { f = mod2sparse_first_in_col(B,j); if (mod2sparse_at_end(f)) break; mod2sparse_delete(B,f); } cc2 += 1; } } if (cc2!=abandon_number) abort(); if (strategy==Mod2sparse_minprod) { for (j = 0; j<M; j++) { rcnt[j] = mod2sparse_count_row(B,j); } } } } /* Get rid of all entries in the rows of L past row K, after reordering. */ for (i = K; i<M; i++) { for (;;) { f = mod2sparse_first_in_row(L,rows[i]); if (mod2sparse_at_end(f)) break; mod2sparse_delete(L,f); } } mod2sparse_free(B); free(rinv); free(cinv); if (strategy==Mod2sparse_minprod) free(rcnt); if (abandon_number>0) free(acnt); return nnf; }
int main ( int argc, char **argv ) { char *alist_file, *pchk_file; FILE *af, *pf; int mxrw, mxcw; int *rw, *cw; int i, j, k; mod2entry *e; int trans; int nozeros; int last; trans = 0; nozeros = 0; for (;;) { if (argc>1 && strcmp(argv[1],"-t")==0) { trans = 1; argc -= 1; argv += 1; } else if (argc>1 && strcmp(argv[1],"-z")==0) { nozeros = 1; argc -= 1; argv += 1; } else { break; } } if (argc!=3) { usage(); } pchk_file = argv[1]; alist_file = argv[2]; read_pchk(pchk_file); if (trans) { mod2sparse *HT; HT = H; H = mod2sparse_allocate(N,M); mod2sparse_transpose(HT,H); M = mod2sparse_rows(H); N = mod2sparse_cols(H); } af = open_file_std(alist_file,"wb"); if (af==NULL) { fprintf(stderr,"Can't create alist file: %s\n",alist_file); exit(1); } fprintf(af,"%d %d\n",M,N); rw = (int *) chk_alloc (M, sizeof *rw); mxrw = 0; for (i = 0; i<M; i++) { rw[i] = mod2sparse_count_row(H,i); if (rw[i]>mxrw) { mxrw = rw[i]; } } cw = (int *) chk_alloc (N, sizeof *cw); mxcw = 0; for (j = 0; j<N; j++) { cw[j] = mod2sparse_count_col(H,j); if (cw[j]>mxcw) { mxcw = cw[j]; } } fprintf(af,"%d %d\n",mxrw,mxcw); for (i = 0; i<M; i++) { fprintf(af,"%d%c",rw[i],i==M-1?'\n':' '); } for (j = 0; j<N; j++) { fprintf(af,"%d%c",cw[j],j==N-1?'\n':' '); } for (i = 0; i<M; i++) { e = mod2sparse_first_in_row(H,i); last = 0; for (k = 0; !last; k++) { last = nozeros ? k==rw[i]-1 : k==mxrw-1; fprintf (af, "%d%c", mod2sparse_at_end(e)?0:mod2sparse_col(e)+1, last?'\n':' '); if (!mod2sparse_at_end(e)) { e = mod2sparse_next_in_row(e); } } } for (j = 0; j<N; j++) { e = mod2sparse_first_in_col(H,j); last = 0; for (k = 0; !last; k++) { last = nozeros ? k==cw[j]-1 : k==mxcw-1; fprintf (af, "%d%c", mod2sparse_at_end(e)?0:mod2sparse_row(e)+1, last?'\n':' '); if (!mod2sparse_at_end(e)) { e = mod2sparse_next_in_col(e); } } } if (ferror(af) || fclose(af)!=0) { fprintf(stderr,"Error writing to alist file %s\n",alist_file); exit(1); } return 0; }