def scale_free_metabolic_network(compounds, reactions, reversible, m, n):
    """
    Uses a Barabasi-Alberts-like preferential attachment algorithm. Adopted from
    the networkx implementation.

    @param m: How many compounds a new reaction node links to

    @param n: How many reactions a new compound node links to
    """
    options = OptionsManager()
    logger = logging.getLogger("%s.scale_free_metabolic_network"\
        % (options.main_logger_name))
    graph = BipartiteMetabolicNetwork(name="scale-free model")
    # target nodes for reactions
    rxn_targets = []
    for i in xrange(m):
        comp = Compound("%s%d" % (options.compound_prefix, i))
        graph.add_compound(comp)
        rxn_targets.append(comp)
#    logger.debug("Targets for reactions: %s", rxn_targets)
    # target nodes for compounds
    comp_targets = []
    # biased lists for preferential attachment
    repeated_cmpds = []
    repeated_rxns = []
    # choose a number of reactions as reversible
    reversibles = random.sample(xrange(reactions), reversible)
    reversibles.sort()
    for i in xrange(n):
        if len(reversibles) > 0 and i == reversibles[0]:
            del reversibles[0]
            rxn = Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=True)
        else:
            rxn = Reaction("%s%d" % (options.reaction_prefix, i),\
                (), (), (), reversible=False)
        graph.add_reaction(rxn)
        comp_targets.append(rxn)
        for comp in rxn_targets:
            if random.random() < 0.5:
                logger.debug("Adding edge %s -> %s.", rxn.identifier,\
                    comp.identifier)
                graph.add_edge(rxn, comp, factor=0)
            else:
                logger.debug("Adding edge %s -> %s.", comp.identifier,\
                    rxn.identifier)
                graph.add_edge(comp, rxn, factor=0)
        repeated_cmpds.extend(rxn_targets)
        repeated_rxns.extend([rxn] * m)
#    logger.debug("Targets for compounds: %s", comp_targets)
    # current vertices being added
    current_rxn = n
    current_comp = m
    # to prevent reactions from consuming and producing the same compound
    new_targets = list()
    rm_targets = list()
    while (current_comp < compounds or current_rxn < reactions):
        if current_comp < compounds:
            source = Compound("%s%d" % (options.compound_prefix, current_comp))
            graph.add_compound(source)
            for rxn in comp_targets:
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        rxn.identifier)
                    graph.add_edge(source, rxn, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", rxn.identifier,\
                        source.identifier)
                    graph.add_edge(rxn, source, factor=0)
            repeated_rxns.extend(comp_targets)
            repeated_cmpds.extend([source] * n)
            comp_targets = set()
            while len(comp_targets) < n:
                rxn = random.choice(repeated_rxns)
                comp_targets.add(rxn)
            current_comp += 1
        if current_rxn < reactions:
            if len(reversibles) > 0 and current_rxn == reversibles[0]:
                del reversibles[0]
                source = Reaction("%s%d" % (options.reaction_prefix,\
                    current_rxn), (), (), (), reversible=True)
            else:
                source = Reaction("%s%d" % (options.reaction_prefix,\
                    current_rxn), (), (), (), reversible=False)
            graph.add_reaction(source)
            new_targets = list()
            rm_targets = list()
            for comp in rxn_targets:
                # same compound may not be in substrates and products
                if (comp in graph.predecessors(source)) or (comp in graph.successors(source)):
                    cmpd = random.choice(repeated_cmpds)
                    while cmpd in graph.predecessors(source) or cmpd in graph.successors(source) or cmpd in new_targets or cmpd in rxn_targets:
                        cmpd = random.choice(repeated_cmpds)
                    new_targets.append(cmpd)
                    rm_targets.append(comp)
                    continue
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        comp.identifier)
                    graph.add_edge(source, comp, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", comp.identifier,\
                        source.identifier)
                    graph.add_edge(comp, source, factor=0)
            for comp in rm_targets:
                rxn_targets.remove(comp)
            for comp in new_targets:
                rxn_targets.add(comp)
                if random.random() < 0.5:
                    logger.debug("Adding edge %s -> %s.", source.identifier,\
                        comp.identifier)
                    graph.add_edge(source, comp, factor=0)
                else:
                    logger.debug("Adding edge %s -> %s.", comp.identifier,\
                        source.identifier)
                    graph.add_edge(comp, source, factor=0)
            repeated_cmpds.extend(rxn_targets)
            repeated_rxns.extend([source] * m)
            rxn_targets = set()
            while len(rxn_targets) < m:
                comp = random.choice(repeated_cmpds)
                rxn_targets.add(comp)
            current_rxn += 1
    prune_network(graph, logger)
    return graph