Direct operations now working properly, tested up to 16 bits.
This commit is contained in:
parent
30b2a58f3d
commit
a6ab689d84
145
galois.py
145
galois.py
@ -8,7 +8,7 @@
|
||||
# code.
|
||||
#
|
||||
|
||||
import random, copy
|
||||
import random
|
||||
|
||||
class GaloisException(Exception):
|
||||
def __init__ (self, value):
|
||||
@ -23,9 +23,13 @@ class GaloisNumber:
|
||||
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 __init__ (self, x, value=0):
|
||||
if isinstance(x, GaloisNumber):
|
||||
self.field = x.field
|
||||
self.value = x.value
|
||||
else:
|
||||
self.field = x
|
||||
self.assign (value)
|
||||
|
||||
def assign (self, v):
|
||||
'''
|
||||
@ -41,26 +45,38 @@ class GaloisNumber:
|
||||
raise GaloisException ("Field elements from different fields")
|
||||
return GaloisNumber (self.field, self.value ^ other.value)
|
||||
|
||||
def __iadd__ (self, other):
|
||||
if self.field != other.field:
|
||||
raise GaloisException ("Field elements from different fields")
|
||||
self.value ^= other.value
|
||||
|
||||
def __sub__ (self, other):
|
||||
return self + other
|
||||
|
||||
def __isub__ (self, other):
|
||||
self += other
|
||||
|
||||
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)
|
||||
return GaloisNumber (self)
|
||||
|
||||
def __mul__ (self, other):
|
||||
if self.field != other.field:
|
||||
raise GaloisException ("Field elements from different fields")
|
||||
return self.field.multiply (self, other)
|
||||
|
||||
def __imul__ (self, other):
|
||||
if self.field != other.field:
|
||||
raise GaloisException ("Field elements from different fields")
|
||||
self.value = self.field.direct_multiply (self.value, other.value)
|
||||
|
||||
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")
|
||||
@ -71,7 +87,8 @@ class GaloisNumber:
|
||||
|
||||
class GaloisFieldLog:
|
||||
'''
|
||||
Pure python implementation of Galois (finite) field arithmetic routines.
|
||||
Pure python implementation of Galois (finite) field arithmetic routines using log/antilog
|
||||
tables.
|
||||
|
||||
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.
|
||||
@ -146,11 +163,111 @@ class GaloisFieldLog:
|
||||
assert product / vb == v, "Multiplication failed for {} * {}".format(v.value,vb.value)
|
||||
return True
|
||||
|
||||
class GaloisFieldDirect:
|
||||
'''
|
||||
Pure python implementation of Galois (finite) field arithmetic routines using direct
|
||||
arithmetic (no log tables).
|
||||
|
||||
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 direct arithmetic. No log tables or inverses to
|
||||
precalculate, since the field might be too large to store them
|
||||
'''
|
||||
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
|
||||
|
||||
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):
|
||||
return GaloisNumber (self, self.direct_multiply (v1.value, v2.value))
|
||||
|
||||
def direct_multiply (self, a, b):
|
||||
# Multiplication is commutative, and it's faster if we use the smaller value as the
|
||||
# multiplier since we can exit the while loop sooner.
|
||||
if b > a:
|
||||
a, b = b, a
|
||||
if a == 0:
|
||||
result = 0
|
||||
else:
|
||||
result = a if b & 1 else 0
|
||||
tmp = a
|
||||
b >>= 1
|
||||
while b != 0:
|
||||
a <<= 1
|
||||
if a >= self.size:
|
||||
a ^= self.prim
|
||||
if b & 1:
|
||||
result ^= a
|
||||
b >>= 1
|
||||
return result
|
||||
|
||||
def invert (self, v):
|
||||
'''
|
||||
Calculate inverse(v) by computing v^(field_size-2).
|
||||
This is just v^2 * v^4 ... v^(field_size / 2), so calculation time is proportional
|
||||
to field width in bits.
|
||||
'''
|
||||
if v.value == 0:
|
||||
return GaloisNumber(self, 0)
|
||||
elif v.value == 1:
|
||||
return GaloisNumber (self, 1)
|
||||
inv = 1
|
||||
sq = v.value
|
||||
for i in range (1, self.bits):
|
||||
sq = self.direct_multiply (sq, sq)
|
||||
inv = self.direct_multiply (inv, sq)
|
||||
return GaloisNumber (self, inv)
|
||||
|
||||
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)
|
||||
for width in GaloisFieldDirect.field_widths:
|
||||
field = GaloisFieldDirect (width)
|
||||
g0 = GaloisNumber (field, 5)
|
||||
g1 = GaloisNumber (field, 7)
|
||||
print g0 + g1
|
||||
if field.self_test ():
|
||||
print "{0} bit field (direct) passed!".format (width)
|
||||
for width in GaloisFieldLog.field_widths:
|
||||
field = GaloisFieldLog (width)
|
||||
if field.self_test ():
|
||||
print "{0} bit field (log) passed!".format (width)
|
||||
g0 = GaloisNumber (field, 5)
|
||||
g1 = GaloisNumber (field, 7)
|
||||
print g0 + g1
|
||||
|
Loading…
Reference in New Issue
Block a user