파동의 복소수 표현 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""파동의 복소수 표현 검증.

문서: 24.파동의 복소수 표현 (예시로 보기, 정의, 카드 C2·C3)
주장:
  1. f(t) = A sin(2πt/T + φ)는 주기가 T다. 2πt만 쓰면 t = 1에서 한 바퀴가 끝난다.
  2. 오일러 공식 e^{iθ} = cos θ + i sin θ (θ 여러 값에서 수치로 확인).
  3. x(t) = A e^{i(2πt/T + φ)}의 허수부가 A sin(2πt/T + φ), 실수부가 A cos(2πt/T + φ)다.
  4. |x(t)| = A로 늘 일정하다. 실수 사인은 0과 A 사이를 오간다.
  5. A = e^α로 두면 A e^{i(2πt/T+φ)} = e^{α+iφ} e^{i 2πt/T}. 예: A = 2, φ = π/3이면 α = ln 2.
  6. 한 주기 평균 전력: 실수 사인은 A²/2, 복소 표현은 A².
  7. 같은 진폭이라도 회전 방향(φ의 증가 방향)이 다른 두 파동 e^{+i2πt}, e^{-i2πt}는 실수부(cos)가 같아
     실수 한 줄로는 구별되지 않지만 복소수로는 구별된다.
"""
import cmath
import math


def x(t, A, T, phi):
    return A * cmath.exp(1j * (2 * math.pi * t / T + phi))


def main() -> None:
    A, T, phi = 2.0, 0.5, math.pi / 3
    # 1. 주기
    for t in [0.0, 0.13, 0.27, 0.41]:
        f0 = A * math.sin(2 * math.pi * t / T + phi)
        f1 = A * math.sin(2 * math.pi * (t + T) / T + phi)
        assert abs(f0 - f1) < 1e-12
    assert abs(math.sin(2 * math.pi * 1.0)) < 1e-12 and abs(math.sin(2 * math.pi * 0.5)) < 1e-12
    # 2. 오일러 공식
    for th in [0, 0.3, math.pi / 2, math.pi, 2.5, -1.1]:
        assert abs(cmath.exp(1j * th) - complex(math.cos(th), math.sin(th))) < 1e-12
    assert abs(cmath.exp(1j * math.pi) + 1) < 1e-12
    # 3, 4. 실수부와 허수부, 크기 일정
    for k in range(50):
        t = k * 0.0137
        z = x(t, A, T, phi)
        ang = 2 * math.pi * t / T + phi
        assert abs(z.imag - A * math.sin(ang)) < 1e-12
        assert abs(z.real - A * math.cos(ang)) < 1e-12
        assert abs(abs(z) - A) < 1e-12
    # 5. e^{α+iφ} e^{i2πt/T}
    alpha = math.log(A)
    assert abs(alpha - 0.693147) < 1e-6
    for t in [0.0, 0.1, 0.33]:
        lhs = x(t, A, T, phi)
        rhs = cmath.exp(alpha + 1j * phi) * cmath.exp(1j * 2 * math.pi * t / T)
        assert abs(lhs - rhs) < 1e-12
    # 6. 한 주기 평균 전력 (중점 규칙 수치 적분)
    N = 20000
    pr = sum((A * math.sin(2 * math.pi * (k + 0.5) / N + phi)) ** 2 for k in range(N)) / N
    pc = sum(abs(x((k + 0.5) * T / N, A, T, phi)) ** 2 for k in range(N)) / N
    assert abs(pr - A ** 2 / 2) < 1e-9 and abs(pc - A ** 2) < 1e-9
    # 7. 회전 방향
    for t in [0.1, 0.2, 0.37]:
        zp, zm = cmath.exp(1j * 2 * math.pi * t), cmath.exp(-1j * 2 * math.pi * t)
        assert abs(zp.real - zm.real) < 1e-12 and abs(zp.imag + zm.imag) < 1e-12
        assert abs(zp - zm) > 1e-3
    print("ALL CHECKS PASSED")


if __name__ == "__main__":
    main()