def gf2_add(a, b): """Add two polynomials in GF(p)[x] Parameters ---------- a : ndarray (uint8 or uint8) or list Addend polynomial's coefficients. b : ndarray (uint8 or uint8) or list Addend polynomial's coefficients. Returns ------- q : ndarray of uint8 Resulting polynomial's coefficients. Notes ----- Rightmost element in the arrays is the leading coefficient of the polynomial. In other words, the ordering for the coefficients of the polynomials is like the one used in MATLAB while in Sympy, for example, the leftmost element is the leading coefficient. Examples ======== >>> a = np.array([1,0,1], dtype="uint8") >>> b = np.array([1,1], dtype="uint8") >>> gf2_add(a,b) array([0, 1, 1], dtype=uint8) """ a, b = check_type(a, b) a, b = strip_zeros(a), strip_zeros(b) N = len(a) D = len(b) if N == D: res = xor(a, b) elif N > D: res = np.concatenate((xor(a[:D], b), a[D:])) else: res = np.concatenate((xor(a, b[:N]), b[N:])) return strip_zeros(res) return res
def gf2_mul(a, b): """Multiply polynomials in GF(2), FFT instead of convolution in time domain is used to speed up computation significantly. Parameters ---------- a : ndarray (uint8 or bool) or list Multiplicand polynomial's coefficients. b : ndarray (uint8 or bool) or list Multiplier polynomial's coefficients. Returns ------- q : ndarray of uint8 Resulting polynomial's coefficients. Examples ======== >>> a = np.array([1,0,1], dtype="uint8") >>> b = np.array([1,1,1], dtype="uint8") >>> gf2_mul(a,b) array([1, 1, 0, 1, 1], dtype=uint8) """ fsize = len(a) + len(b) - 1 fsize = 2**np.ceil(np.log2(fsize)).astype( int) #use nearest power of two much faster fslice = slice(0, fsize) ta = np.fft.fft(a, fsize) tb = np.fft.fft(b, fsize) res = np.fft.ifft(ta * tb)[fslice].copy() k = np.mod(np.rint(np.real(res)), 2).astype('uint8') return strip_zeros(k)
def mul(a, b): """Performs polynomial multiplication over GF2. Parameters ---------- b : ndarray (uint8 or bool) or list Multiplicand polynomial's coefficients. a : ndarray (uint8 or bool) or list Multiplier polynomial's coefficients. Returns ------- out : ndarray of uint8 Notes ----- This function performs exactly the same operation as gf2_mul but here instead of the fft, convolution in time domain is used. This is because this function must be used multiple times in gf2_xgcd and performing the fft in that instance introduced significant overhead. """ out = np.mod(np.convolve(a, b), 2).astype("uint8") return strip_zeros(out)
def gf2_div(dividend, divisor): """This function implements polynomial division over GF2. Given univariate polynomials ``dividend`` and ``divisor`` with coefficients in GF2, returns polynomials ``q`` and ``r`` (quotient and remainder) such that ``f = q*g + r`` (operations are intended for polynomials in GF2). The input arrays are the coefficients (including any coefficients equal to zero) of the dividend and "denominator divisor polynomials, respectively. This function was created by heavy modification of numpy.polydiv. Parameters ---------- dividend : ndarray (uint8 or bool) Dividend polynomial's coefficients. divisor : ndarray (uint8 or bool) Divisor polynomial's coefficients. Returns ------- q : ndarray of uint8 Quotient polynomial's coefficients. r : ndarray of uint8 Quotient polynomial's coefficients. Notes ----- Rightmost element in the arrays is the leading coefficient of the polynomial. In other words, the ordering for the coefficients of the polynomials is like the one used in MATLAB while in Sympy, for example, the leftmost element is the leading coefficient. Examples ======== >>> x = np.array([1, 0, 1, 1, 1, 0, 1], dtype="uint8") >>> y = np.array([1, 1, 1], dtype="uint8") >>> gf2_div(x, y) (array([1, 1, 1, 1, 1], dtype=uint8), array([], dtype=uint8)) """ N = len(dividend) - 1 D = len(divisor) - 1 if dividend[N] == 0 or divisor[D] == 0: dividend, divisor = strip_zeros(dividend), strip_zeros(divisor) if not divisor.any(): # if every element is zero raise ZeroDivisionError("polynomial division") elif D > N: q = np.array([]) return q, dividend else: u = dividend.astype("uint8") v = divisor.astype("uint8") m = len(u) - 1 n = len(v) - 1 scale = v[n].astype("uint8") q = np.zeros((max(m - n + 1, 1), ), u.dtype) r = u.astype(u.dtype) for k in range(0, m - n + 1): d = scale and r[m - k].astype("uint8") q[-1 - k] = d r[m - k - n:m - k + 1] = np.logical_xor(r[m - k - n:m - k + 1], np.logical_and(d, v)) r = strip_zeros(r) return q, r
def __repr__(self): return str(gf2_help_func.strip_zeros(self.array))