• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lrq3000 / unireedsolomon / 4705024434

pending completion
4705024434

push

github

Stephen L
refactor: require explicit flag to --cythonize (also fixes unit tests on PyPy) + update credits

1 of 314 branches covered (0.32%)

Branch coverage included in aggregate %.

116 of 726 relevant lines covered (15.98%)

0.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

15.54
/src/unireedsolomon/ff.py
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3

4
# Copyright (c) 2010 Andrew Brown <brownan@cs.duke.edu, brownan@gmail.com>
5
# Copyright (c) 2015 Stephen Larroque <LRQ3000@gmail.com>
6
# See LICENSE.txt for license terms
7

8
# For fast software computation in Finite Fields, see the excellent paper: Huang, Cheng, and Lihao Xu. "Fast software implementation of finite field operations." Washington University in St. Louis, Tech. Rep (2003).
9
# to understand the basic mathematical notions behind finite fields, see the excellent tutorial: http://research.swtch.com/field
10

11
from ._compat import _range
2✔
12

13
import array
2✔
14

15
# Galois Field's characteristic, by default, it's GF(2^8) == GF(256)
16
# Note that it's -1 (thus for GF(2^8) it's really 255 and not 256) because this is historically tied to the definition of Reed-Solomon codes: since the 0 and 256 values are impossible, we effectively have only 255 possible values. But later were defined (singly) extended Reed-Solomon codes, which include the 0 and thus 256 values, and then doubly extended Reed-Solomon codes which include the 0 and 256 == infinity.
17
GF2_charac = int(2**8 - 1)
2✔
18
GF2_c_exp = 8
2✔
19

20
# Exponent table for generator=3 and prim=0x11b in GF(2^8)
21
GF2int_exptable = array.array("i", [1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19,
2✔
22
        53, 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34,
23
        102, 170, 229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112,
24
        144, 171, 230, 49, 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104,
25
        184, 211, 110, 178, 205, 76, 212, 103, 169, 224, 59, 77, 215, 98,
26
        166, 241, 8, 24, 40, 120, 136, 131, 158, 185, 208, 107, 189, 220,
27
        127, 129, 152, 179, 206, 73, 219, 118, 154, 181, 196, 87, 249, 16,
28
        48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, 254, 25, 43, 125,
29
        135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, 251, 22,
30
        58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, 195,
31
        94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218,
32
        117, 159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223,
33
        122, 142, 137, 128, 155, 182, 193, 88, 232, 35, 101, 175, 234, 37,
34
        111, 177, 200, 67, 197, 84, 252, 31, 33, 99, 165, 244, 7, 9, 27,
35
        45, 119, 153, 176, 203, 70, 202, 69, 207, 74, 222, 121, 139, 134,
36
        145, 168, 227, 62, 66, 198, 81, 243, 14, 18, 54, 90, 238, 41, 123,
37
        141, 140, 143, 138, 133, 148, 167, 242, 13, 23, 57, 75, 221, 124,
38
        132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1])
39

40
# Logarithm table for the same GF parameters
41
GF2int_logtable = array.array("i", [-1, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, # log(0) is undefined, it should be none, but for performance we use an array, thus we need to set an integer, here we replace None by -1
2✔
42
        3, 100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105,
43
        28, 193, 125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114,
44
        154, 201, 9, 120, 101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130,
45
        69, 53, 147, 218, 142, 150, 143, 219, 189, 54, 208, 206, 148, 19,
46
        92, 210, 241, 64, 70, 131, 56, 102, 221, 253, 48, 191, 6, 139, 98,
47
        179, 37, 226, 152, 34, 136, 145, 16, 126, 110, 72, 195, 163, 182,
48
        30, 66, 58, 107, 40, 84, 250, 133, 61, 186, 43, 121, 10, 21, 155,
49
        159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87, 175, 88, 168,
50
        80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, 44,
51
        215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81,
52
        160, 127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164,
53
        118, 123, 183, 204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161,
54
        108, 170, 85, 41, 157, 151, 178, 135, 144, 97, 190, 220, 252, 188,
55
        149, 207, 205, 55, 63, 91, 209, 83, 57, 132, 60, 65, 162, 109, 71,
56
        20, 42, 158, 93, 86, 242, 211, 171, 68, 17, 146, 217, 35, 32, 46,
57
        137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197,
58
        49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7])
59

60
def rwh_primes1(n):
2✔
61
    # http://stackoverflow.com/questions/2068372/fastest-way-to-list-all-primes-below-n-in-python/3035188#3035188
62
    ''' Returns  a list of primes < n '''
63
    sieve = [True] * (n//2)
×
64
    for i in _range(3,int(n**0.5)+1,2):
×
65
        if sieve[i//2]:
×
66
            sieve[i*i//2::i] = [False] * ((n-i*i-1)//(2*i)+1)
×
67
    return [2] + [2*i+1 for i in _range(1,n//2) if sieve[i]]
×
68

69
def find_prime_polynomials(generator=2, c_exp=8, fast_primes=False, single=False):
2✔
70
    '''Compute the list of prime polynomials for the given generator and galois field characteristic exponent.'''
71
    # fast_primes will output less results but will be significantly faster.
72
    # single will output the first prime polynomial found, so if all you want is to just find one prime polynomial to generate the LUT for Reed-Solomon to work, then just use that.
73

74
    # A prime polynomial (necessarily irreducible) is necessary to reduce the multiplications in the Galois Field, so as to avoid overflows.
75
    # Why do we need a "prime polynomial"? Can't we just reduce modulo 255 (for GF(2^8) for example)? Because we need the values to be unique.
76
    # For example: if the generator (alpha) = 2 and c_exp = 8 (GF(2^8) == GF(256)), then the generated Galois Field (0, 1, α, α^1, α^2, ..., α^(p-1)) will be galois field it becomes 0, 1, 2, 4, 8, 16, etc. However, upon reaching 128, the next value will be doubled (ie, next power of 2), which will give 256. Then we must reduce, because we have overflowed above the maximum value of 255. But, if we modulo 255, this will generate 256 == 1. Then 2, 4, 8, 16, etc. giving us a repeating pattern of numbers. This is very bad, as it's then not anymore a bijection (ie, a non-zero value doesn't have a unique index). That's why we can't just modulo 255, but we need another number above 255, which is called the prime polynomial.
77
    # Why so much hassle? Because we are using precomputed look-up tables for multiplication: instead of multiplying a*b, we precompute alpha^a, alpha^b and alpha^(a+b), so that we can just use our lookup table at alpha^(a+b) and get our result. But just like in our original field we had 0,1,2,...,p-1 distinct unique values, in our "LUT" field using alpha we must have unique distinct values (we don't care that they are different from the original field as long as they are unique and distinct). That's why we need to avoid duplicated values, and to avoid duplicated values we need to use a prime irreducible polynomial.
78

79
    # Here is implemented a bruteforce approach to find all these prime polynomials, by generating every possible prime polynomials (ie, every integers between field_charac+1 and field_charac*2), and then we build the whole Galois Field, and we reject the candidate prime polynomial if it duplicates even one value or if it generates a value above field_charac (ie, cause an overflow).
80
    # Note that this algorithm is slow if the field is too big (above 12), because it's an exhaustive search algorithm. There are probabilistic approaches, and almost surely prime approaches, but there is no determistic polynomial time algorithm to find irreducible monic polynomials. More info can be found at: http://people.mpi-inf.mpg.de/~csaha/lectures/lec9.pdf
81
    # Another faster algorithm may be found at Adleman, Leonard M., and Hendrik W. Lenstra. "Finding irreducible polynomials over finite fields." Proceedings of the eighteenth annual ACM symposium on Theory of computing. ACM, 1986.
82

83
    # Prepare the finite field characteristic (2^p - 1), this also represent the maximum possible value in this field
84
    root_charac = 2 # we're in GF(2)
×
85
    field_charac = int(root_charac**c_exp - 1)
×
86
    field_charac_next = int(root_charac**(c_exp+1) - 1)
×
87

88
    prim_candidates = []
×
89
    if fast_primes:
×
90
        prim_candidates = rwh_primes1(field_charac_next) # generate maybe prime polynomials and check later if they really are irreducible
×
91
        prim_candidates = [x for x in prim_candidates if x > field_charac] # filter out too small primes
×
92
    else:
93
        prim_candidates = _range(field_charac+2, field_charac_next, root_charac) # try each possible prime polynomial, but skip even numbers (because divisible by 2 so necessarily not irreducible)
×
94

95
    # Start of the main loop
96
    correct_primes = []
×
97
    for prim in prim_candidates: # try potential candidates primitive irreducible polys
×
98
        seen = bytearray(field_charac+1) # memory variable to indicate if a value was already generated in the field (value at index x is set to 1) or not (set to 0 by default)
×
99
        conflict = False # flag to know if there was at least one conflict
×
100

101
        # Second loop, build the whole Galois Field
102
        x = GF2int(1)
×
103
        for i in _range(field_charac):
×
104
            # Compute the next value in the field (ie, the next power of alpha/generator)
105
            x = x.multiply(generator, prim, field_charac+1)
×
106

107
            # Rejection criterion: if the value overflowed (above field_charac) or is a duplicate of a previously generated power of alpha, then we reject this polynomial (not prime)
108
            if x > field_charac or seen[x] == 1:
×
109
                conflict = True
×
110
                break
×
111
            # Else we flag this value as seen (to maybe detect future duplicates), and we continue onto the next power of alpha
112
            else:
113
                seen[x] = 1
×
114

115
        # End of the second loop: if there's no conflict (no overflow nor duplicated value), this is a prime polynomial!
116
        if not conflict: 
×
117
            correct_primes.append(prim)
×
118
            if single: return prim
×
119

120
    # Return the list of all prime polynomials
121
    return correct_primes # you can use the following to print the hexadecimal representation of each prime polynomial: print [hex(i) for i in correct_primes]
×
122

123
def init_lut(generator=3, prim=0x11b, c_exp=8):
2✔
124
    '''Precompute the logarithm and anti-log (look-up) tables for faster computation later, using the provided primitive polynomial.
125
    These tables are used for multiplication/division since addition/substraction are simple XOR operations inside GF of characteristic 2.
126
    The basic idea is quite simple: since b**(log_b(x), log_b(y)) == x * y given any number b (the base or generator of the logarithm), then we can use any number b to precompute logarithm and anti-log (exponentiation) tables to use for multiplying two numbers x and y.
127
    That's why when we use a different base/generator number, the log and anti-log tables are drastically different, but the resulting computations are the same given any such tables.
128
    For more infos, see https://en.wikipedia.org/wiki/Finite_field_arithmetic#Implementation_tricks
129
    '''
130
    # generator is the generator number (the "increment" that will be used to walk through the field by multiplication, this must be a prime number). This is basically the base of the logarithm/anti-log tables. Also often noted "alpha" in academic books.
131
    # prim is the primitive/prime (binary) polynomial and must be irreducible (it can't represented as the product of two smaller polynomials). It's a polynomial in the binary sense: each bit is a coefficient, but in fact it's an integer between 0 and 255, and not a list of gf values. For more infos: http://research.swtch.com/field
132
    # note that the choice of generator or prime polynomial doesn't matter very much: any two finite fields of size p^n have identical structure, even if they give the individual elements different names (ie, the coefficients of the codeword will be different, but the final result will be the same: you can always correct as many errors/erasures with any choice for those parameters). That's why it makes sense to refer to all the finite fields, and all decoders based on Reed-Solomon, of size p^n as one concept: GF(p^n). It can however impact sensibly the speed (because some parameters will generate sparser tables).
133

134
    global GF2int_exptable, GF2int_logtable, GF2_charac, GF2_c_exp
135
    GF2_charac = int(2**c_exp - 1)
×
136
    GF2_c_exp = int(c_exp)
×
137
    exptable = [-1] * (GF2_charac+1) # anti-log (exponential) table. The first two elements will always be [GF2int(1), generator]
×
138
    logtable = [-1] * (GF2_charac+1) # log table, log[0] is impossible and thus unused
×
139

140
    # Construct the anti-log table
141
    # It's basically the cumulative product of 1 by the generator number, on and on and on until you have walked through the whole field.
142
    # That's why exptable is always dense (all entries are filled), but logtable may be sparse (lots of empty values, because multiple logtable's entries point to the same exptable's entry).
143
    g = GF2int(1)
×
144
    for i in range(GF2_charac+1): # note that the last item of exptable will always be equal to the first item in the table, because g^p==g^0 because of the modulo p (we're in a finite field!).
×
145
        exptable[i] = g # compute anti-log for this value and store it in a table
×
146
        #logtable[g] = i # compute logtable at the same time as exptable (but log[1] will always be equal to g^255, which may be weird when compared to lists of logtables online but this is equivalent)
147
        g = g.multiply(generator, prim, GF2_charac+1) # equivalent to: g = generator**(i+1)
×
148

149
    # Construct the log table
150
    # Ignore the last element of the field because fields wrap back around.
151
    # The log of 1 can have two values: either g^0 (the exact value change depending on parameters) or it could be 255 (g^255=1) because of the wraparound
152
    # Note that either way, this does not change anything any output later (ie, the ecc symbols will be the same either way).
153
    for i, x in enumerate(exptable[:-1]):
×
154
        logtable[x] = i
×
155

156
    # Optimization: convert to integer arrays
157
    GF2int_exptable = array.array('i', exptable)
×
158
    GF2int_logtable = array.array('i', logtable)
×
159
    return GF2int_exptable, GF2int_logtable
×
160

161

162

163
class GF2int(int):
2✔
164
    '''Instances of this object are elements of the field GF(2^p)
165
    Instances are integers in the range 0 to p-1
166
    This field is defined using the irreducable polynomial
167
    x^8 + x^4 + x^3 + x + 1
168
    and using 3 as the generator for the exponent table and log table.
169
    '''
170

171
    __slots__ = [] # define all properties to save memory (can't add new properties at runtime) and it speeds up a lot. Here there's no property at all since it's only a type extending integers.
2✔
172

173
    # Maps integers to GF2int instances
174
    #cache = {}
175

176
    # def __new__(cls, value): # Note: works but commented out because on computers, we'd rather use less CPU than use less memory.
177
        # # Check cache
178
        # # Caching sacrifices a bit of speed for less memory usage. This way,
179
        # # there are only a max of 256 instances of this class at any time.
180
        # try:
181
            # return GF2int.cache[value]
182
        # except KeyError:
183
            # if value > GF2_charac or value < 0:
184
                # raise ValueError("Field elements of GF(2^p) are between 0 and %i. Cannot be %s" % (GF2_charac, value))
185

186
            # newval = int.__new__(cls, value)
187
            # GF2int.cache[int(value)] = newval
188
            # return newval
189

190
    def __add__(a, b):
2✔
191
        '''Addition in GF(2^8) is the xor of the two'''
192
        # Technical notes on why it works: In practice only one table is needed. That would be for the GP(256) multiply. Note that all arithmetic is carry-less, meaning that there is no carry-propagation.
193
        # Addition and subtraction without carry is equivalent to an xor.
194
        # So in GF(256), a + b and a - b are both equivalent to a xor b.
195
        # For more infos, see the great post at http://stackoverflow.com/questions/8440654/addition-and-multiplication-in-a-galois-field
196
        return GF2int(a ^ b)
×
197
    __sub__ = __add__
2✔
198
    __radd__ = __add__
2✔
199
    __rsub__ = __add__
2✔
200
    def __neg__(self):
2✔
201
        return self
×
202

203
    def __mul__(a, b):
2✔
204
        '''Multiplication in GF(2^8)'''
205
        # GF(256) multiplication is also carry-less, and can be done using carry-less multiplication in a similar way with carry-less addition/subtraction. This can be done efficiently with hardware support via say Intel's CLMUL instruction set.
206
        # a * b is really the same as exp(log(a) + log(b)). And because GF256 has only 256 elements, there are only GF2_charac unique powers of "x", and same for log. So these are easy to put in a lookup table.
207
        if a == 0 or b == 0: # not an optimization, it's necessary because log(0) is undefined
×
208
            return GF2int(0)
×
209
        x = GF2int_logtable[a]
×
210
        y = GF2int_logtable[b]
×
211
        #z = (x + y) % GF2_charac # in logarithms, addition = multiplication after exponentiation
212
        # Faster implementation of finite field multiplication: z = (log[a]+log[b] & GF2_charac) + (log[a]+log[b] >> GF2_c_exp), you can replace GF2_charac by (2^m)-1 and GF2_c_exp by m (eg: for GF(2^16), you'd get 65535 and 16). This optimization was shown in paper: "Fast software implementation of finite field operations", Cheng Huang and Lihao Xu, Washington University in St. Louis, Tech. Rep (2003).
213
        z = (x + y)
×
214
        z = (z & GF2_charac) + (z >> GF2_c_exp)
×
215
        return GF2int(GF2int_exptable[z])
×
216
    __rmul__ = __mul__
2✔
217

218
    def __pow__(self, power, modulo=None):
2✔
219
        # TODO: maybe try to implement the fast exponentiation here (implement binary exponentiation in Galois Fields that uses Montgomery Multiplication and using normal basis): http://stackoverflow.com/a/11640271/1121352  Algorithms for exponentiation in finite fields, by Shuhong Gao, Joachim Von Zur Gathen, Daniel Panario and Victor Shoup
220
        if isinstance(power, GF2int):
×
221
            raise TypeError("Raising a Field element to another Field element is not defined. power must be a regular integer")
×
222
        x = GF2int_logtable[self]
×
223
        z = (x * power) % GF2_charac
×
224
        return GF2int(GF2int_exptable[z])
×
225

226
    def inverse(self):
2✔
227
        e = GF2int_logtable[self]
×
228
        return GF2int(GF2int_exptable[GF2_charac - e])
×
229

230
    def __div__(self, other):
2✔
231
        #return self * GF2int(other).inverse() # self / other = self * inv(other) . This is equivalent to what is below, but 2x slower.
232
        if self == 0 or other == 0:
×
233
            return GF2int(0)
×
234
        x = GF2int_logtable[self]
×
235
        y = GF2int_logtable[other]
×
236
        z = (x - y) % GF2_charac # in logarithms, substraction = division after exponentiation
×
237
        return GF2int(GF2int_exptable[z])
×
238
    __floordiv__ = __div__
2✔
239
    __truediv__ = __div__
2✔
240

241
    def __rdiv__(self, other):
2✔
242
        return self.inverse() * other
×
243
    __rfloordiv__ = __rdiv__
2✔
244
    __rtruediv__ = __rdiv__
2✔
245

246
    def __repr__(self):
2✔
247
        n = self.__class__.__name__
×
248
        return "%s(%r)" % (n, int(self))
×
249

250
    def _to_binpoly(x):
2✔
251
        '''Convert a Galois Field's number into a nice polynomial'''
252
        if x <= 0: return "0"
×
253
        b = 1 # init to 2^0 = 1
×
254
        c = [] # stores the degrees of each term of the polynomials
×
255
        i = 0 # counter for b = 2^i
×
256
        while x > 0:
×
257
            b = (1 << i) # generate a number power of 2: 2^0, 2^1, 2^2, ..., 2^i. Equivalent to b = 2^i
×
258
            if x & b : # then check if x is divisible by the power of 2. Equivalent to x % 2^i == 0
×
259
                # If yes, then...
260
                c.append(i) # append this power (i, the exponent, gives us the coefficient)
×
261
                x ^= b # and compute the remainder of x / b
×
262
            i = i+1 # increment to compute the next power of 2
×
263
        return " + ".join(["x^%i" % y for y in c[::-1]]) # print a nice binary polynomial
×
264

265
    def multiply(a, b, prim=0x11b, field_charac_full=256, carryless=True):
2✔
266
        '''A slow multiply method. This method gives the same results as the
267
        other __mul__ method but without needing precomputed tables,
268
        thus it can be used to generate those tables.
269

270
        If prim is set to 0 and carryless=False, the function produces the result of a standard multiplication of integers (outside of a finite field, ie, no modular reduction and no carry-less operations).
271

272
        This procedure is called Russian Peasant Multiplication algorithm, which is just a general algorithm to multiply two integers together.
273
        The only two differences that you need to account for when doing multiplication in a finite field (as opposed to just integers) are:
274
        1- carry-less addition and substraction (XOR in GF(2^p))
275
        2- modular reduction (to avoid duplicate values in the field) using a prime polynomial
276
        '''
277

278
        r = 0
×
279
        a = int(a)
×
280
        b = int(b)
×
281
        while b: # while b is not 0
×
282
            if b & 1: r = r ^ a if carryless else r + a # b is odd, then add the corresponding a to r (the sum of all a's corresponding to odd b's will give the final product). Note that since we're in GF(2), the addition is in fact an XOR (very important because in GF(2) the multiplication and additions are carry-less, thus it changes the result!).
×
283
            b = b >> 1 # equivalent to b // 2
×
284
            a = a << 1 # equivalent to a*2
×
285
            if prim > 0 and a & field_charac_full: a = a ^ prim # GF modulo: if a >= 256 then apply modular reduction using the primitive polynomial (we just substract, but since the primitive number can be above 256 then we directly XOR).
×
286

287
        return GF2int(r)
×
288

289
    def multiply_slow(x, y, prim=0x11b):
2✔
290
        '''Another equivalent (but even slower) way to compute multiplication in Galois Fields without using a precomputed look-up table.
291
        This is the form you will most often see in academic literature, by using the standard carry-less multiplication + modular reduction using an irreducible prime polynomial.'''
292

293
        ### Define bitwise carry-less operations as inner functions ###
294
        def cl_mult(x,y):
×
295
            '''Bitwise carry-less multiplication on integers'''
296
            z = 0
×
297
            i = 0
×
298
            while (y>>i) > 0:
×
299
                if y & (1<<i):
×
300
                    z ^= x<<i
×
301
                i += 1
×
302
            return z
×
303

304
        def bit_length(n):
×
305
            '''Compute the position of the most significant bit (1) of an integer. Equivalent to int.bit_length()'''
306
            bits = 0
×
307
            while n >> bits: bits += 1
×
308
            return bits
×
309

310
        def cl_div(dividend, divisor=None):
×
311
            '''Bitwise carry-less long division on integers and returns the remainder'''
312
            # Compute the position of the most significant bit for each integers
313
            dl1 = bit_length(dividend)
×
314
            dl2 = bit_length(divisor)
×
315
            # If the dividend is smaller than the divisor, just exit
316
            if dl1 < dl2:
×
317
                return dividend
×
318
            # Else, align the most significant 1 of the divisor to the most significant 1 of the dividend (by shifting the divisor)
319
            for i in _range(dl1-dl2,-1,-1):
×
320
                # Check that the dividend is divisible (useless for the first iteration but important for the next ones)
321
                if dividend & (1 << i+dl2-1):
×
322
                    # If divisible, then shift the divisor to align the most significant bits and XOR (carry-less substraction)
323
                    dividend ^= divisor << i
×
324
            return dividend
×
325

326
        ### Main GF multiplication routine ###
327

328
        # Multiply the gf numbers
329
        result = cl_mult(x,y)
×
330
        # Then do a modular reduction (ie, remainder from the division) with an irreducible primitive polynomial so that it stays inside GF bounds
331
        if prim > 0:
×
332
            result = cl_div(result, prim)
×
333

334
        return result
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc