/// <summary>Encodes a data block by adding <see cref="EccLength"/> error-correction symbols to it.</summary> /// <param name="source">The array containing the data block to encode</param> /// <param name="srcIndex">The index within <paramref name="source"/> at which the data block begins</param> /// <param name="length">The length of the data block. This can be at most 255-<see cref="EccLength"/></param> /// <param name="destination">The array into which the encoded block will be written</param> /// <param name="destIndex">The index at which the encoded block will be written. The array must have at least /// <paramref name="length"/>+<see cref="EccLength"/> bytes available at the index. /// </param> /// <returns>Returns the number of bytes written to the <paramref name="destination"/> array, which will equal /// <paramref name="length"/>+<see cref="EccLength"/>. /// </returns> public int Encode(byte[] source, int srcIndex, int length, byte[] destination, int destIndex) { Utility.ValidateRange(source, srcIndex, length); Utility.ValidateRange(destination, destIndex, length + EccLength); if (source.Length > 255 - EccLength) { throw new ArgumentException("The source length can be at most 255 - EccLength."); } if (EccLength != 0) { int[] coef = new int[length + EccLength]; for (int i = 0; i < length; i++) { coef[i + EccLength] = source[i + srcIndex]; } GF2pPolynomial code = new GF2pPolynomial(Field, coef, false) % Prime; for (int i = 0; i < code.data.Length; i++) { destination[i + destIndex] = (byte)code.data[i]; } } Array.Copy(source, srcIndex, destination, destIndex + EccLength, length); return(length + EccLength); }
public void T05_PolynomialMultiplication() { GF2pField field = new GF2pField(8); GF2pPolynomial a = new GF2pPolynomial(field, 0, 4, 17, 0, 9), b = new GF2pPolynomial(field, 19, 11, 0, 100); Assert.AreEqual(new GF2pPolynomial(field, 0, 76, 18, 187, 6, 57, 0, 99), a * b); Assert.AreEqual(a * b, b * a); Assert.AreEqual(new GF2pPolynomial(field, 0), new GF2pPolynomial(field, 0) * a); Assert.AreEqual(new GF2pPolynomial(field, 0), default(GF2pPolynomial) * b); Assert.AreEqual(new GF2pPolynomial(field, 121, 49, 0, 33), b * 7); Assert.AreEqual(0, GF2pPolynomial.MultiplyAt(a, b, 0)); Assert.AreEqual(76, GF2pPolynomial.MultiplyAt(a, b, 1)); Assert.AreEqual(18, GF2pPolynomial.MultiplyAt(a, b, 2)); Assert.AreEqual(187, GF2pPolynomial.MultiplyAt(a, b, 3)); Assert.AreEqual(6, GF2pPolynomial.MultiplyAt(a, b, 4)); Assert.AreEqual(57, GF2pPolynomial.MultiplyAt(a, b, 5)); Assert.AreEqual(0, GF2pPolynomial.MultiplyAt(a, b, 6)); Assert.AreEqual(99, GF2pPolynomial.MultiplyAt(a, b, 7)); Assert.AreEqual(0, GF2pPolynomial.MultiplyAt(a, b, 8)); Assert.AreEqual(0, GF2pPolynomial.MultiplyAt(a, b, 9)); Assert.AreEqual(0, a.MultiplyAt(b, 0)); Assert.AreEqual(76, a.MultiplyAt(b, 1)); Assert.AreEqual(18, a.MultiplyAt(b, 2)); Assert.AreEqual(187, a.MultiplyAt(b, 3)); }
/// <include file="documentation.xml" path="/Math/ReedSolomon/DecodeBool/*"/> public int Decode(byte[] source, int srcIndex, int length, byte[] destination, int destIndex, int[] errorPositions, bool allErrorsKnown = false) { if (length < EccLength) { throw new ArgumentOutOfRangeException("length", "must be at least EccLength"); } Utility.ValidateRange(source, srcIndex, length); Utility.ValidateRange(destination, destIndex, length - EccLength); GF2pPolynomial message = ToPolynomial(source, srcIndex, length), syndromes = CalculateSyndromes(message, EccLength); if (syndromes.IsZero) { Array.Copy(source, srcIndex + EccLength, destination, destIndex, length - EccLength); return(length - EccLength); } GF2pPolynomial locator = default(GF2pPolynomial), evaluator = default(GF2pPolynomial); if (errorPositions != null && errorPositions.Length != 0) { locator = CalculateErasureLocator(errorPositions, Field); evaluator = CalculateErrorEvaluator(syndromes, locator); } else if (allErrorsKnown) { return(-1); } if (!allErrorsKnown) { BerlekampMassey(syndromes, locator, evaluator, errorPositions == null ? 0 : errorPositions.Length, out locator, out evaluator); } int[] allErrorPositions = FindErrors(locator, length); if (allErrorPositions == null || allErrorPositions.Length > EccLength) { return(-1); } // now return the corrected data. copy the source data to the destination Array.Copy(source, srcIndex + EccLength, destination, destIndex, length - EccLength); int correctionStart = 0; // skip connections to ECC codes, since we don't return them while (correctionStart < allErrorPositions.Length && allErrorPositions[correctionStart] < EccLength) { correctionStart++; } if (correctionStart < allErrorPositions.Length) // if there are corrections to the data... { int[] errorMagnitudes = Forney(evaluator, allErrorPositions, correctionStart); // apply them for (int i = 0; i < errorMagnitudes.Length; i++) { destination[destIndex + allErrorPositions[i + correctionStart] - EccLength] ^= (byte)errorMagnitudes[i]; } } return(length - EccLength); }
public void T02_PolynomialBasics() { GF2pField field = new GF2pField(8); TestHelpers.TestException <ArgumentNullException>(() => new GF2pPolynomial(null, 0)); TestHelpers.TestException <ArgumentNullException>(() => new GF2pPolynomial(null, new int[0])); TestHelpers.TestException <ArgumentOutOfRangeException>("within the field", () => new GF2pPolynomial(field, -1)); TestHelpers.TestException <ArgumentOutOfRangeException>("within the field", () => new GF2pPolynomial(field, 256)); TestHelpers.TestException <ArgumentOutOfRangeException>("within the field", () => new GF2pPolynomial(field, new[] { -1 })); TestHelpers.TestException <ArgumentOutOfRangeException>("within the field", () => new GF2pPolynomial(field, new[] { 256 })); GF2pPolynomial zero = new GF2pPolynomial(field, 0); GF2pPolynomial p = new GF2pPolynomial(field, new[] { 0, 1, 254, 255, 0, 0 }); Assert.AreEqual(0, p[0]); // test the indexer Assert.AreEqual(1, p[1]); Assert.AreEqual(254, p[2]); Assert.AreEqual(255, p[3]); Assert.AreEqual(3, p.Degree); // test various properties Assert.AreEqual(-1, zero.Degree); Assert.AreEqual(field, zero.Field); Assert.AreEqual(field, p.Field); Assert.IsTrue(zero.IsZero); Assert.IsFalse(p.IsZero); Assert.AreEqual(0, zero.Length); Assert.AreEqual(4, p.Length); Assert.IsTrue(p.Equals((object)(new GF2pPolynomial(field, 0, 1) + new GF2pPolynomial(field, 0, 0, 254, 255)))); // test Equals Assert.IsTrue(p.Equals(new GF2pPolynomial(new GF2pField(8), 0, 1, 254, 255))); // check that the field instance can be different Assert.IsFalse(p.Equals(new GF2pPolynomial(new GF2pField(8, 333), 0, 1, 254, 255))); // but it must be compatible Assert.IsFalse(p.Equals(new GF2pPolynomial(field, 0, 1, 3, 4))); Assert.IsTrue(p.Equals(p)); Assert.IsTrue(zero.Equals(zero)); Assert.IsTrue(zero.Equals(default(GF2pPolynomial))); Assert.IsFalse(p.Equals(zero)); Assert.IsFalse(zero.Equals(p)); Assert.AreNotEqual(zero.GetHashCode(), p.GetHashCode()); // test GetHashCode. mostly just make sure it doesn't throw Assert.IsTrue(p == new GF2pPolynomial(field, 0, 1, 254, 255)); Assert.IsFalse(p != new GF2pPolynomial(field, 0, 1, 254, 255)); Assert.IsFalse(p == zero); Assert.IsTrue(p != zero); TestHelpers.AssertArrayEquals(zero.ToArray(), new int[0]); // test ToArray TestHelpers.AssertArrayEquals(p.ToArray(), 0, 1, 254, 255); Assert.AreEqual("0", zero.ToString()); // test ToString and Parse Assert.AreEqual("17 + 2x", new GF2pPolynomial(field, 17, 2).ToString()); Assert.AreEqual("x + 254x^2 + 255x^3", p.ToString()); Assert.AreEqual(zero, GF2pPolynomial.Parse(field, "-0")); Assert.AreEqual(p, GF2pPolynomial.Parse(field, "+165x^3- 90 x^3 + x-254x^2")); Assert.AreEqual(p, p.Truncate(10)); // test Truncate Assert.AreEqual(p, p.Truncate(p.Length)); Assert.AreEqual(new GF2pPolynomial(field, 0, 1, 254), p.Truncate(3)); Assert.AreEqual(new GF2pPolynomial(field, 0, 1), p.Truncate(2)); Assert.AreEqual(new GF2pPolynomial(field, 0), p.Truncate(1)); Assert.AreEqual(new GF2pPolynomial(field, 0), new GF2pPolynomial(field, 1, 2).Truncate(0)); }
static GF2pPolynomial CalculateSyndromes(GF2pPolynomial message, int eccLength) { int[] syndromes = new int[eccLength]; // add a zero at the front to simply later calculations for (int i = 0; i < syndromes.Length; i++) { syndromes[i] = message.Evaluate(message.Field.Exp(i + 1)); // add 1 because we assume fcr == 1 } return(new GF2pPolynomial(message.Field, syndromes, false)); }
static GF2pPolynomial CreatePrimePolynomial(GF2pField field, int symbolCount) { GF2pPolynomial poly = new GF2pPolynomial(field, 1); for (int i = 1; i <= symbolCount; i++) { poly *= new GF2pPolynomial(field, new int[] { field.Exp(i), 1 }, false); // add 1 because fcr == 1 } return(poly); }
public void T03_PolynomialAddition() { GF2pField field = new GF2pField(8); GF2pPolynomial zero = new GF2pPolynomial(field, 0); GF2pPolynomial a = new GF2pPolynomial(field, 0, 1, 2, 3), b = new GF2pPolynomial(field, 5, 6, 7, 8, 9, 10); Assert.AreEqual(new GF2pPolynomial(field, 5 ^ 0, 6 ^ 1, 7 ^ 2, 8 ^ 3, 9, 10), a + b); Assert.AreEqual(new GF2pPolynomial(field, 5 ^ 0, 6 ^ 1, 7 ^ 2, 8 ^ 3, 9, 10), b + a); Assert.AreEqual(new GF2pPolynomial(field, 5 ^ 0, 6 ^ 1, 7 ^ 2, 8 ^ 3, 9, 10), a - b); Assert.AreEqual(new GF2pPolynomial(field, 5 ^ 0, 6 ^ 1, 7 ^ 2, 8 ^ 3, 9, 10), b - a); Assert.AreEqual(a, (a + default(GF2pPolynomial))); Assert.AreEqual(b, (default(GF2pPolynomial) + b)); Assert.AreEqual(a.Field, (a + b).Field); }
static GF2pPolynomial CalculateErasureLocator(int[] positions, GF2pField field) { if (positions == null) { throw new ArgumentNullException(); } GF2pPolynomial locator = new GF2pPolynomial(field, 1); foreach (int position in positions) { locator *= new GF2pPolynomial(field, new int[] { 1, field.Exp(position) }, false); } return(locator); }
static void BerlekampMassey(GF2pPolynomial syndromes, GF2pPolynomial erasureLocator, GF2pPolynomial erasureEvaluator, int erasureCount, out GF2pPolynomial errataLocator, out GF2pPolynomial errataEvaluator) { GF2pPolynomial locator, prevLocator, evaluator, prevEvaluator, a, b; if (erasureCount != 0) { locator = erasureLocator; a = evaluator = erasureEvaluator; } else { evaluator = locator = new GF2pPolynomial(syndromes.Field, 1); a = new GF2pPolynomial(syndromes.Field, 0); } b = locator; int L = 0; for (int i = 0, count = syndromes.Length - erasureCount; i < count; i++) { int k = i + erasureCount, delta = GF2pPolynomial.MultiplyAt(syndromes, locator, k); prevLocator = locator; prevEvaluator = evaluator; GF2pPolynomial shiftA = a << 1, shiftB = b << 1; locator += shiftB * delta; evaluator += shiftA * delta; if (delta == 0 || 2 * L > k + erasureCount + 1) { b = shiftB; a = shiftA; } else { b = prevLocator / delta; a = prevEvaluator / delta; L = k + 1 - L; } } if (evaluator.Length > locator.Length) { evaluator = evaluator.Truncate(locator.Length); } evaluator = CalculateErrorEvaluator(syndromes, locator); errataLocator = locator; errataEvaluator = evaluator; }
static int[] Forney(GF2pPolynomial evaluator, int[] errorPositions, int startIndex) { int[] powers = new int[errorPositions.Length], magnitudes = new int[powers.Length - startIndex]; for (int i = 0; i < powers.Length; i++) { powers[i] = evaluator.Field.Exp(errorPositions[i]); } for (int i = 0; i < magnitudes.Length; i++) { int invPower = evaluator.Field.Invert(powers[i + startIndex]), divisor = 1; for (int ei = 0; ei < powers.Length; ei++) { if (ei != i + startIndex) { divisor = evaluator.Field.Multiply(divisor, evaluator.Field.Multiply(powers[ei], invPower) ^ 1); } } magnitudes[i] = evaluator.Field.Divide(evaluator.Evaluate(invPower), divisor); } return(magnitudes); }
public void T06_PolynomialDivision() { Action <GF2pPolynomial, GF2pPolynomial, GF2pPolynomial, GF2pPolynomial> testDivision = (a, b, q, r) => { Assert.AreEqual(q, a / b); Assert.AreEqual(r, a % b); GF2pPolynomial remainder; Assert.AreEqual(q, a.DivRem(b, out remainder)); Assert.AreEqual(r, remainder); Assert.AreEqual(q, GF2pPolynomial.DivRem(a, b, out remainder)); Assert.AreEqual(r, remainder); }; GF2pField field = new GF2pField(8); GF2pPolynomial x = new GF2pPolynomial(field, 0, 4, 17, 0, 9, 1), y = new GF2pPolynomial(field, 19, 11, 0, 100); testDivision(x, y, new GF2pPolynomial(field, 136, 24, 185), new GF2pPolynomial(field, 237, 0, 112)); testDivision(y, x, new GF2pPolynomial(field, 0), y); x = new GF2pPolynomial(field, 1, 2, 3, 4); testDivision(x, y, new GF2pPolynomial(field, 222), new GF2pPolynomial(field, 31, 195, 3)); testDivision(y, x, new GF2pPolynomial(field, 25), new GF2pPolynomial(field, 10, 57, 43)); }
public void T04_PolynomialShifting() { GF2pField field = new GF2pField(8); GF2pPolynomial p = new GF2pPolynomial(field, 0, 1, 0, 2, 0, 3); Assert.AreEqual(new GF2pPolynomial(field, 1, 0, 2, 0, 3), p >> 1); Assert.AreEqual(new GF2pPolynomial(field, 0, 2, 0, 3), p >> 2); Assert.AreEqual(new GF2pPolynomial(field, 2, 0, 3), p >> 3); Assert.AreEqual(new GF2pPolynomial(field, 0), p >> 6); Assert.AreEqual(new GF2pPolynomial(field, 0), p >> 8); Assert.AreEqual(new GF2pPolynomial(field, 1, 0, 2, 0, 3), p << -1); Assert.AreEqual(new GF2pPolynomial(field, 0, 2, 0, 3), p << -2); Assert.AreEqual(new GF2pPolynomial(field, 2, 0, 3), p << -3); Assert.AreEqual(new GF2pPolynomial(field, 0), p << -6); Assert.AreEqual(new GF2pPolynomial(field, 0), p << -8); Assert.AreEqual(new GF2pPolynomial(field, 0, 0, 1, 0, 2, 0, 3), p << 1); Assert.AreEqual(new GF2pPolynomial(field, 0, 0, 0, 1, 0, 2, 0, 3), p << 2); Assert.AreEqual(new GF2pPolynomial(field, 0, 0, 1, 0, 2, 0, 3), p >> -1); Assert.AreEqual(new GF2pPolynomial(field, 0, 0, 0, 1, 0, 2, 0, 3), p >> -2); }
static int[] FindErrors(GF2pPolynomial locator, int dataLength) { // TODO: ideally we'd use the more efficient Chien's search here rather than this brute-force approach int[] errorPositions = new int[locator.Degree]; int ei = 0; for (int maxValue = (int)(locator.Field.Order - 1), di = 0; di < dataLength; di++) { if (locator.Evaluate(locator.Field.Exp(maxValue - di)) == 0) { if (ei != errorPositions.Length) { errorPositions[ei++] = di; } else { return(null); } } } return(ei == errorPositions.Length ? errorPositions : null); }
static GF2pPolynomial CalculateErrorEvaluator(GF2pPolynomial syndromes, GF2pPolynomial locator) { return((syndromes * locator).Truncate(syndromes.Length) << 1); }