def route(control=0, target=3, sites=4):
    if (type(sites) is not int or sites < 2 or control == target or
            control not in range(sites) or target not in range(sites)):
        raise ValueError("control and target must be distinct physical sites")
    layout = list(range(sites)); ops = []
    while abs(layout.index(control)-layout.index(target)) > 1:
        p = layout.index(control); step = 1 if layout.index(target) > p else -1
        q = p + step
        layout[p], layout[q] = layout[q], layout[p]
        ops.append(("SWAP", p, q, tuple(layout)))
    ops.append(("CNOT", layout.index(control), layout.index(target), tuple(layout)))
    return ops
ops = route()
reverse_ops = route(3, 0)
adjacent_ops = route(1, 2)
invalid_rejected = False
try:
    route(0, 0)
except ValueError:
    invalid_rejected = True
assert [op[0] for op in ops] == ["SWAP", "SWAP", "CNOT"]
assert abs(ops[-1][1]-ops[-1][2]) == 1
assert sorted(ops[-1][3]) == [0,1,2,3]
assert [op[0] for op in reverse_ops] == ["SWAP", "SWAP", "CNOT"]
assert [op[0] for op in adjacent_ops] == ["CNOT"] and invalid_rejected
print(f"PASS: 40 routing forward_ops={len(ops)} reverse_ops={len(reverse_ops)} adjacent_ops={len(adjacent_ops)}")
