# [Python-Dev] Complex numbers

TBER Abdelmalek a.tber10 at gmail.com
Mon Sep 4 17:05:16 EDT 2017

```This module implements cplx class (complex numbers) regardless to the
built-in class.
The main goal of this module is to propose some improvement to complex
numbers in python and deal with them from a mathematical approach.
Also cplx class doesn't support the built-in class intentionally, as the
idea was to give an alternative to it.
With the hope I managed to succeed, here is the module :
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-dev/attachments/20170904/f47fafbc/attachment.html>
-------------- next part --------------
# Module for complex numbers (doesn't support the built-in complex class)
# Originally contributed by TBER Abdelmalek

import re, math
from fractions import Fraction as Q

if __name__ == '__main__' :
import os, Complex
help(Complex)
os.system("pause")

_supported = (int, float, Q, str, tuple, list) # Supported types for instanciation and operations for cplx class

class cplx :

"""This class implements complex numbers at the form of 'a+bi'
with a : the real part, and b : the imaginary part.
a and b are real numbers : [integers, floating numbers, or
fractions (from Fraction class of fractions module), refered here by Q].

Construction of complex numbers can be from two major ways :
- cplx([real part[, imaginary part]]) :
Two real numbers arguments given and both optionals, so that cplx() = 0
For fractions use Fraction class : cplx(Q(n,d),Q(n',d'))
for details about Fraction, see help(Fraction).

-cplx('valid form of a complex number in a STRING'):
The advantage of this way is the ease of fractions use with '/'.
Examples : cplx('2i-1/4')
cplx('6 - 1/5 * i') # spaces don't bother at all
cplx('7i')
"""
global _supported

def __init__(self, a=0, b=None):
if isinstance(a, str) and b is None:  # construction from string
if a == '' :
self.re = 0
self.im = 0
elif cplx.verify(a):
part_one = ''
part_two = ''
switch = False
first = True
for c in a :
if c in ('+', '-') and not first :
switch = True
part_two += c
elif not switch :
if c.isalnum() or c in ('+', '-', '/') : part_one += c
elif c in (',', '.') : part_one += '.'
else :
if c.isalnum() or c == '/' : part_two += c
elif c in (',', '.') : part_two += '.'
first = False
if 'i' in part_two :
part_two = part_two[:len(part_two)-1]
if '.' in part_one : self.re = float(part_one)
elif '/' in part_one : self.re = Q(part_one)
else : self.re = int(part_one)
if '.' in part_two : self.im = float(part_two)
elif '/' in part_two : self.im = Q(part_two)
elif part_two == '+' : self.im = 1
elif part_two == '-' : self.im = -1
else : self.im = int(part_two)
elif 'i' in part_one :
part_one = part_one[:len(part_one)-1]
if part_two == '' : self.re = 0
elif '.' in  part_two : self.re = float(part_two)
elif '/' in part_two : self.re = Q(part_two)
else : self.re = int(part_two)
if '.' in part_one : self.im = float(part_one)
elif '/' in part_one : self.im = Q(part_one)
elif part_one == '' or part_one == '+' : self.im = 1
elif part_one == '-' : self.im = -1
else : self.im = int(part_one)
else :
if '.' in part_one : self.re = float(part_one)
elif '/' in part_one : self.re = Q(part_one)
else : self.re = int(part_one)
self.im = 0
else :
raise ValueError("The form of complex numbers should be such as : 'a+bi' with a, b integers, floating numbers or fractions.")
elif isinstance(a, (tuple,list)) and len(a) == 2 and b is None:   # construction from 2-tuples or list
self.re = a[0]
self.im = a[1]
elif isinstance(a, cplx) and b is None :   # construction from cplx(complex)
self.re = a.re
self.im = a.im
elif isinstance(a, (int, float, Q)) :      # construction from integers, fractions and floating numbers
self.re = a
if b is None : self.im = 0
elif isinstance(b, (int, float, Q)) : self.im = b
else : raise TypeError("Imaginary part sould be an integer, floating number or fraction .")
else :
raise TypeError("Invalid arguments! For details see help(cplx).")

def __setattr__(self, n, v):
if n not in ('re', 'im') : raise AttributeError("Invalid attribute.")
if isinstance(v, (int, float, Q)) : object.__setattr__(self, n, v)
else : raise TypeError("Illegal assignement.")

def __delattr__(self, n):
raise AttributeError("cplx instances are characterized by 're' and 'im' attributes, deleting them is impossible.")

def __repr__(self):
"""Returns repr(self) in an elegant way."""
chain = ''
if isinstance(self.re, Q) and self.re._numerator != 0 :
if self.re._denominator != 1 :
if self.re._numerator < 0 : chain += '-'
chain += '({}/{})'.format(abs(self.re._numerator), self.re._denominator)
else : chain += '{}'.format(int(self.re))
elif self.re != 0 : chain += '{}'.format(self.re)
if self.re != 0 and self.im > 0 : chain += '+'
elif self.im < 0 : chain += '-'
if isinstance(self.im, Q) and self.im._numerator != 0 :
if self.im._denominator != 1 :
chain += '({}/{})'.format(abs(self.im._numerator), self.im._denominator)
elif abs(self.im) != 1 : chain += '{}'.format(abs(int(self.im)))
elif self.im != 0 and abs(self.im) != 1 : chain += '{}'.format(abs(self.im))
if self.im != 0 : chain += 'i'
if chain == '' : chain = '0'
return chain

def __str__(self):
"""Returns str(self)"""
return repr(self)

def __int__(self):
"""Returns int(real part)"""
return int(self.re)

def __float__(self):
"""Returns float(real part)"""
return float(self.re)

def __bool__(self):
"""Returns self != 0"""
return self != 0

def __pos__(self):
"""+self"""
return self

def __neg__(self):
"""-self"""
return cplx(-self.re, -self.im)

def __abs__(self):
"""Returns the absolute value if self is a real number.
Returns its modulus if it is complex."""
if self.im == 0 : return cplx(abs(self.re))
else : return self.modul()

# Comparaison block
def __eq__(self, value):
"""self == value"""
if isinstance(value, (_supported, cplx)) :
value = cplx(value)
return self.re == value.re and self.im == value.im
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __ne__(self, value):
"""self != 0"""
return not self == value

def __gt__(self, value):
"""self > value"""
if isinstance(value, (_supported, cplx)) :
value = cplx(value)
if self.im == 0 and value.im == 0 : return self.re > value.re
else : raise ValueError("Only real numbers are to be compared.")
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __ge__(self, value):
"""self >= value"""
return self > value or self == value

def __lt__(self, value):
"""self < value"""
return not self >= value

def __le__(self, value):
"""self <= value"""
return not self > value

# Operation block
"""self + value"""
if isinstance(value, (_supported, cplx)) :
value = cplx(value)
return cplx(self.re + value.re, self.im + value.im)
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

"""value + self"""
return self + value

def __sub__(self, value):
"""self - value"""
return self + (-value)

def __rsub__(self, value):
"""value - self"""
return -self + value

def __mul__(self, value):
"""self * value"""
if isinstance(value, (_supported, cplx)) :
value = cplx(value)
return cplx(self.re*value.re - self.im*value.im, self.re*value.im + self.im*value.re)
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __rmul__(self, value):
"""value * self"""
return self * value

def __pow__(self, value):
"""self**value"""
if self.im == 0 : return cplx(self.re**value)
elif isinstance(value, (_supported, cplx)) :
value = cplx(value)
if value == int(value):
if value == 0 : return 1
z = self
x = 1
while x < abs(value) :
z *= self
x += 1
if value > 0 : return z
else : return 1/z
else :
return math.e**(value * (math.log(self.modul()) + self.arg()*cplx('i')))
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __rpow__(self, value):
"""value**self"""
if self.im == 0 : return value**self.re
elif value == 0 : return 1 if self == 0 else 0
elif isinstance(value, (int, float, Q)) :
return (value**self.re)*(math.cos(self.im*math.log(value)) + math.sin(self.im*math.log(value))*cplx('i'))
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __truediv__(self, value):
"""self/value : real and imaginary parts are left as fractions.
Use c_float converter method to have floating numbers instead of fractions."""
if value == 0 : raise ZeroDivisionError
elif isinstance(value, (_supported, cplx)) :
value = cplx(value)
return cplx(Q(self.re*value.re + self.im*value.im, value.re**2 + value.im**2), Q(self.im*value.re - self.re*value.im, value.re**2 + value.im**2))
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __rtruediv__(self,value):
"""value/self"""
if isinstance(value, (_supported, cplx)) : return cplx(value)/self
else : raise TypeError("This type : {} is not supported for this operation.".format(type(value)))

def __floordiv__(self, value):
"""self//value"""
return (self/value).c_int()

def __rfloordiv__(self, value):
"""value//self"""
return (value/self).c_int()

def __mod__(self, value):
"""self % value"""
return self - (self//value)*value

def __rmod__(self, value):
"""value % self"""
return value - (value//self)*self

def __divmod__(self, value):
"""divmod(self, value)"""
return (self//value, self % value)

def __rdivmod__(self, value):
"""divmod(value, self)"""
return (value//self, value % self)

# Converting methods for complex
def c_int(self):
"""Converts real and imaginary parts into integers (returns a new object)"""
return cplx(int(self.re), int(self.im))

def c_float(self):
"""Converts real and imaginary parts into floating numbers (returns a new object)"""
return cplx(float(self.re), float(self.im))

def c_fract(self):
"""Converts real and imaginary parts into fractions (returns a new object)"""
return cplx(Q.from_float(float(self.re)), Q.from_float(float(self.im)))

# Useful methods
def coord(self):
"""Returns the coordinates of a complex number in a tuple.
a+bi -> (a,b)"""
return (self.re, self.im)

def conjugate(self):
"""Returns the conjugate of a complex number.
a+bi -> a-bi"""
return cplx(self.re, -self.im)

def switch(self):
"""Returns a complex number with real part and imaginary part switched.
a+bi -> b+ai"""
return cplx(self.im, self.re)

def modul(self):
"""Returns the modulus of the complex number"""
return math.sqrt(self.re**2 + self.im**2)

def arg(self):
"""Returns the argument of the complex number in radian"""
if self != 0 : teta = math.acos(abs(self.re)/abs(self))
if self.re >= 0 and self.im > 0 : angle = teta
elif self.re > 0 and self.im <= 0 : angle = -teta
elif self.re < 0 and self.im >= 0 : angle = math.pi - teta
elif self.re <= 0 and self.im < 0 : angle = -math.pi + teta
else : raise ValueError("0 doesn't have an argument.")
return angle

def deg_arg(self):
"""Returns the argument of the complex number in degrees"""
return math.degrees(self.arg())

def polar(self):
"""Returns the trigonometric form of a complex number -> str"""
return "{}(cos({}) + sin({})i)".format(self.modul(), self.arg(), self.arg())

def factorise(self, value=1):
"""Returns a factorisation of a complex number by a value given (similar to divmod) -> str
Example : (2i).factorise(i+1) >>> 2i = (i+1)(i+1)+0"""
chain = "{} = ({})({})".format(self, value, self//value)
if str(self%value)[0] == '-' : chain += "{}".format(self%value)
else : chain += "+{}".format(self%value)
return chain

@classmethod
def verify(cls, phrase) :
"""verify('a+bi') -> bool
The argument must be provided in a string to verify whether it's a valid complex number or not.
a and b can be integers, fractions(n/d), or floating numbers(with . or ,)"""
if isinstance(phrase, str) :
phrase = phrase.replace(" ", "")
model_re = r"[+-]?[0-9]+([,./][0-9]+)?([+-]([0-9]+([,./][0-9]+)?[*]?)?i)?" # a real part and an optional imaginary part
model_im = r"[+-]?([0-9]+([,./][0-9]+)?[*]?)?i([+-][0-9]+([,./][0-9]+)?)?" # an imaginary part and an optional real part
return re.fullmatch(model_re, phrase) is not None or re.fullmatch(model_im, phrase) is not None
else : raise TypeError("The complex number must be given in a string.")

#---------------------------------------#
i = cplx(0,1) # Most basic complex number
#---------------------------------------#
```