#########################################
######## TP informatique bio-sép ########
########        2025-2026        ########
#########################################


#########################################
###### Thème 1 : Nombres complexes ######
#########################################


from math import pi, cos, sin
import matplotlib.pyplot as plt

## Q2 : fonctions parties réelle et imaginaire

def rpart(z) :
    return z[0]

def ipart(z) :
    return z[1]

## Q3 : somme, produit, conjugué

def somme(z1,z2) :
    a1, b1 = z1
    a2, b2 = z2
    return (a1+a2, b1+b2)

def produit(z1,z2) :
    a1, b1 = z1
    a2, b2 = z2
    return (a1*a2 - b1*b2, a1*b2+a2*b1)

def bar(z) :
    a, b = z
    return (a, -b)

## Q4 : module

def module2(z) :
    a, b = z
    return a**2 + b**2

# produit(z,bar(z)) renvoie un tuple, et non un flottant

def moduleComplexe(z) :
    z2 = module2(z)
    return (z2**0.5, 0)

## Q5 : inverse

def inverse(z) :
    zb = bar(z)
    z2 = module2(z)
    a, b = zb
    return (a/z2, b/z2)

## Q6-Q7 : représentations graphiques dans le plan complexe

def texte(z) :
    a,b = z
    s = b>=0
    signe = '+'*s + '-'*(1-s)
    return str(a)+signe+str(abs(b))+'i'

def dessine(z,text='') :
    a,b = z
    plt.plot([0,a],[0,b])
    plt.annotate(text,z)
    plt.arrow(0,0,a,b,width=0.05,length_includes_head=True)

# z = (1,-3)
# dessine(z,texte(z))
# plt.grid('on')
# plt.axis('equal')
# plt.show()

# Q8 : la somme de 2 complexes s'interprète graphiquement
# comme la somme de 2 vecteurs du plan complexe.
# C'est la relation de Chasles.

## Q9 : notation d'Euler

def expi(theta) :
    return (cos(theta), sin(theta))

# theta = pi/3
# z = expi(theta)
# dessine(z,texte(z))
# z2 = inverse(z)
# dessine(z2,texte(z2))
# plt.grid('on')
# plt.axis('equal')
# plt.show()

## Q10 : z et 1/z quand |z| = 1
# après quelques tests, on voit que le vecteur image de 1/z
# est symétrique de celui de z par rapport à l'axe des abscisses.
# Autrement dit, 1/z est le conjugué de z.
# Explication : |z|² = 1 = z * bar(z)
# donc 1/z = bar(z)
# z étant non nul, donc la division est possible.

## Q11 : suite géométrique de complexes

def u(C,theta,n) :
    L = [C]
    u0 = C
    for _ in range(n) :
        u0 = produit(expi(theta),u0)
        L.append(u0)
    return L

# Q12 : Un = U0 * q**n = C (expi(theta)**n)
#                      = C expi(n*theta)     d'après la formule de Moivre

## Q13 : représentation graphique des premiers
##       termes d'une suite géométrique

# C = (1,0)
# tour = 2*pi
# N = 5
# theta = tour / N
# n = N-1
# L = u(C,theta,n)
# for z in L :
#     dessine(z)
# plt.grid('on')
# plt.axis('equal')
# plt.show()

## Q14 : puissances entières

def puissance_rec(C,n) :
    '''renvoie C**n, avec C un complexe, version recursive'''
    if n==0 :
        return (1,0)
    else :
        return produit(C,puissance_rec(C,n-1))

def puissance_iter(C,n) :
    '''renvoie C**n, avec C un complexe, version iterative'''
    p = (1,0)
    for _ in range(n) :
        p = produit(p,C)
    return p

## Q15 : suite des puissances successives d'un complexe

def tracerPuissances(C,n) :
    z = C
    for _ in range(n) :
        dessine(z)
        z = produit(C,z)
    plt.grid('on')
    plt.axis('equal')
    plt.show()

## Q18 : liste des racines n-ème de 1

def listeRacines_boucle(n) :
    L = []
    theta = 2*pi/n
    for k in range(n) :
        omega = expi(k*theta)
        L.append(omega)
    return L

def listeRacines_comp(n) :
    omega = expi(2*pi/n)
    return [puissance_rec(omega,k) for k in range(n)]

## Q19 : somme et produits des w_k

def sommeEtProduit(n) :
    S,P = (0,0), (1,0)
    omega = (1,0)
    for k in range(n) :
        S = somme(S,omega)
        P = produit(P,omega)
        omega = produit(omega,expi(2*pi/n))
    return [S,P]