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 :
# 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
        def __add__(self, value):
                """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)))
                
        def __radd__(self, 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
#---------------------------------------#
_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to