Re: #1
But now I think that, the revisions I suggested earlier still have some flaws!
While the recent fix adding _n=None to Z_.__init__ successfully resolves the TypeError, it introduces a minor design flaw: it exposes a meaningless second argument to the outside world, allowing unintuitive instantiations like Z100(22, 666). To maintain the encapsulation and prevent potential misuse, you can further improve this by removing the dummy parameter and handling the argument compatibility dynamically within the base class instead.
New proposed Fix:
Instead of modifying the Z_ subclass to accept an unnatural dummy parameter _n=None (which would incorrectly allow users to initialize it with two arguments like Z100(22, 666)), the fix should be applied to the parent Z_n class's arithmetic methods (__add__, __mul__, __neg__, __pow__ and inverse).
In Z_n class's arithmetic methods, you can use a try ... except TypeError: ... block to attempt initialization with two arguments, falling back to one argument if it fails. For example, in __add__:
Old:
def __add__(self, b: Self) -> Self:
if not self.n == b.n:
raise Exception("unsupported operation")
return self.__class__(self.z + b.z, self.n)
New:
def __add__(self, b: Self) -> Self:
if not self.n == b.n:
raise Exception("unsupported operation")
result_z = self.z + b.z
try:
return self.__class__(result_z, self.n)
except TypeError:
return self.__class__(result_z)
This same fallback logic should be applied to __mul__, __neg__, __pow__, and inverse as well.
By catching the TypeError and falling back to self.__class__(...) with a single argument, it can achieve compatibility for both Z_n and Z_ objects. The advantage of this method is that it maintains encapsulation, so that the parameter list of the constructor of the dynamic Z_ class isn't "polluted" with unnecessary arguments _n that could be misused externally.
Note: While you could technically resolve this by directly overriding all arithmetic methods within the Z_ class, doing so would introduce significant code duplication and verbosity. Instead, applying the try ... except ... method in the parent Z_n class provides a slightly more indirect but far more elegant and maintainable solution.
Additionally, the inverse method in Z_n currently hardcodes its return type to Z_n instead of dynamically returning the instance's actual class (self.__class__(...)), which violates the -> Self type hint and breaks consistency with __add__, __mul__, __neg__and __pow__. The logic which returns self.__class__(...) here maintains the consistency and ensures it returns the appropriate type.
Old return logic in method invrese:
return Z_n(PrimalityTesting.extended_euclidean(int(self.z), int(self.n))[0], self.n)
The new return logic in method invrese can be like this:
result_z = PrimalityTesting.extended_euclidean(int(self.z), int(self.n))[0]
try:
return self.__class__(result_z, self.n)
except TypeError:
return self.__class__(result_z)
The new proposed fix code and its relevant change can be referenced at the following link:
1343922569-webmail-hzau@685542d
Thanks!
Re: #1
But now I think that, the revisions I suggested earlier still have some flaws!
While the recent fix adding
_n=NonetoZ_.__init__successfully resolves theTypeError, it introduces a minor design flaw: it exposes a meaningless second argument to the outside world, allowing unintuitive instantiations likeZ100(22, 666). To maintain the encapsulation and prevent potential misuse, you can further improve this by removing the dummy parameter and handling the argument compatibility dynamically within the base class instead.New proposed Fix:
Instead of modifying the
Z_subclass to accept an unnatural dummy parameter_n=None(which would incorrectly allow users to initialize it with two arguments likeZ100(22, 666)), the fix should be applied to the parentZ_nclass's arithmetic methods (__add__,__mul__,__neg__,__pow__andinverse).In
Z_nclass's arithmetic methods, you can use atry ... except TypeError: ...block to attempt initialization with two arguments, falling back to one argument if it fails. For example, in__add__:Old:
New:
This same fallback logic should be applied to
__mul__,__neg__,__pow__, andinverseas well.By catching the
TypeErrorand falling back toself.__class__(...)with a single argument, it can achieve compatibility for bothZ_nandZ_objects. The advantage of this method is that it maintains encapsulation, so that the parameter list of the constructor of the dynamicZ_class isn't "polluted" with unnecessary arguments_nthat could be misused externally.Additionally, the
inversemethod inZ_ncurrently hardcodes its return type toZ_ninstead of dynamically returning the instance's actual class (self.__class__(...)), which violates the-> Selftype hint and breaks consistency with__add__,__mul__,__neg__and__pow__. The logic which returnsself.__class__(...)here maintains the consistency and ensures it returns the appropriate type.Old return logic in method
invrese:The new return logic in method
invresecan be like this:The new proposed fix code and its relevant change can be referenced at the following link:
1343922569-webmail-hzau@685542d
Thanks!