from math import atan2

# ############################################################################## #
# UNE CLASSE PERMETTANT D'EFFECTUER DES MANIPULATIONS DE BASE SUR LES COMPLEXES. #
# ############################################################################## #

class cplx:

    # ====================================================================== #
    # CONSTRUCTION                                                           #
    # Les noms des attributs doivent permettre de lire aisément les caluls ! #
    # self.re : partie réelle du complexe.                                   #
    # self.im : partie imaginaire du complexe.                               #
    # ====================================================================== #

    def __init__(self,x,y):
        self.re = x
        self.im = y

    # ======================================================================================= #
    # Représentation classique du complexe, en tenant compte des situations particulières ... #
    # ATTENTION ! La méthode spéciale __repr__ doit renvoyer une chaîne de caractères !       #
    # ======================================================================================= #
    
    def __repr__(self):
        s = ''
        if self.re == 0 and self.im == 0:
            return '0'
        else:
            if self.re != 0:
                s = str(self.re)
                if self.im == 0:
                    return s
                else:
                    if self.im > 0:
                        if self.im == 1:
                            s += ' + i'
                        else:
                            s += ' + ' + str(self.im) + ' i'
                    elif self.im < 0:
                        if self.im == -1:
                            s += ' - i'
                        else:
                            s += ' - ' + str(abs(self.im)) + 'i'
            else:
                if self.im > 0:
                    if self.im == 1:
                        s += 'i'
                    else:
                        s += str(self.im) + ' i'
                elif self.im < 0:
                    if self.im == -1:
                        s += ' - i'
                    else:
                        s += str(self.im) + ' i'
        return s

    # ====== #
    # MODULE #
    # ====== #
    
    def module(self):
        return (self.re**2 + self.im**2)**0.5

    # ================= #
    # COMPLEXE CONJUGUE #
    # ================= #
    
    def conj(self):
        return cplx(self.re,-self.im)

    # ======= #
    # INVERSE #
    # ======= #

    def inverse(self):
        if self.re != 0 or self.im != 0:
            return cplx(self.re/self.module()**2,-self.im/self.module()**2)
        else:
            print ('L\'inverse du complexe nul n\'est pas défini !')

    # ======== #
    # ARGUMENT #
    # ======== #
    
    def argument(self):
        if self.re != 0 or self.im != 0:
            return atan2(self.im,self.re)
        else:
            print ('Le complexe nul n\'admet pas d\'argument !')
    
    # ================================================================ #
    # SURCHARGE DE __add__ et __radd__ POUR LE SUPPORT DE cplx par "+" #
    # ================================================================ #
    
    # Méthode spéciale __add__ : surcharge de l'opérateur + afin de pouvoir
    # simplement sommer un complexe ... et un autre nombre grâce au symbole +.
    # ATTENTION ! Ici, on a traite le cas où le PREMIER TERME de l'addition est un complexe !
    def __add__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('L\'addition est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(self.re + z.re,self.im + z.im)

    # Méthode spéciale __radd__ : surcharge de l'opérateur + afin de pouvoir
    # simplement sommer un complexe ... et un autre nombre grâce au symbole +.
    # ATTENTION ! Ici, on a traite le cas où le DEUXIEME TERME de l'addition est un complexe !
    def __radd__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('L\'addition est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(self.re + z.re,self.im + z.im)

    # ================================================================ #
    # SURCHARGE DE __sub__ et __rsub__ POUR LE SUPPORT DE cplx par "-" #
    # ================================================================ #

    # Méthode spéciale __sub__ : surcharge de l'opérateur - afin de pouvoir
    # simplement soustraire un complexe ... et un autre nombre grâce au symbole -.
    # ATTENTION ! Ici, on a traite le cas où le PREMIER TERME de la soustraction est un complexe !
    def __sub__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('La soustraction est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(self.re - z.re,self.im - z.im)

    # Méthode spéciale __rsub__ : surcharge de l'opérateur - afin de pouvoir
    # simplement soustraire un complexe ... et un autre nombre grâce au symbole -.
    # ATTENTION ! Ici, on a traite le cas où le DEUXIEME TERME de la soustraction est un complexe !
    def __rsub__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('La soustraction est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(z.re - self.re,z.im - self.im)

    # ================================================================ #
    # SURCHARGE DE __mul__ et __rmul__ POUR LE SUPPORT DE cplx par "*" #
    # ================================================================ #

    # Méthode spéciale __mul__ : surcharge de l'opérateur * afin de pouvoir
    # simplement multiplier un complexe ... et un autre nombre grâce au symbole *.
    # ATTENTION ! Ici, on a traite le cas où le PREMIER FACTEUR de la multiplication est un complexe !
    def __mul__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('La multiplication est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(self.re * z.re - self.im * z.im,self.re * z.im + self.im * z.re)

    # Méthode spéciale __rmul__ : surcharge de l'opérateur * afin de pouvoir
    # simplement multiplier un complexe ... et un autre nombre grâce au symbole *.
    # ATTENTION ! Ici, on a traite le cas où le SECOND FACTEUR de la multiplication est un complexe !
    def __rmul__(self,z):
        if type(z) != cplx and type(z) != int and type(z) != float:
            return ('La multiplication est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return cplx(self.re * z.re - self.im * z.im,self.re * z.im + self.im * z.re)

    # ======================================================================== #
    # SURCHARGE DE __truediv__ et __rtruediv__ POUR LE SUPPORT DE cplx par "/" #
    # ======================================================================== #

    # Méthode spéciale __truediv__ : surcharge de l'opérateur / afin de pouvoir
    # simplement diviser un complexe ... et un autre nombre grâce au symbole /.
    # ATTENTION ! Ici, on a traite le cas où le DIVIDENDE de la division est un complexe !
    def __truediv__(self,z):
        if (type(z) != cplx and type(z) != int and type(z) != float) or (type(z) == cplx and z.re == 0 and z.im == 0) or z == 0:
            return ('La division est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return self * z.inverse()

    # Méthode spéciale __rtruediv__ : surcharge de l'opérateur / afin de pouvoir
    # simplement diviser un complexe ... et un autre nombre grâce au symbole /.
    # ATTENTION ! Ici, on a traite le cas où le DIVISEUR de la division est un complexe !
    def __rtruediv__(self,z):
        if (type(z) != cplx and type(z) != int and type(z) != float) or (type(z) == cplx and z.re == 0 and z.im == 0) or z == 0:
            return ('La division est impossible !')
        else:
            if type(z) != cplx:
                z = cplx(z,0)
            return z * self.inverse()