0

I'm trying to use the parent class's __div__() in order to maintain the same type so that many operations can be called at once as in the last example mix1 = bf2/bf4*bf1%bf5 in main() below where multiple arithmetic operations are strung together. For some reason, I can use super() in __add__() but not in __div__(). The error is "IndexError: list index out of range" and I've been going over and over this without any progress. Note that this is all related to polynomial arithmetic within a finite field.

I'm including the parsePolyVariable() and it's dependents (sorry if it looks like there's a bit of code but I assure you it's all for a good cause and builds character), since that's where the list error seems to be stemming from but I can't for the life of me figure out where everything is going very wrong. I'm teaching myself Python, so I'm sure there are some other beginners out there who will see where I'm missing the obvious.

I've been looking over these but they don't seem to be related to this situation:

http://docs.python.org/2/library/functions.html#super

Python super(Class, self).method vs super(Parent, self).method

How can I use Python's super() to update a parent value?

import re

class GF2Polynomial(object): #classes should generally inherit from object

    def __init__(self, string):
        '''__init__ is a standard special method used to initialize objects.
        Here __init__ will initialize a gf2infix object based on a string.'''
        self.string = string  #basically the initial string (polynomial)
        #if self.parsePolyVariable(string) == "0": self.key,self.lst = "0",[0]
        #else:
        self.key,self.lst = self.parsePolyVariable(string) # key determines polynomial compatibility
        self.bin = self.prepBinary(string)  #main value used in operations

    def id(self,lst):
        """returns modulus 2 (1,0,0,1,1,....) for input lists"""
        return [int(lst[i])%2 for i in range(len(lst))]

    def listToInt(self,lst):
        """converts list to integer for later use"""
        result = self.id(lst)
        return int(''.join(map(str,result)))

    def parsePolyToListInput(self,poly):
        """
        replaced by parsePolyVariable. still functional but not needed.
        performs regex on raw string and converts to list
        """
        c = [int(i.group(0)) for i in re.finditer(r'\d+', poly)]
        return [1 if x in c else 0  for x in xrange(max(c), -1, -1)]

    def parsePolyVariable(self,poly):
        """
        performs regex on raw string, converts to list.
        also determines key (main variable used) in each polynomial on intake
        """
        c = [int(m.group(0)) for m in re.finditer(r'\d+', poly)] #re.finditer returns an iterator
        if sum(c) == 0: return "0",[0]
        letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
        degree = max(c); varmatch = True; key = letter[0]
        for i in range(len(letter)):
            if letter[i] != key: varmatch = False
            else: varmatch = True
        if varmatch == False: return "error: not all variables in %s are the same"%a
        lst = [1 if x in c else (1 if x==0 else (1 if x=='x' else 0))  for x in xrange(degree, -1, -1)]
        return key,lst

    def polyVariableCheck(self,other):
        return self.key == other.key

    def prepBinary(self,poly):
        """converts to base 2; bina,binb are binary values like 110100101100....."""
        x = self.lst; a = self.listToInt(x)
        return int(str(a),2)

    def __add__(self,other):
        """
        __add__ is another special method, and is used to override the + operator.  This will only
        work for instances of gf2pim and its subclasses.
        self,other are gf2infix instances; returns GF(2) polynomial in string format
        """
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        return GF2Polynomial(self.outFormat(self.bin^other.bin))

    def __sub__(self,other):
        """
        __sub__ is the special method for overriding the - operator
        same as addition in GF(2)
        """
        return self.__add__(other)

    def __mul__(self,other):
        """
        __mul__ is the special method for overriding the * operator
        returns product of 2 polynomials in gf2; self,other are values 10110011...
        """
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        bitsa = reversed("{0:b}".format(self.bin))
        g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
        return GF2Polynomial(self.outFormat(reduce(lambda x,y: x^y,g)))

    def __div__(self,other):
        """
        __div__ is the special method for overriding the / operator
        returns quotient formatted as polynomial
        """
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        if self.bin == other.bin: return 1
        return GF2Polynomial(self.outFormat(self.bin/other.bin))

    def __mod__(self,other):
        """
        __mod__ is the special method for overriding the % operator
        returns remainder formatted as polynomial
        """
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        if self.bin == other.bin: return 0
        return GF2Polynomial(self.outFormat(self.bin%other.bin))

    def __str__(self):
        return self.string

    def outFormat(self,raw):
        """process resulting values into polynomial format"""
        raw = "{0:b}".format(raw); raw = str(raw[::-1]); g = [] #reverse binary string for enumeration
        g = [i for i,c in enumerate(raw) if c == '1']
        processed = "x**"+" + x**".join(map(str, g[::-1]))
        proc1 = processed.replace("x**1","x"); proc2 = proc1.replace("x**0","1")
        if len(g) == 0: return 0 #return 0 if list empty
        return proc2  #returns result in gf(2) polynomial form



class BinaryField(GF2Polynomial):
    def __init__(self, poly, mod):
        if mod == "0": self.string = "Error: modulus division by 0"
        elif mod == "0": self.string = "%s is 0 so resulting mod is 0"%(poly)
        fieldPoly = GF2Polynomial(poly) % mod
        if fieldPoly == 0: self.string = "%s and %s are the same so resulting mod is 0"%(poly,mod)
        else: super(BinaryField, self).__init__(fieldPoly.string)
        #self.degree = len(str(fieldPoly))

    def polyFieldCheck(self,other):
        return self.degree() == other.degree()

    def __add__(self, other):
        """
        inherited from GF2Polynomial
        """
        return super(BinaryField, self).__add__(other) % min(other,self)

    def __sub__(self,other):
        """
        inherited from GF2Polynomial
        """
        return self.__add__(other)

    def __mul__(self, other):
        """
        special method of BinaryField, needed for format adjustments between classes
        """
        #print "self = %s,%s   other = %s,%s  "%(self.degree(),type(self.degree()),other.degree(),type(other.degree()))
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        if self.polyFieldCheck(other) == False:
            return "error: fields of %s and %s do not match"%(self.string,other.string)
        else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
        bitsa = reversed("{0:b}".format(self.bin))
        g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
        result = reduce(lambda x,y: x^y,g)%min(self.bin,other.bin)
        return GF2Polynomial(self.outFormat(result))

    def __div__(self, other):
        """
        special method of BinaryField, needed for format adjustments between classes
        """
        if self.polyVariableCheck(other) == False:
            return "error: variables of %s and %s do not match"%(self.string,other.string)
        if self.polyFieldCheck(other) == False:
            return "error: fields of %s and %s do not match"%(self.string,other.string)
        else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
        if self.bin == other.bin: return 1
        result = self.bin/other.bin
        #return self.outFormat(result)
        return super(BinaryField, self).__div__(other) #% min(other,self)

    def degree(self):
        return len(self.lst)-1

And here's the main():

if __name__ == '__main__':
    ## "x**1 + x**0" polynomial string style input
    poly1 = "x**14 + x**1 + x**0"; poly2 = "x**6 + x**2 + x**1"; poly3 = "y**6 + y**2 + y**1"
    a = GF2Polynomial(poly1); b = GF2Polynomial(poly2); c = GF2Polynomial(poly3)
    ## "x+1" polynomial string style input
    poly4 = "x**14 + x + 1"; poly5 = "x**6 + x**2 + x"; poly6 = "x**8 + x**3 + 1"
    d = GF2Polynomial(poly4); e = GF2Polynomial(poly5); f = GF2Polynomial(poly6)
    poly7 = "x**9 + x**5 + 1"; poly8 = "x**11 + x**7 + x**4 + 1"; poly9 = "x**5 + x**4 + x**2 + x"
    g = GF2Polynomial(poly7); h = GF2Polynomial(poly8); i = GF2Polynomial(poly9)
##    g = GF2Polynomial("x**5 + x**4 + x**3 + 1"); h = GF2Polynomial("x**5 + x"); print "(g*h)%b = ",(g*h)%b
##    dd = GF2Polynomial("x**0"); print "dd -- ",dd
##    ee = GF2Polynomial("0"); print "ee -- ",ee
    bf1 = BinaryField(poly1,b); print bf1; print "degree bf1 = ",bf1.degree()
    bf2 = BinaryField(poly4,e); print "bf2  ",bf2; bf3 = BinaryField(poly4,d); print "bf3  ",bf3,type(bf3)
    bf4 = BinaryField(poly4,h); bf5 = BinaryField(poly9,e); bf6 = BinaryField(poly8,i)
    add1 = bf1+bf2
    print "add1   ",add1
    div1 = bf1/bf2
    print "div1   ",div1,type(div1)
    mix1 = bf2*bf1%bf5
    print "mix1    ",mix1,type(mix1)

EDIT: The full traceback --

Message File Name   Line    Position    
Traceback               
    <module>    C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py    233     
    __div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py    197     
    __div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py    100     
    __init__    C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py    20      
    parsePolyVariable   C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py    48      
IndexError: list index out of range             

For reference line 48 is degree = max(c); varmatch = True; key = letter[0]. Personal notes and information were removed, adjusting the line numbers.

Community
  • 1
  • 1
stackuser
  • 869
  • 16
  • 34
  • 4
    What is the *full* traceback of the exception you get? Can you reduce your example code to *just* the code that produces an error? – Martijn Pieters Aug 13 '13 at 21:15
  • 1
    The error is not related to `super` but to the internal state of your polynomials. The parent method is(probably... without traceback I can't tell for sure) called correctly, but it fails for some other reason. You should double check that your implementation of `__div__` is correct. (Style notes: I really cannot see ` == False`. Simply use `not self.polyVariableCheck(other)` and I cannot understand why your `__div__` methods *return* error strings when they fail. You should probably raise an exception instead). – Bakuriu Aug 13 '13 at 21:18
  • Full traceback is posted. The code posted above is already reduced considerably to the main culprit methods and their dependents. `__mod__` is necessary during initialization to the class and `__mul__` (may also need fixing) is there for comparison in return type to other special methods. Appreciate any help you can provide as I've been debugging this single issue for a very long time now. I'm not sure what else to check in `__div__()` as it is basically just the division itself. I'd like to use the `__div__()` from the parent instead of re-writing it, hence why I'm trying to use `super()`. – stackuser Aug 13 '13 at 21:34
  • This has **nothing** to do with `super()`. – Martijn Pieters Aug 13 '13 at 21:37

1 Answers1

3

Your return GF2Polynomial(self.outFormat(self.bin/other.bin)) line results in the string 1, which is then passed to the GF2Polynomial.parsePolyVariable() method.

This value has no letters, so the line:

letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]

returns an empty list. The next line:

degree = max(c); varmatch = True; key = letter[0]

then fails because key = letter[0] gives a IndexError exception.

Your code is hard to read because you use one-letter variables and put multiple statements on one line, so it is hard to make out what your expectations are in that function.

The exception has otherwise nothing to do with super(). There is a simple bug in your own code somewhere.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thanks! +1. I added `if letter == []: return "1",[1]` at line 48 and it seemed to go well until I actually tried it with `mix1 = bf5/bf2*bf1%bf5` and to which it errored `Message File Name Line Position Traceback C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 245 TypeError: can't multiply sequence by non-int of type 'BinaryField' ` which is an error i got before also, which is not letting me use the multiple operation together as i'm looking to do (as per my original question). – stackuser Aug 13 '13 at 22:00
  • With the fix I also see `mix1 error: variables of x and x**5 + x**4 + x**2 + 1 do not match `. – Martijn Pieters Aug 13 '13 at 22:01
  • and replacing the `mix1` line at the end I see `TypeError: not all arguments converted during string formatting` for the line you gave. – Martijn Pieters Aug 13 '13 at 22:03
  • what did you replace `mix1` with? all the string formatting looks to have right number and type of place holders... but `mix1 = bf2*bf1/bf5` does work so at least that's progress. – stackuser Aug 13 '13 at 22:16
  • with `mix1 = bf5/bf2*bf1%bf5`. The expression `bf5/bf2*bf1` returns a string, so the `%` is interpreted as string interpolation. – Martijn Pieters Aug 13 '13 at 22:18
  • You should **not** mix error messages into your methods. Use exceptions instead. – Martijn Pieters Aug 13 '13 at 22:19
  • Oooooohhhhhhh. i see now. it returns strings (piece by piece after each part of the expression, not just at the end), so those would have to be initialized again into the class or they'll just stay as strings (and not polynomials). i was thinking that they became a string after the whole expression executed. thanks for teaching me something today. now this makes me want to read your blog. – stackuser Aug 13 '13 at 22:25