avoidReinstalls :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)
avoidReinstalls p = trav go
  where
    go (PChoiceF qpn@(Q _ pn) i@(gr, _) cs)
      | p pn      = PChoiceF qpn i disableReinstalls
      | otherwise = PChoiceF qpn i cs
      where
        disableReinstalls =
          let installed = [ v | (I v (Inst _), _) <- toList cs ]
          in  P.mapWithKey (notReinstall installed) cs

        notReinstall vs (I v InRepo) _
          | v `elem` vs                = Fail (toConflictSet (Goal (P qpn) gr)) CannotReinstall
        notReinstall _  _            x = x
    go x          = x

-- | Always choose the first goal in the list next, abandoning all
-- other choices.
--
-- This is unnecessary for the default search strategy, because
-- it descends only into the first goal choice anyway,
-- but may still make sense to just reduce the tree size a bit.
avoidReinstalls :: (PN -> Bool) -> Tree (QGoalReasons, a) -> Tree (QGoalReasons, a)
avoidReinstalls p = trav go
  where
    go (PChoiceF qpn@(Q _ pn) i@(gr, _) cs)
      | p pn      = PChoiceF qpn i disableReinstalls
      | otherwise = PChoiceF qpn i cs
      where
        disableReinstalls =
          let installed = [ v | (I v (Inst _), _) <- toList cs ]
          in  P.mapWithKey (notReinstall installed) cs

        notReinstall vs (I v InRepo) _
          | v `elem` vs                = Fail (toConflictSet (Goal (P qpn) gr)) CannotReinstall
        notReinstall _  _            x = x
    go x          = x

-- | Always choose the first goal in the list next, abandoning all
-- other choices.
--
-- This is unnecessary for the default search strategy, because
-- it descends only into the first goal choice anyway,
-- but may still make sense to just reduce the tree size a bit.
avoidReinstalls p = trav go
  where
    go (PChoiceF qpn@(Q _ pn) i@(gr, _) cs)
      | p pn      = PChoiceF qpn i disableReinstalls
      | otherwise = PChoiceF qpn i cs
      where
        disableReinstalls =
          let installed = [ v | (I v (Inst _), _) <- toList cs ]
          in  P.mapWithKey (notReinstall installed) cs

        notReinstall vs (I v InRepo) _
          | v `elem` vs                = Fail (toConflictSet (Goal (P qpn) gr)) CannotReinstall
        notReinstall _  _            x = x
    go x          = x

-- | Always choose the first goal in the list next, abandoning all
-- other choices.
--
-- This is unnecessary for the default search strategy, because
-- it descends only into the first goal choice anyway,
-- but may still make sense to just reduce the tree size a bit.