-- cut to the chase scene; it happens

sepBindsByDropPoint dflags is_case drop_pts floaters
  = go floaters (map (\fvs -> (fvs, [])) (emptyVarSet : drop_pts))
  where
    go :: FloatInBinds -> [DropBox] -> [FloatInBinds]
        -- The *first* one in the argument list is the drop_here set
        -- The FloatInBinds in the lists are in the reverse of
        -- the normal FloatInBinds order; that is, they are the right way round!

    go [] drop_boxes = map (reverse . snd) drop_boxes

    go (bind_w_fvs@(FB bndrs bind_fvs bind) : binds) drop_boxes@(here_box : fork_boxes)
        = go binds new_boxes
        where
          -- "here" means the group of bindings dropped at the top of the fork

          (used_here : used_in_flags) = [ fvs `intersectsVarSet` bndrs
                                        | (fvs, _) <- drop_boxes]

          drop_here = used_here || not can_push

                -- For case expressions we duplicate the binding if it is
                -- reasonably small, and if it is not used in all the RHSs
                -- This is good for situations like
                --      let x = I# y in
                --      case e of
                --        C -> error x
                --        D -> error x
                --        E -> ...not mentioning x...

          n_alts      = length used_in_flags
          n_used_alts = count id used_in_flags -- returns number of Trues in list.

          can_push = n_used_alts == 1           -- Used in just one branch
                   || (is_case &&               -- We are looking at case alternatives
                       n_used_alts > 1 &&       -- It's used in more than one
                       n_used_alts < n_alts &&  -- ...but not all
                       floatIsDupable dflags bind) -- and we can duplicate the binding

          new_boxes | drop_here = (insert here_box : fork_boxes)
                    | otherwise = (here_box : new_fork_boxes)

          new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe fork_boxes used_in_flags

          insert :: DropBox -> DropBox
          insert (fvs,drops) = (fvs `unionVarSet` bind_fvs, bind_w_fvs:drops)

          insert_maybe box True  = insert box
          insert_maybe box False = box

    go _ _ = panic "sepBindsByDropPoint/go"
sepBindsByDropPoint dflags is_case drop_pts floaters
  = go floaters (map (\fvs -> (fvs, [])) (emptyVarSet : drop_pts))
  where
    go :: FloatInBinds -> [DropBox] -> [FloatInBinds]
        -- The *first* one in the argument list is the drop_here set
        -- The FloatInBinds in the lists are in the reverse of
        -- the normal FloatInBinds order; that is, they are the right way round!

    go [] drop_boxes = map (reverse . snd) drop_boxes

    go (bind_w_fvs@(FB bndrs bind_fvs bind) : binds) drop_boxes@(here_box : fork_boxes)
        = go binds new_boxes
        where
          -- "here" means the group of bindings dropped at the top of the fork

          (used_here : used_in_flags) = [ fvs `intersectsVarSet` bndrs
                                        | (fvs, _) <- drop_boxes]

          drop_here = used_here || not can_push

                -- For case expressions we duplicate the binding if it is
                -- reasonably small, and if it is not used in all the RHSs
                -- This is good for situations like
                --      let x = I# y in
                --      case e of
                --        C -> error x
                --        D -> error x
                --        E -> ...not mentioning x...

          n_alts      = length used_in_flags
          n_used_alts = count id used_in_flags -- returns number of Trues in list.

          can_push = n_used_alts == 1           -- Used in just one branch
                   || (is_case &&               -- We are looking at case alternatives
                       n_used_alts > 1 &&       -- It's used in more than one
                       n_used_alts < n_alts &&  -- ...but not all
                       floatIsDupable dflags bind) -- and we can duplicate the binding

          new_boxes | drop_here = (insert here_box : fork_boxes)
                    | otherwise = (here_box : new_fork_boxes)

          new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe fork_boxes used_in_flags

          insert :: DropBox -> DropBox
          insert (fvs,drops) = (fvs `unionVarSet` bind_fvs, bind_w_fvs:drops)

          insert_maybe box True  = insert box
          insert_maybe box False = box

    go _ _ = panic "sepBindsByDropPoint/go"
sepBindsByDropPoint dflags is_case drop_pts floaters
  = go floaters (map (\fvs -> (fvs, [])) (emptyVarSet : drop_pts))
  where
    go :: FloatInBinds -> [DropBox] -> [FloatInBinds]
        -- The *first* one in the argument list is the drop_here set
        -- The FloatInBinds in the lists are in the reverse of
        -- the normal FloatInBinds order; that is, they are the right way round!

    go [] drop_boxes = map (reverse . snd) drop_boxes

    go (bind_w_fvs@(FB bndrs bind_fvs bind) : binds) drop_boxes@(here_box : fork_boxes)
        = go binds new_boxes
        where
          -- "here" means the group of bindings dropped at the top of the fork

          (used_here : used_in_flags) = [ fvs `intersectsVarSet` bndrs
                                        | (fvs, _) <- drop_boxes]

          drop_here = used_here || not can_push

                -- For case expressions we duplicate the binding if it is
                -- reasonably small, and if it is not used in all the RHSs
                -- This is good for situations like
                --      let x = I# y in
                --      case e of
                --        C -> error x
                --        D -> error x
                --        E -> ...not mentioning x...

          n_alts      = length used_in_flags
          n_used_alts = count id used_in_flags -- returns number of Trues in list.

          can_push = n_used_alts == 1           -- Used in just one branch
                   || (is_case &&               -- We are looking at case alternatives
                       n_used_alts > 1 &&       -- It's used in more than one
                       n_used_alts < n_alts &&  -- ...but not all
                       floatIsDupable dflags bind) -- and we can duplicate the binding

          new_boxes | drop_here = (insert here_box : fork_boxes)
                    | otherwise = (here_box : new_fork_boxes)

          new_fork_boxes = zipWithEqual "FloatIn.sepBinds" insert_maybe fork_boxes used_in_flags

          insert :: DropBox -> DropBox
          insert (fvs,drops) = (fvs `unionVarSet` bind_fvs, bind_w_fvs:drops)

          insert_maybe box True  = insert box
          insert_maybe box False = box

    go _ _ = panic "sepBindsByDropPoint/go"