# Fichier: python_cheats/cheatsheets/math_cmath.txt
# Cheatsheet math & cmath Python - Guide Complet


[OK] INTRODUCTION

# math: Fonctions mathématiques pour nombres réels
# cmath: Fonctions mathématiques pour nombres complexes

import math
import cmath


[OK] CONSTANTES MATHÉMATIQUES


# math module
print(math.pi)        # 3.141592653589793
print(math.e)         # 2.718281828459045
print(math.tau)       # 6.283185307179586 (2*pi, Python 3.6+)
print(math.inf)       # Infini positif
print(-math.inf)      # Infini négatif
print(math.nan)       # Not a Number

# Vérifications
math.isinf(math.inf)   # True
math.isnan(math.nan)   # True
math.isfinite(42)      # True
math.isfinite(math.inf) # False

# cmath constantes
print(cmath.pi)       # 3.141592653589793
print(cmath.e)        # 2.718281828459045
print(cmath.inf)      # Infini
print(cmath.infj)     # Infini complexe (0+infj)
print(cmath.nan)      # NaN
print(cmath.nanj)     # NaN complexe (0+nanj)


[OK] FONCTIONS DE BASE


# Valeur absolue
math.fabs(-5.5)       # 5.5 (retourne float)
abs(-5)               # 5 (retourne int si int)
abs(-5.5)             # 5.5

# Arrondi
math.ceil(4.2)        # 5 (arrondi supérieur)
math.floor(4.8)       # 4 (arrondi inférieur)
math.trunc(4.8)       # 4 (tronque vers zéro)
round(4.5)            # 4 (banker's rounding)
round(5.5)            # 6
round(4.567, 2)       # 4.57 (2 décimales)

# Modulo et reste
math.fmod(10, 3)      # 1.0 (reste division, même signe que dividende)
math.remainder(10, 3) # 1.0 (reste IEEE)
10 % 3                # 1 (modulo Python)

# Différences:
# fmod(-10, 3) = -1.0
# -10 % 3 = 2

# Somme précise (évite erreurs floating point)
values = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]
sum(values)           # 0.9999999999999999
math.fsum(values)     # 1.0 (précision IEEE)

# Produit (Python 3.8+)
from math import prod
prod([2, 3, 4])       # 24
prod([])              # 1 (identité multiplicative)


[OK] PUISSANCES ET RACINES


# Puissance
math.pow(2, 3)        # 8.0 (retourne toujours float)
2 ** 3                # 8 (type dépend des opérandes)
pow(2, 3)             # 8

# Racine carrée
math.sqrt(16)         # 4.0
math.sqrt(2)          # 1.4142135623730951
16 ** 0.5             # 4.0

# Racine n-ième
math.pow(8, 1/3)      # 2.0 (racine cubique)
8 ** (1/3)            # 2.0

# Exponentielles
math.exp(1)           # 2.718281828459045 (e^1)
math.exp(2)           # 7.38905609893065
math.expm1(x)         # exp(x) - 1 (précis pour x proche de 0)

# Puissances de 2
math.exp2(3)          # 8.0 (2^3, Python 3.11+)
2 ** 3                # 8

# Logarithmes
math.log(math.e)      # 1.0 (ln)
math.log(100, 10)     # 2.0 (log base 10)
math.log10(100)       # 2.0 (log base 10)
math.log2(8)          # 3.0 (log base 2)
math.log1p(x)         # log(1+x) (précis pour x proche de 0)

# Exemples
math.log(1024, 2)     # 10.0
math.log10(1000)      # 3.0
math.log2(1024)       # 10.0


[OK] TRIGONOMÉTRIE (RADIANS)


# Fonctions de base
math.sin(math.pi / 2)     # 1.0
math.cos(0)               # 1.0
math.tan(math.pi / 4)     # 1.0

# Fonctions inverses
math.asin(1)              # π/2 (arcsin)
math.acos(0)              # π/2 (arccos)
math.atan(1)              # π/4 (arctan)
math.atan2(y, x)          # atan(y/x) avec quadrant correct

# atan2 exemples
math.atan2(1, 1)          # π/4 (quadrant I)
math.atan2(1, -1)         # 3π/4 (quadrant II)
math.atan2(-1, -1)        # -3π/4 (quadrant III)
math.atan2(-1, 1)         # -π/4 (quadrant IV)

# Conversion degrés/radians
math.radians(180)         # π
math.degrees(math.pi)     # 180.0

# Exemples pratiques
angle_deg = 45
angle_rad = math.radians(angle_deg)
print(math.sin(angle_rad))  # 0.7071...

# Hypoténuse
math.hypot(3, 4)          # 5.0 (√(3²+4²))
math.hypot(1, 1)          # 1.4142135623730951 (√2)

# Hypot n-dimensions (Python 3.8+)
math.hypot(1, 1, 1)       # 1.7320508075688772 (√3)
math.hypot(3, 4, 0)       # 5.0


[OK] TRIGONOMÉTRIE HYPERBOLIQUE


# Fonctions hyperboliques
math.sinh(0)              # 0.0
math.cosh(0)              # 1.0
math.tanh(0)              # 0.0

# Fonctions inverses
math.asinh(0)             # 0.0
math.acosh(1)             # 0.0
math.atanh(0)             # 0.0

# Formules:
# sinh(x) = (e^x - e^-x) / 2
# cosh(x) = (e^x + e^-x) / 2
# tanh(x) = sinh(x) / cosh(x)

# Identité: cosh²(x) - sinh²(x) = 1


[OK] FONCTIONS SPÉCIALES


# Factorielle
math.factorial(5)         # 120
math.factorial(0)         # 1
# math.factorial(-1)      # ValueError
# math.factorial(3.5)     # ValueError (entiers seulement)

# Combinaisons (Python 3.8+)
math.comb(5, 2)           # 10 (5 choose 2)
math.comb(10, 3)          # 120

# Permutations (Python 3.8+)
math.perm(5, 2)           # 20 (5 P 2)
math.perm(5)              # 120 (5!)

# GCD (Plus Grand Commun Diviseur)
math.gcd(48, 18)          # 6
math.gcd(100, 50, 25)     # 25 (Python 3.9+: n arguments)

# LCM (Plus Petit Commun Multiple, Python 3.9+)
math.lcm(12, 18)          # 36
math.lcm(4, 6, 8)         # 24

# Gamma function
math.gamma(5)             # 24.0 (= 4!)
math.gamma(0.5)           # 1.772... (√π)

# Log gamma (pour éviter overflow)
math.lgamma(100)          # log(gamma(100))

# Fonction erreur
math.erf(0)               # 0.0
math.erf(1)               # 0.8427007929497149
math.erfc(0)              # 1.0 (1 - erf(x))


[OK] COMPARAISONS ET TESTS


# Vérifier type de nombre
math.isfinite(42)         # True
math.isfinite(math.inf)   # False
math.isinf(math.inf)      # True
math.isnan(math.nan)      # True

# Comparaison proche (Python 3.5+)
math.isclose(0.1 + 0.2, 0.3)           # True
math.isclose(1.0, 1.0000001)           # True (tolérance par défaut)
math.isclose(1.0, 1.1)                 # False

# Avec tolérance custom
math.isclose(1.0, 1.01, rel_tol=0.01)  # True (1% tolérance)
math.isclose(1.0, 1.01, abs_tol=0.01)  # True (tolérance absolue)

# rel_tol: tolérance relative (défaut: 1e-9)
# abs_tol: tolérance absolue (défaut: 0.0)

# Copie signe
math.copysign(5, -1)      # -5.0
math.copysign(-5, 1)      # 5.0


[OK] MANIPULATION DE BITS FLOTTANTS


# Décomposer float
mantissa, exponent = math.frexp(12.5)
# 12.5 = mantissa * 2^exponent
# mantissa = 0.78125, exponent = 4
# 0.78125 * 2^4 = 12.5

# Recomposer
math.ldexp(0.78125, 4)    # 12.5

# Décomposer mantisse/exposant base 10
math.modf(12.75)          # (0.75, 12.0) (fraction, entier)


[OK] NOMBRES COMPLEXES (cmath)


# Créer nombres complexes
z1 = 3 + 4j
z2 = complex(3, 4)
z3 = 5 + 0j

# Parties réelle et imaginaire
z1.real                   # 3.0
z1.imag                   # 4.0

# Conjugué
z1.conjugate()            # (3-4j)

# Fonctions cmath
cmath.phase(3 + 4j)       # 0.927... (angle en radians)
abs(3 + 4j)               # 5.0 (module)
cmath.polar(3 + 4j)       # (5.0, 0.927...) (r, θ)
cmath.rect(5, 0.927)      # (3+4j) approx

# Conversion polaire/rectangulaire
r, theta = cmath.polar(3 + 4j)
z = cmath.rect(r, theta)

# Racine carrée complexe
cmath.sqrt(-1)            # 1j
cmath.sqrt(-4)            # 2j
math.sqrt(-4)             # ValueError!

# Logarithme complexe
cmath.log(1j)             # (0+1.57...j) (π/2)
cmath.log(-1)             # (0+3.14...j) (πi)

# Exponentielle complexe
cmath.exp(1j * cmath.pi)  # (-1+0j) (formule d'Euler)

# Trigonométrie complexe
cmath.sin(1 + 2j)
cmath.cos(1 + 2j)
cmath.tan(1 + 2j)

# Fonctions inverses
cmath.asin(1 + 2j)
cmath.acos(1 + 2j)
cmath.atan(1 + 2j)

# Hyperboliques complexes
cmath.sinh(1 + 2j)
cmath.cosh(1 + 2j)
cmath.tanh(1 + 2j)

# Vérifications
cmath.isinf(complex(math.inf, 0))   # True
cmath.isnan(complex(math.nan, 0))   # True
cmath.isfinite(3 + 4j)              # True


[OK] EXEMPLES PRATIQUES


# 1. Distance entre deux points
def distance(x1, y1, x2, y2):
    return math.hypot(x2 - x1, y2 - y1)

distance(0, 0, 3, 4)      # 5.0

# Version 3D
def distance_3d(p1, p2):
    return math.hypot(p2[0] - p1[0], p2[1] - p1[1], p2[2] - p1[2])

# 2. Angle entre deux points
def angle(x1, y1, x2, y2):
    return math.atan2(y2 - y1, x2 - x1)

# 3. Conversion coordonnées cartésiennes/polaires
def cart_to_polar(x, y):
    r = math.hypot(x, y)
    theta = math.atan2(y, x)
    return r, theta

def polar_to_cart(r, theta):
    x = r * math.cos(theta)
    y = r * math.sin(theta)
    return x, y

# 4. Normaliser un vecteur
def normalize(x, y):
    magnitude = math.hypot(x, y)
    return x / magnitude, y / magnitude

# 5. Produit scalaire
def dot_product(x1, y1, x2, y2):
    return x1 * x2 + y1 * y2

# 6. Angle entre deux vecteurs
def angle_between_vectors(x1, y1, x2, y2):
    dot = dot_product(x1, y1, x2, y2)
    mag1 = math.hypot(x1, y1)
    mag2 = math.hypot(x2, y2)
    return math.acos(dot / (mag1 * mag2))

# 7. Rotation d'un point
def rotate_point(x, y, angle):
    cos_a = math.cos(angle)
    sin_a = math.sin(angle)
    new_x = x * cos_a - y * sin_a
    new_y = x * sin_a + y * cos_a
    return new_x, new_y

# 8. Interpolation linéaire
def lerp(a, b, t):
    """Interpolation linéaire entre a et b (t dans [0, 1])"""
    return a + (b - a) * t

# 9. Clamp (limiter une valeur)
def clamp(value, min_val, max_val):
    return max(min_val, min(max_val, value))

# 10. Smooth step (interpolation douce)
def smoothstep(edge0, edge1, x):
    t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0)
    return t * t * (3.0 - 2.0 * t)

# 11. Map range (remapper valeur)
def map_range(value, in_min, in_max, out_min, out_max):
    return (value - in_min) * (out_max - out_min) / (in_max - in_min) + out_min

# 12. Décibels (logarithme)
def to_db(amplitude):
    """Convertir amplitude en décibels"""
    return 20 * math.log10(amplitude)

def from_db(db):
    """Convertir décibels en amplitude"""
    return math.pow(10, db / 20)

# 13. Moyenne géométrique
def geometric_mean(values):
    n = len(values)
    product = math.prod(values)
    return math.pow(product, 1/n)

# 14. Coefficient binomial
def binomial(n, k):
    """Coefficient binomial: n choose k"""
    return math.comb(n, k)

# 15. Probabilité (distribution normale)
def normal_pdf(x, mu=0, sigma=1):
    """Fonction densité de probabilité normale"""
    return (1 / (sigma * math.sqrt(2 * math.pi))) * \
           math.exp(-0.5 * ((x - mu) / sigma) ** 2)


[OK] PRÉCISION NUMÉRIQUE


# Problèmes floating point
0.1 + 0.2                 # 0.30000000000000004
0.1 + 0.2 == 0.3          # False!

# Solutions:
# 1. math.isclose()
math.isclose(0.1 + 0.2, 0.3)  # True

# 2. round()
round(0.1 + 0.2, 10) == round(0.3, 10)  # True

# 3. decimal module (précision arbitraire)
from decimal import Decimal
Decimal('0.1') + Decimal('0.2') == Decimal('0.3')  # True

# 4. fractions module (rationnels exacts)
from fractions import Fraction
Fraction(1, 10) + Fraction(2, 10) == Fraction(3, 10)  # True

# Epsilon machine
import sys
sys.float_info.epsilon    # 2.220446049250313e-16


[OK] CONVERSIONS UTILES


# Degrés <-> Radians
degrees = 90
radians = math.radians(degrees)  # π/2
back = math.degrees(radians)      # 90.0

# Pourcentage <-> Décimal
percent = 75
decimal = percent / 100           # 0.75
back = decimal * 100              # 75.0

# Température
def celsius_to_fahrenheit(c):
    return c * 9/5 + 32

def fahrenheit_to_celsius(f):
    return (f - 32) * 5/9

# Coordonnées sphériques <-> cartésiennes
def spherical_to_cartesian(r, theta, phi):
    x = r * math.sin(phi) * math.cos(theta)
    y = r * math.sin(phi) * math.sin(theta)
    z = r * math.cos(phi)
    return x, y, z

def cartesian_to_spherical(x, y, z):
    r = math.sqrt(x**2 + y**2 + z**2)
    theta = math.atan2(y, x)
    phi = math.acos(z / r)
    return r, theta, phi


[OK] STATISTIQUES BASIQUES (avec math)


def mean(values):
    """Moyenne arithmétique"""
    return math.fsum(values) / len(values)

def variance(values):
    """Variance"""
    m = mean(values)
    return sum((x - m) ** 2 for x in values) / len(values)

def std_dev(values):
    """Écart-type"""
    return math.sqrt(variance(values))

def median(values):
    """Médiane"""
    sorted_values = sorted(values)
    n = len(sorted_values)
    if n % 2 == 0:
        return (sorted_values[n//2-1] + sorted_values[n//2]) / 2
    return sorted_values[n//2]

# Note: Utiliser statistics module pour des fonctions plus complètes
import statistics
statistics.mean([1, 2, 3, 4, 5])
statistics.stdev([1, 2, 3, 4, 5])


[OK] NOMBRES PREMIERS


def is_prime(n):
    """Vérifier si n est premier"""
    if n < 2:
        return False
    if n == 2:
        return True
    if n % 2 == 0:
        return False
    
    # Vérifier jusqu'à √n
    sqrt_n = math.isqrt(n)  # Python 3.8+
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

# math.isqrt() : racine carrée entière (Python 3.8+)
math.isqrt(16)            # 4
math.isqrt(17)            # 4 (floor)


[OK] SUITES MATHÉMATIQUES


# Suite de Fibonacci
def fibonacci(n):
    """n-ième terme de Fibonacci"""
    phi = (1 + math.sqrt(5)) / 2  # Nombre d'or
    return round(phi**n / math.sqrt(5))

# Nombre d'or
phi = (1 + math.sqrt(5)) / 2      # 1.618...

# Série géométrique
def geometric_series(a, r, n):
    """Somme des n premiers termes: a + ar + ar² + ... + ar^(n-1)"""
    if r == 1:
        return a * n
    return a * (1 - r**n) / (1 - r)

# Série arithmétique
def arithmetic_series(a, d, n):
    """Somme des n premiers termes: a + (a+d) + (a+2d) + ..."""
    return n * (2*a + (n-1)*d) / 2


[OK] OPTIMISATION ET PERFORMANCE


# Utiliser ** pour petites puissances entières
x ** 2              # Plus rapide que math.pow(x, 2)
x ** 3              # Plus rapide que x * x * x pour x simple

# math.pow() retourne toujours float
math.pow(2, 3)      # 8.0
2 ** 3              # 8 (int)

# Pré-calculer constantes
# Lent:
for i in range(1000):
    result = i * math.pi

# Rapide:
pi = math.pi
for i in range(1000):
    result = i * pi

# Éviter appels répétés dans boucles
# Lent:
for i in range(len(array)):
    result = math.sqrt(array[i])

# Rapide:
sqrt = math.sqrt
for i in range(len(array)):
    result = sqrt(array[i])


[OK] ERREURS COURANTES


# 1. Domaine invalide
# math.sqrt(-1)           # ValueError
# Solution: utiliser cmath
cmath.sqrt(-1)            # 1j

# 2. Overflow
# math.exp(1000)          # OverflowError
# Solution: vérifier avant ou utiliser try/except

# 3. Division par zéro
# math.log(0)             # ValueError
# Solution: vérifier avant

# 4. Précision floating point
0.1 + 0.2 == 0.3          # False!
# Solution: math.isclose()

# 5. Radians vs degrés
math.sin(90)              # 0.89... (FAUX! 90 radians)
math.sin(math.radians(90))  # 1.0 (CORRECT)

# 6. Integer overflow (Python 2)
# En Python 3, les int sont de taille illimitée
2 ** 1000                 # Pas d'overflow!

# 7. atan vs atan2
math.atan(1/1)            # π/4
math.atan(1/-1)           # -π/4 (perd info quadrant!)
math.atan2(1, -1)         # 3π/4 (CORRECT: quadrant II)


[OK] ALTERNATIVES ET COMPLÉMENTS


# NumPy pour calculs vectorisés
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
np.sqrt(arr)              # Appliqué à tout le tableau
np.sin(arr)
np.exp(arr)

# SciPy pour fonctions scientifiques avancées
from scipy import special
special.j0(1)             # Bessel function
special.erf(1)            # Error function

# SymPy pour calcul symbolique
from sympy import symbols, sin, cos, diff
x = symbols('x')
diff(sin(x), x)           # cos(x)

# Decimal pour précision arbitraire
from decimal import Decimal, getcontext
getcontext().prec = 50    # 50 chiffres
Decimal('1') / Decimal('3')

# Fractions pour rationnels exacts
from fractions import Fraction
Fraction(1, 3) + Fraction(1, 6)  # Fraction(1, 2)

# Statistics module
import statistics
statistics.mean([1, 2, 3])
statistics.median([1, 2, 3])
statistics.stdev([1, 2, 3])


[OK] FORMULES MATHÉMATIQUES UTILES


# Théorème de Pythagore
# c² = a² + b²
c = math.hypot(a, b)

# Loi des cosinus
# c² = a² + b² - 2ab·cos(C)
c = math.sqrt(a**2 + b**2 - 2*a*b*math.cos(C))

# Formule d'Euler
# e^(iπ) + 1 = 0
cmath.exp(1j * cmath.pi) + 1  # ≈ 0

# Identités trigonométriques
# sin²(x) + cos²(x) = 1
# tan(x) = sin(x) / cos(x)
# sin(2x) = 2·sin(x)·cos(x)

# Logarithmes
# log(a·b) = log(a) + log(b)
# log(a/b) = log(a) - log(b)
# log(a^n) = n·log(a)

# Changement de base
# log_b(x) = log(x) / log(b)
def log_base(x, base):
    return math.log(x) / math.log(base)


[OK] BONNES PRATIQUES


# [OK] Utiliser math.hypot() au lieu de sqrt(x²+y²)
# [OK] Utiliser math.isclose() pour comparer floats
# [OK] Utiliser math.fsum() pour sommes précises
# [OK] Convertir degrés en radians avant trigonométrie
# [OK] Utiliser cmath pour nombres complexes
# [OK] Utiliser math.isqrt() pour racine carrée entière (3.8+)
# [OK] Utiliser math.prod() pour produits (3.8+)
# [OK] Vérifier domaine avant sqrt, log, asin, etc.
# [OK] Utiliser atan2() au lieu de atan() pour angles

# [X] Ne pas comparer floats avec ==
# [X] Ne pas oublier de convertir deg->rad
# [X] Ne pas utiliser ** pour très grandes puissances (overflow)
# [X] Ne pas ignorer les ValueError pour domaines invalides
# [X] Ne pas mélanger radians et degrés


[OK] CHEAT SHEET RAPIDE


# Basiques
math.sqrt(x)              # √x
math.pow(x, y)            # x^y
math.exp(x)               # e^x
math.log(x)               # ln(x)
math.log10(x)             # log₁₀(x)

# Arrondi
math.ceil(x)              # [LEFT_CEILING]x[RIGHT_CEILING]
math.floor(x)             # [LEFT_FLOOR]x[RIGHT_FLOOR]
math.trunc(x)             # Tronquer

# Trigonométrie (radians!)
math.sin(x), math.cos(x), math.tan(x)
math.asin(x), math.acos(x), math.atan(x)
math.atan2(y, x)          # atan(y/x) avec quadrant

# Distance
math.hypot(x, y)          # √(x²+y²)

# Conversions
math.radians(deg)         # deg -> rad
math.degrees(rad)         # rad -> deg

# Constantes
math.pi                   # π ≈ 3.14159
math.e                    # e ≈ 2.71828
math.tau                  # τ = 2π ≈ 6.28318

# Tests
math.isnan(x)             # x est NaN?
math.isinf(x)             # x est infini?
math.isfinite(x)          # x est fini?
math.isclose(a, b)        # a ≈ b?

# Complexes (cmath)
abs(z)                    # Module
cmath.phase(z)            # Argument
cmath.polar(z)            # (r, θ)
cmath.rect(r, theta)      # r·e^(iθ)


[OK] RESSOURCES


# Documentation math: https://docs.python.org/3/library/math.html
# Documentation cmath: https://docs.python.org/3/library/cmath.html
# IEEE 754 floating point standard
# NumPy pour calculs numériques: https://numpy.org/
# SciPy pour calculs scientifiques: https://scipy.org/