-- | At each level the sets are 'partitioned' by every occuring
-- element. This is repeated recursively. At the deepest level
-- occuring pairs are tagged with their level. On the way back up,
-- the pairs occuring deepest, the one with the largest overlap,
-- are kept, and it is taken care to not use any set more often than
-- once.
recur :: (Ord b, Show a, Show b) =>
     (a -> S.Set b)
     -> Int
     -> [(SetId, a)]
     -> (b -> Bool)
     -> [(Level, ((SetId, a), (SetId, a)))]
recur ex level ys elpred =
  let partitions =
        M.unionsWith (++)
        [ M.fromList
          [ (element, [(id,x)])
          | element <- S.toList $ ex x
          , elpred element ]
        | (id,x) <- ys ]
  in
   -- trace ("partitions:\n" ++ show partitions)$
   keepLargestPairs $ concat $ map (pairs level) $
     M.toList partitions
  where
    pairs _ (_, [_]) = []
    pairs _ (_, [(aid, a),(bid, b)]) =
      [( S.size (S.intersection (ex a) (ex b))
       , ((aid, a), (bid, b)))]
    pairs level (el, partition) =
      let pairsWithLevel = recur ex (succ level) partition (>el)
          usedIds =
            S.fromList $ concat
            [ [aid,bid]
            | (_, ((aid, _), (bid, _))) <- pairsWithLevel]
          morePairs =
            [ (level, (a,b))
            | (a,b) <- pairSequence
                       [ x
                       | x@(id,_) <- partition
                       , S.notMember id usedIds]]
      in morePairs ++ pairsWithLevel
recur :: (Ord b, Show a, Show b) =>
     (a -> S.Set b)
     -> Int
     -> [(SetId, a)]
     -> (b -> Bool)
     -> [(Level, ((SetId, a), (SetId, a)))]
recur ex level ys elpred =
  let partitions =
        M.unionsWith (++)
        [ M.fromList
          [ (element, [(id,x)])
          | element <- S.toList $ ex x
          , elpred element ]
        | (id,x) <- ys ]
  in
   -- trace ("partitions:\n" ++ show partitions)$
   keepLargestPairs $ concat $ map (pairs level) $
     M.toList partitions
  where
    pairs _ (_, [_]) = []
    pairs _ (_, [(aid, a),(bid, b)]) =
      [( S.size (S.intersection (ex a) (ex b))
       , ((aid, a), (bid, b)))]
    pairs level (el, partition) =
      let pairsWithLevel = recur ex (succ level) partition (>el)
          usedIds =
            S.fromList $ concat
            [ [aid,bid]
            | (_, ((aid, _), (bid, _))) <- pairsWithLevel]
          morePairs =
            [ (level, (a,b))
            | (a,b) <- pairSequence
                       [ x
                       | x@(id,_) <- partition
                       , S.notMember id usedIds]]
      in morePairs ++ pairsWithLevel
recur ex level ys elpred =
  let partitions =
        M.unionsWith (++)
        [ M.fromList
          [ (element, [(id,x)])
          | element <- S.toList $ ex x
          , elpred element ]
        | (id,x) <- ys ]
  in
   -- trace ("partitions:\n" ++ show partitions)$
   keepLargestPairs $ concat $ map (pairs level) $
     M.toList partitions
  where
    pairs _ (_, [_]) = []
    pairs _ (_, [(aid, a),(bid, b)]) =
      [( S.size (S.intersection (ex a) (ex b))
       , ((aid, a), (bid, b)))]
    pairs level (el, partition) =
      let pairsWithLevel = recur ex (succ level) partition (>el)
          usedIds =
            S.fromList $ concat
            [ [aid,bid]
            | (_, ((aid, _), (bid, _))) <- pairsWithLevel]
          morePairs =
            [ (level, (a,b))
            | (a,b) <- pairSequence
                       [ x
                       | x@(id,_) <- partition
                       , S.notMember id usedIds]]
      in morePairs ++ pairsWithLevel