#!/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