def narrow_region(frontier, target, max_states=100):
    ''' Given a frontier to start with, run through the largest powers of BASE
        until we get a decent number of starting states

        >>> base = 2
        >>> target = IntegerBaseFixed(1031 * 1093, None, None, base=2)
        >>> num_digits = target_to_max_factor_num_digits(target, base=2, moe=0)
        >>> frontier = generate_starting_products(num_digits, base=2)
        >>> frontier = narrow_region(frontier, target, 5)
        >>> for prod in frontier:
        ...     print prod.num_fixed_digits_left, prod.num_fixed_digits_right, prod
        1 6 10000100001x10000100001=100010000110001000001
        1 6 10000000001x10001000001=100010000100001000001
        1 7 10000000001x10000110001=100001100100000110001
        1 7 10000010001x10000100001=100001100101000110001
        1 7 10000010001x10000110001=100010000101101000001
    '''
    frontier = deque(frontier)
    while len(frontier) < max_states:
        prod = frontier.popleft()
        assert prod.base == target.base
        children = generate_children(prod, 'r')

        for child in children:
            if child.as_int == target.as_int:
                return deque([child])
            if has_potential_children(child, target):
                frontier.append(child)
    return frontier
def factorize(target, base=BASE):
    ''' Simple breadth first factorization algorithm

        >>> factorize(143, 2)
        (11, 13)

        >>> factorize(169, 2)
        (13, 13)

        >>> factorize(289, 2)
        (17, 17)

        >>> factorize(323, 2)
        (17, 19)

        >>> factorize(309485009822787627980424653, 2)
        (17592186044443L, 17592186044471L)
    '''
    target = IntegerBaseFixed(target, None, None, base)
    num_digits = target_to_max_factor_num_digits(target, base=base, moe=0)
    frontier = deque(generate_starting_products(num_digits, base))

    # Now narrow starting from the right, in case we have a very low or very
    # high factor
    frontier = narrow_region(frontier, target, max_states=100)
    if len(frontier) == 1:
        prod = frontier[0]
        assert prod.as_int == target.as_int
        p, q = prod.p.as_int, prod.q.as_int
        return order(p, q)

    while len(frontier):
        prod = frontier.popleft()

        if prod.num_fixed_digits_left > prod.num_fixed_digits_right:
            children = generate_children(prod, 'r')
        else:
            children = generate_children(prod, 'l')

        for child in children:
            if child.as_int == target.as_int:
                p, q = child.p.as_int, child.q.as_int
                return order(p, q)
            if has_potential_children(child, target):
                frontier.append(child)

    print 'No factorization found!'