NRZI와 맨체스터 문서의 구현 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""NRZ, NRZI, 맨체스터 인코딩의 구현과 자체 테스트.

문서: 41.NRZI와 맨체스터 (예시, 정의, 실행 추적, 카드), 43.인코딩 방식 비교,
      4.연습문제/43.인코딩 예제 사다리 (문제 1~3)
표현: 비트 하나를 앞 절반·뒤 절반 두 칸으로 나누고, 칸마다 신호 높이 0(낮음) 또는 1(높음)을 적는다.
      그림 문자열에서는 0 -> '_', 1 -> '‾'.
규칙:
  - NRZ: 1은 높음, 0은 낮음. 비트 안에서 바뀌지 않는다.
  - NRZI: 1이면 비트 한가운데서 지금 높이를 뒤집고, 0이면 지금 높이를 그대로 둔다. 처음 높이는 낮음.
  - 맨체스터: NRZ 높이 XOR 클럭. 클럭은 비트마다 앞 절반 0, 뒤 절반 1.
    그래서 0은 낮음→높음(올라감), 1은 높음→낮음(내려감).
"""

EXAMPLE_BITS = "0010111101000010"  # 40·41 문서의 예시 비트열


def nrz(bits):
    return [int(b) for b in bits for _ in range(2)]


def clock(n_bits):
    return [0, 1] * n_bits


def nrzi(bits, start=0):
    level = start
    out = []
    for b in bits:
        out.append(level)          # 앞 절반: 지금 높이
        if b == "1":
            level ^= 1             # 한가운데서 뒤집기
        out.append(level)          # 뒤 절반
    return out


def manchester(bits):
    return [d ^ c for d, c in zip(nrz(bits), clock(len(bits)))]


def decode_nrzi(levels):
    """한 비트의 두 칸이 다르면(한가운데서 바뀌었으면) 1, 같으면 0."""
    return "".join("1" if levels[i] != levels[i + 1] else "0" for i in range(0, len(levels), 2))


def decode_manchester(levels):
    """앞 절반의 클럭이 0이므로 앞 절반 높이가 곧 데이터다."""
    return "".join(str(levels[i]) for i in range(0, len(levels), 2))


def recover_clock(received, data_bits):
    """M' XOR Data = (Data XOR Clock) XOR Data = Clock."""
    return [m ^ d for m, d in zip(received, nrz(data_bits))]


def draw(levels):
    return "".join("‾" if x else "_" for x in levels)


def longest_flat(levels):
    """높이가 바뀌지 않고 이어지는 가장 긴 칸 수 (클럭을 맞출 단서가 없는 구간)."""
    best = run = 1
    for a, b in zip(levels, levels[1:]):
        run = run + 1 if a == b else 1
        best = max(best, run)
    return best


def transitions(levels):
    return sum(a != b for a, b in zip(levels, levels[1:]))


def main():
    b = EXAMPLE_BITS
    # 문서 그림 1과 같은 모양
    assert draw(nrz(b)) == "____‾‾__‾‾‾‾‾‾‾‾__‾‾________‾‾__"
    assert draw(manchester(b)) == "_‾_‾‾__‾‾_‾_‾_‾__‾‾__‾_‾_‾_‾‾__‾"
    assert draw(nrzi(b)) == "_____‾‾‾‾__‾‾__‾‾‾‾__________‾‾‾"
    print("[OK] 예시 비트열", b)
    print("     NRZ       ", draw(nrz(b)))
    print("     클럭      ", draw(clock(len(b))))
    print("     맨체스터  ", draw(manchester(b)))
    print("     NRZI      ", draw(nrzi(b)))

    # 맨체스터: 0은 올라감, 1은 내려감
    assert manchester("0") == [0, 1] and manchester("1") == [1, 0]
    # 되돌리기와 클럭 꺼내기 (모든 4~10비트 열)
    for n in range(1, 11):
        for x in range(2 ** n):
            s = format(x, f"0{n}b")
            assert decode_manchester(manchester(s)) == s
            assert decode_nrzi(nrzi(s)) == s
            assert decode_nrzi(nrzi(s, start=1)) == s
            assert recover_clock(manchester(s), s) == clock(n)
            # 맨체스터는 모든 비트 한가운데서 바뀐다
            m = manchester(s)
            assert all(m[2 * i] != m[2 * i + 1] for i in range(n))
            assert longest_flat(m) <= 2
    print("[OK] 1~10비트 모든 열: 복호 일치, M' XOR Data = Clock, 맨체스터는 매 비트 중앙 전이")

    # NRZI: 1이 아무리 이어져도 비트마다 전이, 0이 이어지면 평평
    assert longest_flat(nrzi("1" * 20)) == 2
    assert longest_flat(nrzi("0" * 20)) == 40
    assert longest_flat(nrz("1" * 20)) == 40 and longest_flat(nrz("0" * 20)) == 40
    print("[OK] NRZI: 1 20개는 비트마다 전이, 0 20개는 40칸 평평 (NRZ는 둘 다 평평)")

    # 효율: 맨체스터는 비트 하나에 신호 변화 칸이 두 개 -> 같은 신호 속도에서 데이터 속도 절반
    assert len(manchester(b)) == 2 * len(b)
    print("[OK] 맨체스터 효율 50%: 비트 16개에 신호 칸 32개")

    # 예제 사다리 문제 1~3 (4.연습문제/43.인코딩 예제 사다리)
    assert draw(nrz("1100")) == "‾‾‾‾____"
    assert draw(nrzi("1100")) == "_‾‾_____"
    assert draw(manchester("1100")) == "‾_‾__‾_‾"
    assert draw(nrzi("0110")) == "___‾‾___"
    assert draw(manchester("0110")) == "_‾‾_‾__‾"
    assert draw(nrzi("101100", start=1)) == "‾____‾‾_____"
    assert draw(manchester("101100")) == "‾__‾‾_‾__‾_‾"
    assert decode_manchester([0, 1, 1, 0, 1, 0, 0, 1, 0, 1]) == "01100"
    assert decode_nrzi([0, 0, 0, 1, 1, 0, 0, 0, 0, 1]) == "01101"
    print("[OK] 예제 사다리 문제 1~3의 답")

    # 43.인코딩 방식 비교 카드 C3: 맨체스터 1001
    assert draw(manchester("1001")) == "‾__‾_‾‾_"
    assert decode_manchester([1, 0, 0, 1, 0, 1, 1, 0]) == "1001"

    # 문서의 실행 추적 표: 10110
    assert draw(nrzi("10110")) == "_‾‾‾‾__‾‾‾"
    assert draw(manchester("10110")) == "‾__‾‾_‾__‾"
    print("[OK] 실행 추적 표 10110")

    # 카드 C3 (41): 1111 0000 의 맨체스터와 NRZI
    assert draw(manchester("11110000")) == "‾_‾_‾_‾__‾_‾_‾_‾"
    assert draw(nrzi("11110000")) == "_‾‾__‾‾_________"
    print("[OK] 카드 C3")
    print("ALL CHECKS PASSED")


if __name__ == "__main__":
    main()