def unlabeled(vertices):
    """
    A generator which generates all unlabeled (structurally different) graphs with a given number of vertices.

    This generator uses an algorithm described in the paper:
    R. C. Read, Everyone a Winner or How to Avoid Isomorphism When Cataloging Combinatorial Configurations, Annals of
    Discrete Mathematics 2 (1978) 107-120

    :param vertices: The number of vertices for which to generate unlabeled graphs over.
    """
    g0 = {}
    complete = vertices * (vertices - 1) / 2

    for i in range(1, vertices + 1, 1):
        g0[i] = []
    yield g0
    Lm = [g0]
    while graph.edgecount(Lm[0]) < complete:
        L = []
        for g in Lm:
            for trial in augmenter(g):
                if len(L) > 0:
                    if code(trial) < code(L[-1]) and is_canonical(trial) == True:
                        L.append(trial)
                        yield (L[-1])
                elif is_canonical(trial) == True:
                    L.append(trial)
                    yield (L[-1])
        Lm = L
        L = []
def unlabeled_by_edge_count(vertices):
    g0 = {}
    complete = vertices * (vertices - 1) / 2

    for i in range(1, vertices + 1, 1):
        g0[i] = []
    Lm = [g0]
    yield Lm
    while graph.edgecount(Lm[0]) < complete:
        L = []
        for g in Lm:
            for trial in augmenter(g):
                if len(L) > 0:
                    # print "code trial(" + str(code(trial)) + "), code L[-1] (" + str(code( (L[-1])))
                    if code(trial) < code(L[-1]) and is_canonical(trial) == True:
                        L.append(trial)
                elif is_canonical(trial):
                    L.append(trial)
        Lm = L
        yield Lm
def unlabeled_complement(vertices):
    """
    A slightly more efficient generator which generates all unlabeled (structurally different) graphs with a given
    number of vertices.

    This generator is equivalent to the 'unlabeled' generator except that it utilizes small shortcut by only generating
    the first half of the unlabeled graphs using the algorithm in 'unlabeled.' It generates the second half by taking
    the complement of all graphs in the first half. The complement in this case simply meaning: "for every pair of
    vertices with an edge, remove that edge and for every pair of vertices with no edge, add an edge."

    :param vertices: The number of vertices for which to generate unlabeled graphs over.
    """

    g0 = {}

    edgeclasses = vertices * (vertices - 1) / 2 + 1
    firsthalf = edgeclasses / 2
    odd = edgeclasses % 2
    print "graphs complement: odd=", odd, "max edges: ", edgeclasses - 1

    # initialize the first graph with the number of vertices but no edges
    for i in range(1, vertices + 1, 1):
        g0[i] = []

    # yield the first graph, then yield it's compliment
    yield g0
    yield graph.complement(g0)

    # Start with a list on m (in this case 0) edges.
    Lm = [g0]

    while graph.edgecount(Lm[0]) < firsthalf - 1:

        # start with an empty list (L) on m+1 edges
        L = []

        # for each graphs graph on m edges, add an edge in all possible ways
        for g in Lm:
            # for each graph generated by adding a single edge
            for trial in augmenter(g):
                if len(L) > 0:
                    if code(trial) < code(L[-1]) and is_canonical(trial) == True:
                        L.append(trial)
                        yield (L[-1])
                        yield (graph.complement(L[-1]))
                elif is_canonical(trial) == True:
                    L.append(trial)
                    yield (L[-1])
                    yield (graph.complement(L[-1]))
        print "Generating: graphs on", graph.edgecount(Lm[0]) + 1, "edges(" + str(len(L)) + ")"

        Lm = L
        L = []

    if odd == 1:
        L = []
        for g in Lm:
            for trial in augmenter(g):
                if len(L) > 0:
                    if code(trial) < code(L[-1]) and is_canonical(trial) == True:
                        L.append(trial)
                        yield (L[-1])
                elif is_canonical(trial) == True:
                    L.append(trial)
                    yield (L[-1])
        Lm = L
        L = []