def propagate_constraints(eq, wc):
    """
  Takes a basis of constraints and propagates them to produce all constraints.
  
  Takes dicts eq and wc where 
    y in eq[x] means that x is equal y  and 
    y in wc[x] means that y is complementary to x
  and produces eq_all and wc_all where
    eq_all[x] = {y | y == x} (set of *all* items equal to x)
    wc_all[x] = {y | y ~= x} (set of *all* items equal to x)
  
  NOTE: Assumes that if  y in eq[x]  =>  x in eq[y]  and likewise for wc.
  """
    keys = list(eq.keys())
    assert list(wc.keys()
        ) == keys, 'eq.keys() and wc.keys() must be equal to the set of items to constrain over'
    eq_all = {}
    wc_all = {}
    for x in keys:
        if x not in eq_all:
            eq_set = set(eq[x])
            eq_set.add(x)
            wc_set = set(wc[x])
            eq_done = set()
            wc_done = set()
            while eq_set - eq_done or wc_set - wc_done:
                for y in (eq_set - eq_done):
                    assert y in keys and y not in eq_all, (x, y)
                    eq_set.update(eq[y])
                    wc_set.update(wc[y])
                    eq_done.add(y)
                for y in (wc_set - wc_done):
                    assert y in keys and y not in eq_all, (x, y)
                    wc_set.update(eq[y])
                    eq_set.update(wc[y])
                    wc_done.add(y)
            for y in eq_set:
                eq_all[y] = eq_set
                wc_all[y] = wc_set
            for y in wc_set:
                eq_all[y] = wc_set
                wc_all[y] = eq_set
    return eq_all, wc_all
