from argparse import ArgumentParser
import random

parser = ArgumentParser(
    description="Create random tadpole graphs and their unions; tadpoles are merged at their leaf (or anywhere if they're cycles"
)

parser.add_argument("-n", type=int)
parser.add_argument("--lo", type=int, help="lower bound on the cycle/clique size")
parser.add_argument("--hi", type=int, help="upper bound on the cycle/clique size")
parser.add_argument("--chords", action="store_true")
parser.add_argument("--tadpoles", type=int, default=1, help="the number of tadpoles")
parser.add_argument("--seed", type=int, default=0)

args = parser.parse_args()
random.seed(args.seed)
n = args.n
if args.hi is None:
    args.hi = n


def tadpole(u, v, s, chords=False):
    """Create tadpole starting from u, using the vertices in u..v and with cycle size s.
       If chords==True, also add "stripes" across the tadpole's body (so as to produce
       many vertices of degree > 2 to allow many digon-free walks.
    """
    assert s >= 3
    assert v - s + 1 > u
    w = v - s + 1  # the degree-3 vertex, which cannot be u itself
    edges = list(zip(range(u, w), range(u + 1, w + 1)))  # the stick; can be empty
    edges.extend(zip(range(w, w + s), range(w + 1, w + s)))
    edges.append((v, w))
    if chords:
        uu, vv = w + 1, w + s - 2
        while uu < vv - 1:
            edges.append((uu, vv))
            uu += 1
            vv -= 1
    return edges


edges: list[tuple[int, int]] = []

k = args.tadpoles


def random_partition(total, parts, lo=4):
    if parts < 1:
        raise ValueError("parts must be at least 1")

    min_required = parts * lo
    if total < min_required:
        raise ValueError(
            f"total must be at least {min_required} to satisfy the minimum value {lo} per part"
        )

    if parts == 1:
        return [total]

    remaining = total - min_required

    # Stars and bars: choose (parts - 1) cut points from (remaining + parts - 1) positions
    cuts = sorted(random.sample(range(1, remaining + parts), parts - 1))
    full_cuts = [0] + cuts + [remaining + parts]
    raw_parts = [full_cuts[i + 1] - full_cuts[i] - 1 for i in range(parts)]

    result = [p + lo for p in raw_parts]
    assert sum(result) == total, f"Sum mismatch: expected {total}, got {sum(result)}"
    return result


result = random_partition(n - 1, k)

edges = []
tadpole_start = 2
for i in range(args.tadpoles):
    edges.append((1, tadpole_start))
    tadpole_edges = tadpole(
        tadpole_start,
        tadpole_start + result[i] - 1,
        random.randrange(3, min(result[i], args.hi + 1)),
        chords=args.chords
    )
    tadpole_start += result[i]
    edges.extend(tadpole_edges)

print(n, len(edges))
for u, v in edges:
    print(*sorted([u, v]))
