mirror of
https://github.com/claunia/Claunia.ReedSolomon.git
synced 2025-12-16 19:24:45 +00:00
Add Galois class.
This commit is contained in:
@@ -38,6 +38,7 @@
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Galois.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
238
Claunia.ReedSolomon/Galois.cs
Normal file
238
Claunia.ReedSolomon/Galois.cs
Normal file
@@ -0,0 +1,238 @@
|
||||
/**
|
||||
* 8-bit Galois Field
|
||||
*
|
||||
* Copyright 2015, Backblaze, Inc. All rights reserved.
|
||||
* Copyright © 2019 Natalia Portillo
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Claunia.ReedSolomon
|
||||
{
|
||||
/// <summary>
|
||||
/// 8-bit Galois Field This class implements multiplication, division, addition, subtraction, and exponentiation.
|
||||
/// The multiplication operation is in the inner loop of erasure coding, so it's been optimized. Having the class be
|
||||
/// "final" helps a little, and having the EXP_TABLE repeat the data, so there's no need to bound the sum of two
|
||||
/// logarithms to 255 helps a lot.
|
||||
/// </summary>
|
||||
public sealed class Galois
|
||||
{
|
||||
/// <summary>The number of elements in the field.</summary>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public const int FIELD_SIZE = 256;
|
||||
|
||||
/// <summary>
|
||||
/// The polynomial used to generate the logarithm table. There are a number of polynomials that work to generate a
|
||||
/// Galois field of 256 elements. The choice is arbitrary, and we just use the first one. The possibilities are: 29,
|
||||
/// 43, 45, 77, 95, 99, 101, 105, 113, 135, 141, 169, 195, 207, 231, and 245.
|
||||
/// </summary>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public const int GENERATING_POLYNOMIAL = 29;
|
||||
|
||||
/// <summary>
|
||||
/// Mapping from members of the Galois Field to their integer logarithms. The entry for 0 is meaningless because
|
||||
/// there is no log of 0. This array is shorts, not bytes, so that they can be used directly to index arrays without
|
||||
/// casting. The values (except the non-value at index 0) are all really bytes, so they range from 0 to 255. This table
|
||||
/// was generated by java_tables.py, and the unit tests check it against the Java implementation.
|
||||
/// </summary>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public static readonly short[] LOG_TABLE =
|
||||
{
|
||||
-1, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28,
|
||||
193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, 240, 18, 130, 69, 29,
|
||||
181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, 191, 139, 98, 102, 221, 48, 253,
|
||||
226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, 150, 219, 189, 241, 210, 19, 92, 131, 56, 70,
|
||||
64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121,
|
||||
43, 78, 212, 229, 172, 115, 243, 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197,
|
||||
254, 24, 227, 165, 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149,
|
||||
188, 207, 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, 83,
|
||||
71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, 108, 161, 59,
|
||||
82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, 169, 160, 81, 11, 245, 22,
|
||||
235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, 214, 244, 234, 168, 80, 88, 175
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Inverse of the logarithm table. Maps integer logarithms to members of the field. There is no entry for 255
|
||||
/// because the highest log is 254. This table was generated by java_tables.py.
|
||||
/// </summary>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
static readonly byte[] EXP_TABLE =
|
||||
{
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143,
|
||||
3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70,
|
||||
140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101,
|
||||
202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113,
|
||||
226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237,
|
||||
199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132,
|
||||
21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99,
|
||||
198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55,
|
||||
110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
|
||||
89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247,
|
||||
243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142,
|
||||
|
||||
// Repeat the table a second time, so multiply()
|
||||
// does not have to check bounds.
|
||||
1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, 234, 201, 143,
|
||||
3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, 119, 238, 193, 159, 35, 70,
|
||||
140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, 95, 190, 97, 194, 153, 47, 94, 188, 101,
|
||||
202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113,
|
||||
226, 217, 175, 67, 134, 17, 34, 68, 136, 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237,
|
||||
199, 147, 59, 118, 236, 197, 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132,
|
||||
21, 42, 84, 168, 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99,
|
||||
198, 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, 55,
|
||||
110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, 83, 166, 81, 162,
|
||||
89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, 144, 61, 122, 244, 245, 247,
|
||||
243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, 131, 27, 54, 108, 216, 173, 71, 142
|
||||
};
|
||||
/// <summary>
|
||||
/// A multiplication table for the Galois field. Using this table is an alternative to using the multiply()
|
||||
/// method, which uses log/exp table lookups.
|
||||
/// </summary>
|
||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||
public static readonly byte[][] MULTIPLICATION_TABLE = GenerateMultiplicationTable();
|
||||
|
||||
/// <summary>Adds two elements of the field. If you're in an inner loop, you should inline this function: it's just XOR.</summary>
|
||||
public static byte Add(byte a, byte b) => (byte)(a ^ b);
|
||||
|
||||
/// <summary>Inverse of addition. If you're in an inner loop, you should inline this function: it's just XOR.</summary>
|
||||
public static byte Subtract(byte a, byte b) => (byte)(a ^ b);
|
||||
|
||||
/// <summary>Multiplies two elements of the field.</summary>
|
||||
public static byte Multiply(byte a, byte b)
|
||||
{
|
||||
if(a == 0 ||
|
||||
b == 0)
|
||||
return 0;
|
||||
|
||||
int logA = LOG_TABLE[a & 255];
|
||||
int logB = LOG_TABLE[b & 255];
|
||||
int logResult = logA + logB;
|
||||
|
||||
return EXP_TABLE[logResult];
|
||||
}
|
||||
|
||||
/// <summary>Inverse of multiplication.</summary>
|
||||
public static byte Divide(byte a, byte b)
|
||||
{
|
||||
if(a == 0)
|
||||
return 0;
|
||||
|
||||
if(b == 0)
|
||||
throw new ArgumentException("Argument 'divisor' is 0");
|
||||
|
||||
int logA = LOG_TABLE[a & 255];
|
||||
int logB = LOG_TABLE[b & 255];
|
||||
int logResult = logA - logB;
|
||||
|
||||
if(logResult < 0)
|
||||
logResult += 255;
|
||||
|
||||
return EXP_TABLE[logResult];
|
||||
}
|
||||
|
||||
/// <summary>Computes a**n. The result will be the same as multiplying a times itself n times.</summary>
|
||||
/// <param name="a">A member of the field.</param>
|
||||
/// <param name="n">A plain-old integer.</param>
|
||||
/// <returns>The result of multiplying a by itself n times.</returns>
|
||||
public static byte Exp(byte a, int n)
|
||||
{
|
||||
if(n == 0)
|
||||
return 1;
|
||||
|
||||
if(a == 0)
|
||||
return 0;
|
||||
|
||||
int logA = LOG_TABLE[a & 255];
|
||||
int logResult = logA * n;
|
||||
|
||||
while(255 <= logResult)
|
||||
logResult -= 255;
|
||||
|
||||
return EXP_TABLE[logResult];
|
||||
}
|
||||
|
||||
/// <summary>Generates a logarithm table given a starting polynomial.</summary>
|
||||
public static short[] GenerateLogTable(int polynomial)
|
||||
{
|
||||
short[] result = new short[FIELD_SIZE];
|
||||
|
||||
for(int i = 0; i < FIELD_SIZE; i++)
|
||||
result[i] = -1; // -1 means "not set"
|
||||
|
||||
int b = 1;
|
||||
|
||||
for(int log = 0; log < FIELD_SIZE - 1; log++)
|
||||
{
|
||||
if(result[b] != -1)
|
||||
throw new Exception("BUG: duplicate logarithm (bad polynomial?)");
|
||||
|
||||
result[b] = (short)log;
|
||||
b = b << 1;
|
||||
|
||||
if(FIELD_SIZE <= b)
|
||||
b = (b - FIELD_SIZE) ^ polynomial;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>Generates the inverse log table.</summary>
|
||||
public static byte[] GenerateExpTable(short[] logTable)
|
||||
{
|
||||
byte[] result = new byte [FIELD_SIZE * 2 - 2];
|
||||
|
||||
for(int i = 1; i < FIELD_SIZE; i++)
|
||||
{
|
||||
int log = logTable[i];
|
||||
result[log] = (byte)i;
|
||||
result[log + FIELD_SIZE - 1] = (byte)i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a multiplication table as an array of byte arrays. To get the result of multiplying a and b:
|
||||
/// MULTIPLICATION_TABLE[a][b]
|
||||
/// </summary>
|
||||
public static byte[][] GenerateMultiplicationTable()
|
||||
{
|
||||
byte[][] result = new byte [256][];
|
||||
|
||||
for(int i = 0; i < 256; i++)
|
||||
result[i] = new byte[256];
|
||||
|
||||
for(int a = 0; a < FIELD_SIZE; a++)
|
||||
{
|
||||
for(int b = 0; b < FIELD_SIZE; b++)
|
||||
result[a][b] = Multiply((byte)a, (byte)b);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all polynomials that can be used to generate the field. This is never used in the code; it's
|
||||
/// just here for completeness.
|
||||
/// </summary>
|
||||
public static int[] AllPossiblePolynomials()
|
||||
{
|
||||
List<int> result = new List<int>();
|
||||
|
||||
for(int i = 0; i < FIELD_SIZE; i++)
|
||||
try
|
||||
{
|
||||
GenerateLogTable(i);
|
||||
result.Add(i);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// this one didn't work
|
||||
}
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user