Add Galois class.

This commit is contained in:
2019-02-11 01:36:33 +00:00
parent fd9cd51a8e
commit e0405a0218
2 changed files with 239 additions and 0 deletions

View File

@@ -38,6 +38,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Galois.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>

View 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();
}
}
}