/*
 * Java Forward Error Correction Library
 * Copyright (C) 2001 Onion Networks
 * Copyright (C) 2000 OpenCola
 *
 * Portions derived from code by Luigi Rizzo:
 * fec.c -- forward error correction based on Vandermonde matrices
 * 980624
 * (C) 1997-98 Luigi Rizzo (luigi@iet.unipi.it)
 *
 * Portions derived from code by Phil Karn (karn@ka9q.ampr.org),
 * Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) and Hari
 * Thirumoorthy (harit@spectra.eng.hawaii.edu), Aug 1995
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the following
 *    disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package anon.mixminion.fec;

import anon.util.ByteArrayUtil;

/**
 * This class provides the majority of the logic for the pure Java
 * implementation of the vandermonde FEC codes.  This code is heavily derived
 * from Luigi Rizzo's original C implementation.  Copyright information can
 * be found in the 'LICENSE' file that comes with this distribution.
 *
 * (c) Copyright 2001 Onion Networks
 * (c) Copyright 2000 OpenCola
 *
 * @author Justin F. Chapweske (justin@chapweske.com)
 * @author JAP-Team -- made some changes
 */
public class FECMath
{

	/**
	 * The following parameter defines how many bits are used for
	 * field elements.  This probably only supports 8 and 16 bit codes
	 * at this time because Java lacks a typedef construct.  This code
	 * should perhaps be redone with some sort of template language/
	 * precompiler for Java.
	 */

	// code over GF(2**gfBits) - change to suit
	public int gfBits;

	/*
	 * You should not need to change anything beyond this point.
	 * The first part of the file implements linear algebra in GF.
	 *
	 * gf is the type used to store an element of the Galois Field.
	 * Must constain at least gfBits bits.
	 */
	// 2^n-1 = the number of elements in this extension field
	public int gfSize; // powers of alpha

	/**
	 * Primitive polynomials - see Lin & Costello, Appendix A,
	 * and  Lee & Messerschmitt, p. 453.
	 */
	public static final String[] prim_polys =
		{
		// gfBits	polynomial
		null, //  0            no code
		null, //  1            no code
		"111", //  2            1+x+x^2
		"1101", //  3            1+x+x^3
		"11001", //  4            1+x+x^4
		"101001", //  5            1+x^2+x^5
		"1100001", //  6            1+x+x^6
		"10010001", //  7            1 + x^3 + x^7
		"101110001", //  8            1+x^2+x^3+x^4+x^8
		"1000100001", //  9            1+x^4+x^9
		"10010000001", // 10            1+x^3+x^10
		"101000000001", // 11            1+x^2+x^11
		"1100101000001", // 12            1+x+x^4+x^6+x^12
		"11011000000001", // 13            1+x+x^3+x^4+x^13
		"110000100010001", // 14            1+x+x^6+x^10+x^14
		"1100000000000001", // 15            1+x+x^15
		"11010000000010001" // 16            1+x+x^3+x^12+x^16
	};

	/**
	 * To speed up computations, we have tables for logarithm, exponent
	 * and inverse of a number. If gfBits <= 8, we use a table for
	 * multiplication as well (it takes 64K, no big deal even on a PDA,
	 * especially because it can be pre-initialized an put into a ROM!),
	 * otherwhise we use a table of logarithms.
	 */

	// index->poly form conversion table
	public char[] gf_exp;
	// Poly->index form conversion table
	public int[] gf_log;
	// inverse of field elem.
	public char[] inverse;

	// inv[\alpha**i]=\alpha**(gfSize-i-1)

	/**
	 * gf_mul(x,y) multiplies two numbers. If gfBits<=8, it is much
	 * faster to use a multiplication table.
	 *
	 * USE_GF_MULC, GF_MULC0(c) and GF_ADDMULC(x) can be used when multiplying
	 * many numbers by the same constant. In this case the first
	 * call sets the constant, and others perform the multiplications.
	 * A value related to the multiplication is held in a local variable
	 * declared with USE_GF_MULC . See usage in addMul1().
	 */
	public char[][] gf_mul_table;

	public FECMath()
	{
		this(8);
	}

	public FECMath(int gfBits)
	{
		this.gfBits = gfBits;
		this.gfSize = ( (1 << gfBits) - 1);

		gf_exp = new char[2 * gfSize];
		gf_log = new int[gfSize + 1];
		inverse = new char[gfSize + 1];

		if (gfBits < 2 || gfBits > 16)
		{
			throw new IllegalArgumentException("gfBits must be 2 .. 16");
		}
		generateGF();
		if (gfBits <= 8)
		{
			initMulTable();
		}
	}

	public final void generateGF()
	{
		int i;

		String primPoly = prim_polys[gfBits];

		char mask = 1; // x ** 0 = 1
		gf_exp[gfBits] = 0; // will be updated at the end of the 1st loop
		/*
		 * first, generate the (polynomial representation of) powers of \alpha,
		 * which are stored in gf_exp[i] = \alpha ** i .
		 * At the same time build gf_log[gf_exp[i]] = i .
		 * The first gfBits powers are simply bits shifted to the left.
		 */
		for (i = 0; i < gfBits; i++, mask <<= 1)
		{
			gf_exp[i] = mask;
			gf_log[gf_exp[i]] = i;
			/*
			 * If primPoly[i] == 1 then \alpha ** i occurs in poly-repr
			 * gf_exp[gfBits] = \alpha ** gfBits
			 */
			if (primPoly.charAt(i) == '1')
			{
				gf_exp[gfBits] ^= mask;
			}
		}
		/*
		 * now gf_exp[gfBits] = \alpha ** gfBits is complete, so can als
		 * compute its inverse.
		 */
		gf_log[gf_exp[gfBits]] = gfBits;
		/*
		 * Poly-repr of \alpha ** (i+1) is given by poly-repr of
		 * \alpha ** i shifted left one-bit and accounting for any
		 * \alpha ** gfBits term that may occur when poly-repr of
		 * \alpha ** i is shifted.
		 */
		mask = (char) (1 << (gfBits - 1));
		for (i = gfBits + 1; i < gfSize; i++)
		{
			if (gf_exp[i - 1] >= mask)
			{
				gf_exp[i] = (char) (gf_exp[gfBits] ^
									( (gf_exp[i - 1] ^ mask) << 1));
			}
			else
			{
				gf_exp[i] = (char) (gf_exp[i - 1] << 1);
			}
			gf_log[gf_exp[i]] = i;
		}
		/*
		 * log(0) is not defined, so use a special value
		 */
		gf_log[0] = gfSize;
		// set the extended gf_exp values for fast multiply
		for (i = 0; i < gfSize; i++)
		{
			gf_exp[i + gfSize] = gf_exp[i];
		}

		/*
		 * again special cases. 0 has no inverse. This used to
		 * be initialized to gfSize, but it should make no difference
		 * since noone is supposed to read from here.
		 */
		inverse[0] = 0;
		inverse[1] = 1;
		for (i = 2; i <= gfSize; i++)
		{
			inverse[i] = gf_exp[gfSize - gf_log[i]];
		}
	}

	public final void initMulTable()
	{
		if (gfBits <= 8)
		{
			gf_mul_table = new char[gfSize + 1][gfSize + 1];

			int i, j;
			for (i = 0; i < gfSize + 1; i++)
			{
				for (j = 0; j < gfSize + 1; j++)
				{
					gf_mul_table[i][j] = gf_exp[modnn(gf_log[i] + gf_log[j])];
				}
			}
			for (j = 0; j < gfSize + 1; j++)
			{
				gf_mul_table[0][j] = gf_mul_table[j][0] = 0;
			}
		}
	}

	/**
	 * modnn(x) computes x % gfSize, where gfSize is 2**gfBits - 1,
	 * without a slow divide.
	 */
	public final char modnn(int x)
	{
		while (x >= gfSize)
		{
			x -= gfSize;
			x = (x >> gfBits) + (x & gfSize);
		}
		return (char) x;
	}

	public final char mul(char x, char y)
	{
		if (gfBits <= 8)
		{
			return gf_mul_table[x][y];
		}
		else
		{
			if (x == 0 || y == 0)
			{
				return 0;
			}

			return gf_exp[gf_log[x] + gf_log[y]];
		}
	}

	/**
	 * Generate GF(2**m) from the irreducible polynomial p(X) in p[0]..p[m]
	 * Lookup tables:
	 *     index->polynomial form		gf_exp[] contains j= \alpha^i;
	 *     polynomial form -> index form	gf_log[ j = \alpha^i ] = i
	 * \alpha=x is the primitive element of GF(2^m)
	 *
	 * For efficiency, gf_exp[] has size 2*gfSize, so that a simple
	 * multiplication of two numbers can be resolved without calling modnn
	 */
	public static final char[] createGFMatrix(int rows, int cols)
	{
		return new char[rows * cols];
	}

	/*
	 * addMul() computes dst[] = dst[] + c * src[]
	 * This is used often, so better optimize it! Currently the loop is
	 * unrolled 16 times, a good value for 486 and pentium-class machines.
	 * The case c=0 is also optimized, whereas c=1 is not. These
	 * calls are unfrequent in my typical apps so I did not bother.
	 *
	 */
	public final void addMul(char[] dst, int dstPos, char[] src,
							 int srcPos, char c, int len)
	{
		// nop, optimize
		if (c == 0)
		{
			return;
		}

		int unroll = 16; // unroll the loop 16 times.
		int i = dstPos;
		int j = srcPos;
		int lim = dstPos + len;

		if (gfBits <= 8)
		{ // use our multiplication table.
			// Instead of doing gf_mul_table[c,x] for multiply, we'll save
			// the gf_mul_table[c] to a local variable since it is going to
			// be used many times.
			char[] gf_mulc = gf_mul_table[c];

			// Not sure if loop unrolling has any real benefit in Java, but
			// what the hey.
			for (; i < lim && (lim - i) > unroll; i += unroll, j += unroll)
			{
				// dst ^= gf_mulc[x] is equal to mult then add (xor == add)

				dst[i] ^= gf_mulc[src[j]];
				dst[i + 1] ^= gf_mulc[src[j + 1]];
				dst[i + 2] ^= gf_mulc[src[j + 2]];
				dst[i + 3] ^= gf_mulc[src[j + 3]];
				dst[i + 4] ^= gf_mulc[src[j + 4]];
				dst[i + 5] ^= gf_mulc[src[j + 5]];
				dst[i + 6] ^= gf_mulc[src[j + 6]];
				dst[i + 7] ^= gf_mulc[src[j + 7]];
				dst[i + 8] ^= gf_mulc[src[j + 8]];
				dst[i + 9] ^= gf_mulc[src[j + 9]];
				dst[i + 10] ^= gf_mulc[src[j + 10]];
				dst[i + 11] ^= gf_mulc[src[j + 11]];
				dst[i + 12] ^= gf_mulc[src[j + 12]];
				dst[i + 13] ^= gf_mulc[src[j + 13]];
				dst[i + 14] ^= gf_mulc[src[j + 14]];
				dst[i + 15] ^= gf_mulc[src[j + 15]];
			}

			// final components
			for (; i < lim; i++, j++)
			{
				dst[i] ^= gf_mulc[src[j]];
			}

		}
		else
		{ // gfBits > 8, no multiplication table
			int mulcPos = gf_log[c];

			// unroll your own damn loop.
			int y;
			for (; i < lim; i++, j++)
			{
				if ( (y = src[j]) != 0)
				{
					dst[i] ^= gf_exp[mulcPos + gf_log[y]];
				}
			}
		}
	}

	/*
	 * addMul() computes dst[] = dst[] + c * src[]
	 * This is used often, so better optimize it! Currently the loop is
	 * unrolled 16 times, a good value for 486 and pentium-class machines.
	 * The case c=0 is also optimized, whereas c=1 is not. These
	 * calls are unfrequent in my typical apps so I did not bother.
	 *
	 */
	public final void addMul(byte[] dst, int dstPos, byte[] src,
							 int srcPos, byte c, int len)
	{
		// nop, optimize
		if (c == 0)
		{
			return;
		}

		int unroll = 16; // unroll the loop 16 times.
		int i = dstPos;
		int j = srcPos;
		int lim = dstPos + len;

		// use our multiplication table.
		// Instead of doing gf_mul_table[c,x] for multiply, we'll save
		// the gf_mul_table[c] to a local variable since it is going to
		// be used many times.
		char[] gf_mulc = gf_mul_table[c & 0xff];

		// Not sure if loop unrolling has any real benefit in Java, but
		// what the hey.
		for (; i < lim && (lim - i) > unroll; i += unroll, j += unroll)
		{
			// dst ^= gf_mulc[x] is equal to mult then add (xor == add)

			dst[i] ^= gf_mulc[src[j] & 0xff];
			dst[i + 1] ^= gf_mulc[src[j + 1] & 0xff];
			dst[i + 2] ^= gf_mulc[src[j + 2] & 0xff];
			dst[i + 3] ^= gf_mulc[src[j + 3] & 0xff];
			dst[i + 4] ^= gf_mulc[src[j + 4] & 0xff];
			dst[i + 5] ^= gf_mulc[src[j + 5] & 0xff];
			dst[i + 6] ^= gf_mulc[src[j + 6] & 0xff];
			dst[i + 7] ^= gf_mulc[src[j + 7] & 0xff];
			dst[i + 8] ^= gf_mulc[src[j + 8] & 0xff];
			dst[i + 9] ^= gf_mulc[src[j + 9] & 0xff];
			dst[i + 10] ^= gf_mulc[src[j + 10] & 0xff];
			dst[i + 11] ^= gf_mulc[src[j + 11] & 0xff];
			dst[i + 12] ^= gf_mulc[src[j + 12] & 0xff];
			dst[i + 13] ^= gf_mulc[src[j + 13] & 0xff];
			dst[i + 14] ^= gf_mulc[src[j + 14] & 0xff];
			dst[i + 15] ^= gf_mulc[src[j + 15] & 0xff];
		}

		// final components
		for (; i < lim; i++, j++)
		{
			dst[i] ^= gf_mulc[src[j] & 0xff];
		}
	}

	/*
	 * computes C = AB where A is n*k, B is k*m, C is n*m
	 */
	public final void matMul(char[] a, char[] b, char[] c,
							 int n, int k, int m)
	{
		matMul(a, 0, b, 0, c, 0, n, k, m);
	}

	/*
	 * computes C = AB where A is n*k, B is k*m, C is n*m
	 */
	public final void matMul(char[] a, int aStart, char[] b, int bStart,
							 char[] c, int cStart, int n, int k, int m)
	{

		for (int row = 0; row < n; row++)
		{
			for (int col = 0; col < m; col++)
			{
				int posA = row * k;
				int posB = col;
				char acc = 0;
				for (int i = 0; i < k; i++, posA++, posB += m)
				{
					acc ^= mul(a[aStart + posA], b[bStart + posB]);
				}
				c[cStart + (row * m + col)] = acc;
			}
		}
	}

	/*
	 * Checks to see if the square matrix is identiy
	 * @return whether it is an identity matrix or not
	 */
	public static final boolean isIdentity(char[] m, int k)
	{
		int pos = 0;
		for (int row = 0; row < k; row++)
		{
			for (int col = 0; col < k; col++)
			{
				if ( (row == col && m[pos] != 1) ||
					(row != col && m[pos] != 0))
				{
					return false;
				}
				else
				{
					pos++;
				}
			}
		}
		return true;
	}

	/*
	 * invertMatrix() takes a matrix and produces its inverse
	 * k is the size of the matrix.
	 * (Gauss-Jordan, adapted from Numerical Recipes in C)
	 * Return non-zero if singular.
	 */
	public final void invertMatrix(char[] src, int k) throws IllegalArgumentException
	{

		int[] indxc = new int[k];
		int[] indxr = new int[k];

		// ipiv marks elements already used as pivots.
		int[] ipiv = new int[k];

		char[] id_row = createGFMatrix(1, k);
		char[] temp_row = createGFMatrix(1, k);

		for (int col = 0; col < k; col++)
		{
			/*
			 * Zeroing column 'col', look for a non-zero element.
			 * First try on the diagonal, if it fails, look elsewhere.
			 */
			int irow = -1;
			int icol = -1;
			boolean foundPiv = false;

			if (ipiv[col] != 1 && src[col * k + col] != 0)
			{
				irow = col;
				icol = col;
				foundPiv = true;
			}
			if (!foundPiv)
			{
				loop1:for (int row = 0; row < k; row++)
				{
					if (ipiv[row] != 1)
					{
						for (int ix = 0; ix < k; ix++)
						{
							if (ipiv[ix] == 0)
							{
								if (src[row * k + ix] != 0)
								{
									irow = row;
									icol = ix;
									foundPiv = true;
									break loop1;
								}
							}
							else if (ipiv[ix] > 1)
							{
								throw new IllegalArgumentException
									("singular matrix");
							}
						}
					}
				}
			}

			// redundant??? I'm too lazy to figure it out -Justin
			if (!foundPiv && icol == -1)
			{
				throw new IllegalArgumentException("XXX pivot not found!");
			}

			// Ok, we've found a pivot by this point, so we can set the
			// foundPiv variable back to false.  The reason that this is
			// so shittily laid out is that the original code had goto's :(
			foundPiv = false;

			ipiv[icol] = ipiv[icol] + 1;
			/*
			 * swap rows irow and icol, so afterwards the diagonal
			 * element will be correct. Rarely done, not worth
			 * optimizing.
			 */
			if (irow != icol)
			{
				for (int ix = 0; ix < k; ix++)
				{
					// swap 'em
					char tmp = src[irow * k + ix];
					src[irow * k + ix] = src[icol * k + ix];
					src[icol * k + ix] = tmp;
				}
			}
			indxr[col] = irow;
			indxc[col] = icol;

			int pivotRowPos = icol * k;
			char c = src[pivotRowPos + icol];
			if (c == 0)
			{
				throw new IllegalArgumentException("singular matrix 2");
			}
			if (c != 1)
			{ /* otherwhise this is a NOP */
				/*
				 * this is done often , but optimizing is not so
				 * fruitful, at least in the obvious ways (unrolling)
				 */
				c = inverse[c];
				src[pivotRowPos + icol] = 1;
				for (int ix = 0; ix < k; ix++)
				{
					src[pivotRowPos + ix] = mul(c, src[pivotRowPos + ix]);
				}
			}
			/*
			 * from all rows, remove multiples of the selected row
			 * to zero the relevant entry (in fact, the entry is not zero
			 * because we know it must be zero).
			 * (Here, if we know that the pivotRowPos is the identity,
			 * we can optimize the addMul).
			 */
			id_row[icol] = 1;
			if (!ByteArrayUtil.equal(src, pivotRowPos, id_row, 0, k))
			{
				for (int p = 0, ix = 0; ix < k; ix++, p += k)
				{
					if (ix != icol)
					{
						c = src[p + icol];
						src[p + icol] = 0;
						addMul(src, p, src, pivotRowPos, c, k);
					}
				}
			}
			id_row[icol] = 0;
		} // done all columns

		for (int col = k - 1; col >= 0; col--)
		{
			if (indxr[col] < 0 || indxr[col] >= k)
			{
				System.err.println("AARGH, indxr[col] " + indxr[col]);
			}
			else if (indxc[col] < 0 || indxc[col] >= k)
			{
				System.err.println("AARGH, indxc[col] " + indxc[col]);
			}
			else
			{
				if (indxr[col] != indxc[col])
				{
					for (int row = 0; row < k; row++)
					{
						// swap 'em
						char tmp = src[row * k + indxc[col]];
						src[row * k + indxc[col]] = src[row * k + indxr[col]];
						src[row * k + indxr[col]] = tmp;
					}
				}
			}
		}
	}

	/*
	 * fast code for inverting a vandermonde matrix.
	 * XXX NOTE: It assumes that the matrix
	 * is not singular and _IS_ a vandermonde matrix. Only uses
	 * the second column of the matrix, containing the p_i's.
	 *
	 * Algorithm borrowed from "Numerical recipes in C" -- sec.2.8, but
	 * largely revised for my purposes.
	 * p = coefficients of the matrix (p_i)
	 * q = values of the polynomial (known)
	 */

	public final void invertVandermonde(char[] src, int k)
	{

		if (k == 1)
		{ // degenerate case, matrix must be p^0 = 1
			return;
		}

		/*
		 * c holds the coefficient of P(x) = Prod (x - p_i), i=0..k-1
		 * b holds the coefficient for the matrix inversion
		 */
		char[] c = createGFMatrix(1, k);
		char[] b = createGFMatrix(1, k);
		char[] p = createGFMatrix(1, k);

		for (int j = 1, i = 0; i < k; i++, j += k)
		{
			c[i] = 0;
			p[i] = src[j]; /* p[i] */
		}
		/*
		 * construct coeffs. recursively. We know c[k] = 1 (implicit)
		 * and start P_0 = x - p_0, then at each stage multiply by
		 * x - p_i generating P_i = x P_{i-1} - p_i P_{i-1}
		 * After k steps we are done.
		 */
		c[k - 1] = p[0]; /* really -p(0), but x = -x in GF(2^m) */
		for (int i = 1; i < k; i++)
		{
			char p_i = p[i]; /* see above comment */
			for (int j = k - 1 - (i - 1); j < k - 1; j++)
			{
				c[j] ^= mul(p_i, c[j + 1]);
			}
			c[k - 1] ^= p_i;
		}

		for (int row = 0; row < k; row++)
		{
			/*
			 * synthetic division etc.
			 */
			char xx = p[row];
			char t = 1;
			b[k - 1] = 1; /* this is in fact c[k] */
			for (int i = k - 2; i >= 0; i--)
			{
				b[i] = (char) (c[i + 1] ^ mul(xx, b[i + 1]));
				t = (char) (mul(xx, t) ^ b[i]);
			}
			for (int col = 0; col < k; col++)
			{
				src[col * k + row] = mul(inverse[t], b[col]);
			}
		}
	}

	public final char[] createEncodeMatrix(int k, int n)
	{
		if (k > gfSize + 1 || n > gfSize + 1 ||
			k > n)
		{
			throw new IllegalArgumentException
				("Invalid parameters n=" + n + ",k=" + k + ",gfSize=" +
				 gfSize);
		}

		char[] encMatrix = createGFMatrix(n, k);

		/*
		 * The encoding matrix is computed starting with a Vandermonde matrix,
		 * and then transforming it into a systematic matrix.
		 *
		 * fill the matrix with powers of field elements, starting from 0.
		 * The first row is special, cannot be computed with exp. table.
		 */
		char[] tmpMatrix = createGFMatrix(n, k);

		tmpMatrix[0] = 1;
		// first row should be 0's, fill in the rest.
		for (int pos = k, row = 0; row < n - 1; row++, pos += k)
		{
			for (int col = 0; col < k; col++)
			{
				tmpMatrix[pos + col] = gf_exp[modnn
					(row * col)];
			}
		}

		/*
		 * quick code to build systematic matrix: invert the top
		 * k*k vandermonde matrix, multiply right the bottom n-k rows
		 * by the inverse, and construct the identity matrix at the top.
		 */
		// much faster than invertMatrix
		invertVandermonde(tmpMatrix, k);
		matMul(tmpMatrix, k * k, tmpMatrix, 0, encMatrix, k * k, n - k,
			   k, k);

		/*
		 * the upper matrix is I so do not bother with a slow multiply
		 */
		ByteArrayUtil.bzero(encMatrix, 0, k * k);
		for (int i = 0, col = 0; col < k; col++, i += k + 1)
		{
			encMatrix[i] = 1;
		}

		return encMatrix;
	}

	/**
	 * createDecodeMatrix constructs the encoding matrix given the
	 * indexes.
	 */
	protected final char[] createDecodeMatrix(char[] encMatrix, int[] index,
											  int k, int n)
	{

		char[] matrix = createGFMatrix(k, k);
		for (int i = 0, pos = 0; i < k; i++, pos += k)
		{
			System.arraycopy(encMatrix, index[i] * k, matrix, pos, k);
		}

		invertMatrix(matrix, k);

		return matrix;
	}
}
