Given a set of integers _S_, the _closure of S under multiplication_ is the smallest set that contains _S_ and such that for any _x, y_ in the closure of _S_, the product _x * y_ is also in the closure of _S_. 

Example 1: Given `S = {2}`, the closure of `S` is the set `{2, 4, 8, 16, 32, 64, ... }`.

Example 2: Given `S = {1, 2, 3}`, the closure of `S` is the set `{1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 27, 32, 36, ...}`. This set contains, e.g, `6` because `6 = 2 * 3`. It further contains `12` and `18` because `12 = 2 * 6` and `18 = 3 * 6`. Etc. 

Example 3: Given `S` the set of prime numbers, the closure of `S` is the set of all positive integers greater than or equal to `2`. 

Your task is two write a generator function that generates the members of the closure of any given _finite_ set of positive numbers `S` in _ascending order_.

Difficulty: interview

Starter code:

def closure_gen(*s):
	

Solutions:
from heapq import heappush, heappop

def closure_gen(*s):
    q = sorted(s)
    m = set(s)
    while q:
        curr = heappop(q)
        yield curr
        for i in s:
            t = curr * i
            if t not in m:
                heappush(q, t)
                m.add(t)

from heapq import *

def closure_gen(*s):
    keys,seen=set(s),set(s)
    q=sorted(seen)
    while q:
        curr=heappop(q)
        for next in [v for v in [curr*i for i in keys] if not v in seen]:
            heappush(q,next)
            seen.add(next)
        yield curr            


def closure_gen(*s):
    if not s: return
    if s[0] == 1:
        yield 1
        s = s[1:]
        if not s:
            return
    indices = {x: 0 for x in s}
    seq = [1]
    while True:
        m = float('inf')
        for x, i in indices.items():
            m = min(m, seq[i] * x)
        yield m
        seq.append(m)
        for x, i in indices.items():
            if seq[i] * x == m:
                indices[x] += 1

def closure_gen(*s):
    print(s)
    #Special cases:
    if s==(): return
    if s==(1,): yield 1;return
    if len(s)==1:
        v0,vi=s[0],s[0]
        while True: yield vi;vi*=v0
    s=list(s)
    for i in [0,1]:
        if i in s:yield i;s.remove(i)

    #=== Main ====
    queue=[1]
    seen=set(queue)
    
    def next_value(i,si,rank):
        while True:
            val=queue[rank]*si
            if val in seen:
                rank+=1
                continue
            break
        seen.add(val)            
        return val,i,si,rank            
        
    queues=[next_value(i,si,0) for i,si in enumerate(s)]

    while True:
        vn,i,si,rank=min(queues)
        queue.append(vn)
        yield vn
        queues[i]=next_value(i,si,rank+1)


from heapq import heappush, heappop, heapify

def closure_gen(*s):
    if not s:
        return None
    set_s = set(s)
    heap_s = [i for i in s]
    heapify(heap_s)
    while set_s:
        current = heappop(heap_s)
        new_values = {current * i for i in s}.difference(set_s)
        for i in new_values:
            heappush(heap_s, i)
        set_s.update(new_values)
        set_s.discard(current)
        yield current


import heapq

def closure_gen(*s):
    if not s: raise StopIteration()
    
    h = set(s)  # set of queued elements (for faster inclusion tests)
    q = list(h)
    heapq.heapify(q)
    
    if q == [1]:
        yield 1
        raise StopIteration()
    
    while True:
        x = heapq.heappop(q)
        h.remove(x)
        yield x

        for y in s:
            z = x * y
            if y != 1 and z not in h:
                heapq.heappush(q, z)
                h.add(z)

import heapq

def closure_gen(*s):
    s=set(sorted(s))
    buf=list(s)
    heapq.heapify(buf)
    while True:
        if len(buf)==0:raise StopIteration
        val=heapq.heappop(buf)
        while len(buf)>0 and buf[0]==val:heapq.heappop(buf)
        for v in s:
            if v<=1:continue
            heapq.heappush(buf,val*v)
        yield val

from heapq import heapify, heappop, heappush


def closure_gen(*s):
    heap = [*s]
    heapify(heap)
    numbers = {*s}

    while heap:
        n = heappop(heap)

        yield n

        to_add = {n * i for i in s} - numbers
        numbers |= to_add

        for n in to_add:
            heappush(heap, n)

def closure_gen(*s):
    s = [*s]

    if 1 in s:
        s.remove(1)
        yield 1

    queue = [(n, n, 0) for n in s]
    numbers = [1]

    while queue:
        prod, *_ = min(queue)
        yield prod

        numbers.append(prod)
        for i, (p, n, d) in enumerate(queue):
            if p <= prod:
                d += p == prod
                queue[i] = numbers[d] * n, n, d

def closure_gen(*seq):
    if not seq : return 
    if seq == (1,) : yield 1 ; return
    if 1 in seq : yield 1
    seq = [i for i in seq if i != 1]
    d, ind, li = {i: [i] for i in seq}, {i: 0 for i in seq}, []
    while True:
        min_ = min(d[i][0] for i in d if d[i])
        li.append(min_)
        yield min_
        for i in d:
            if d[i][0] == min_ : d[i].pop(0)
            if not d[i]:
                d[i].append(li[ind[i]] * i)
                ind[i] += 1