pythonclassnumbers

Python class that does integer operations mod n


I am trying to create a Python class that does operations mod n. For example using mod 100:

11*11==21
66+39==5
1+2+3-30==73
2**10==24

This is how I am doing it:

class ModInteger:
    def __init__(self, value, mod):
        
        self.mod = mod
        self.value = value % mod
    def __add__(self, other):
        return ModInteger((self.value + other.value) % self.mod, self.mod)
    def __sub__(self, other):
        return ModInteger((self.value - other.value) % self.mod, self.mod)
    def __mul__(self, other):
        return ModInteger((self.value * other.value) % self.mod, self.mod)
    def __pow__(self, other):
        return ModInteger((self.value ** other.value) % self.mod, self.mod)
    #...

It works, but is there a less tedious and repetitive way of doing it?


Solution

  • A more generic approach would be to build ModInteger with a metaclass that creates wrapper methods around relevant int methods.

    To allow compatibility with both int and ModInteger objects the wrapper methods should convert arguments of ModInteger objects into int objects before passing them to int methods, and convert returning values back to ModInteger objects if they are int.

    The demo below shows how wrapper methods work for both unary and binary operations, as well as methods such as __repr__, which doesn't take an argument and doesn't return an int object:

    class ModIntegerMeta(type):
        def __new__(metacls, name, bases, members):
            cls = super().__new__(metacls, name, bases, members)
            for name in '__add__', '__sub__', '__mul__', '__neg__', '__repr__':
                setattr(cls, name, cls.create_method(name))
            return cls
    
        def create_method(cls, name):
            def method(self, *args):
                value = getattr(self.value, name)(*(
                    arg.value if isinstance(arg, cls) else arg for arg in args
                ))
                if type(value) is int:
                    return cls(value, self.mod)
                return value
            return method
    
    class ModInteger(metaclass=ModIntegerMeta):
        def __init__(self, value, mod):
            self.mod = mod
            self.value = value % mod
    
    print((-ModInteger(66, 100) + ModInteger(39, 100) * 5))
    

    This outputs 29 because (-66 + (39 * 5) % 100) % 100 == 29

    Demo: https://ideone.com/m5upVv