Initial version of Galois field arithmetic in python. This uses log tables.
This commit is contained in:
commit
93c2329d06
156
galois.py
Executable file
156
galois.py
Executable file
@ -0,0 +1,156 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
#
|
||||||
|
# Native python routines for Galois field calculations.
|
||||||
|
#
|
||||||
|
# These calculations can be done much faster in C/C++ (and with vectorization), but
|
||||||
|
# this code helps illustrate how Galois field math works. Also, it's helpful if
|
||||||
|
# you want to do small amounts of calculation without working with native C/C++
|
||||||
|
# code.
|
||||||
|
#
|
||||||
|
|
||||||
|
import random, copy
|
||||||
|
|
||||||
|
class GaloisException(Exception):
|
||||||
|
def __init__ (self, value):
|
||||||
|
self.value = value
|
||||||
|
def __str__ (self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
|
class GaloisNumber:
|
||||||
|
'''
|
||||||
|
Class to represent a number in a Galois (finite) field.
|
||||||
|
|
||||||
|
The class supports "normal" syntax for the addition, subtraction, multiplication,
|
||||||
|
division, additive inverse (-), and multiplicative inverse (~) operations.
|
||||||
|
'''
|
||||||
|
def __init__ (self, field, value=0):
|
||||||
|
self.field = field
|
||||||
|
self.assign (value)
|
||||||
|
|
||||||
|
def assign (self, v):
|
||||||
|
'''
|
||||||
|
Assign a new integer value to this Galois field number. The number must be valid in
|
||||||
|
the field with which the GaloisNumber instance was defined.
|
||||||
|
'''
|
||||||
|
if v > self.field.size:
|
||||||
|
raise GaloisException ("Value {0} is outside field".format (value))
|
||||||
|
self.value = v
|
||||||
|
|
||||||
|
def __add__ (self, other):
|
||||||
|
if self.field != other.field:
|
||||||
|
raise GaloisException ("Field elements from different fields")
|
||||||
|
return GaloisNumber (self.field, self.value ^ other.value)
|
||||||
|
|
||||||
|
def __invert__ (self):
|
||||||
|
return self.field.invert (self)
|
||||||
|
|
||||||
|
def __neg__ (self):
|
||||||
|
# We want a shallow copy so that the field reference remains the same
|
||||||
|
return copy.copy (self)
|
||||||
|
|
||||||
|
def __mul__ (self, other):
|
||||||
|
if self.field != other.field:
|
||||||
|
raise GaloisException ("Field elements from different fields")
|
||||||
|
return self.field.multiply (self, other)
|
||||||
|
|
||||||
|
def __div__ (self, other):
|
||||||
|
if self.field != other.field:
|
||||||
|
raise GaloisException ("Field elements from different fields")
|
||||||
|
return self.field.divide (self, other)
|
||||||
|
|
||||||
|
def __sub__ (self, other):
|
||||||
|
return self + other
|
||||||
|
|
||||||
|
def __eq__ (self, other):
|
||||||
|
if self.field != other.field:
|
||||||
|
raise GaloisException ("Field elements from different fields")
|
||||||
|
return self.value == other.value
|
||||||
|
|
||||||
|
def __repr__ (self):
|
||||||
|
return self.field.fmt (self.value)
|
||||||
|
|
||||||
|
class GaloisFieldLog:
|
||||||
|
'''
|
||||||
|
Pure python implementation of Galois (finite) field arithmetic routines.
|
||||||
|
|
||||||
|
There only needs to be one instantiation of the field for a given set of parameters,
|
||||||
|
but elements from different field instances with the same parameters may be mixed.
|
||||||
|
'''
|
||||||
|
field_widths = (4, 8, 12, 16)
|
||||||
|
poly_defaults = {4: 0x13, 8: 0x11d, 12:0x1053, 16: 0x1100b}
|
||||||
|
multiply_test_size = 10000
|
||||||
|
def __init__ (self, bits, primitive_polynomial = None, repr_prefix = 'G', alpha = 1):
|
||||||
|
'''
|
||||||
|
Create a Galois field using log/antilog tables for arithmetic.
|
||||||
|
'''
|
||||||
|
if bits not in self.field_widths:
|
||||||
|
raise GaloisException ("Field widths supported: {0}".format (self.field_widths))
|
||||||
|
self.bits = bits
|
||||||
|
self.size = (1 << bits)
|
||||||
|
self.prim = self.poly_defaults[bits] if not primitive_polynomial else primitive_polynomial
|
||||||
|
self.value_format = repr_prefix + '{:0>' + str(bits / 4) + 'x}'
|
||||||
|
self.alpha = alpha
|
||||||
|
# Set up the log and anti-log tables
|
||||||
|
self.log_tbl = [0] * self.size
|
||||||
|
self.antilog_tbl = [0] * (self.size - 1)
|
||||||
|
b = 1
|
||||||
|
for i in range (self.size - 1):
|
||||||
|
self.log_tbl[b] = i
|
||||||
|
self.antilog_tbl[i] = b
|
||||||
|
b <<= 1
|
||||||
|
if b >= self.size:
|
||||||
|
b ^= self.prim
|
||||||
|
|
||||||
|
def __eq__ (self, other):
|
||||||
|
return self.bits == other.bits and self.prim == other.prim and self.alpha == other.alpha
|
||||||
|
|
||||||
|
def fmt (self, v):
|
||||||
|
return self.value_format.format (v)
|
||||||
|
|
||||||
|
def multiply (self, v1, v2):
|
||||||
|
a = v1.value
|
||||||
|
b = v2.value
|
||||||
|
if a == 0 or b == 0:
|
||||||
|
return GaloisNumber (self, 0)
|
||||||
|
return GaloisNumber (self, self.antilog_tbl[(self.log_tbl[a] + self.log_tbl[b]) %
|
||||||
|
(self.size - 1)])
|
||||||
|
|
||||||
|
def invert (self, v):
|
||||||
|
if v.value == 0:
|
||||||
|
return GaloisNumber(self, 0)
|
||||||
|
elif v.value == 1:
|
||||||
|
return GaloisNumber (self, 1)
|
||||||
|
else:
|
||||||
|
return GaloisNumber (self, self.antilog_tbl[self.size - 1 - self.log_tbl[v.value]])
|
||||||
|
|
||||||
|
def divide (self, v1, v2):
|
||||||
|
return self.multiply (v1, self.invert(v2))
|
||||||
|
|
||||||
|
def self_test (self):
|
||||||
|
mul_identity = GaloisNumber (self, 1)
|
||||||
|
v = GaloisNumber (self)
|
||||||
|
g_0 = GaloisNumber (self, 0)
|
||||||
|
g_1 = GaloisNumber (self, 1)
|
||||||
|
for i in range (self.size):
|
||||||
|
v.assign (i)
|
||||||
|
if i == 0: continue
|
||||||
|
assert v * ~v == mul_identity, "Multiplicative inverse failed at {}".format (i)
|
||||||
|
assert g_0 - v == -v, "Additive inverse failed at {}".format (i)
|
||||||
|
assert v * g_1 == v, "Multiplicative identity failed at {}".format (i)
|
||||||
|
vb = GaloisNumber (self)
|
||||||
|
for a in range (1, self.multiply_test_size):
|
||||||
|
v.assign (random.randint (1, self.size - 1))
|
||||||
|
vb.assign (random.randint (1, self.size - 1))
|
||||||
|
product = v * vb
|
||||||
|
assert product / v == vb, "Multiplication failed for {} * {}".format(v.value,vb.value)
|
||||||
|
assert product / vb == v, "Multiplication failed for {} * {}".format(v.value,vb.value)
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
for width in GaloisFieldLog.field_widths:
|
||||||
|
field = GaloisFieldLog (width)
|
||||||
|
if field.self_test ():
|
||||||
|
print "{0} bit field passed!".format (width)
|
||||||
|
g0 = GaloisNumber (field, 5)
|
||||||
|
g1 = GaloisNumber (field, 7)
|
||||||
|
print g0 + g1
|
Loading…
Reference in New Issue
Block a user