showSubstitution :: Substitution -> String
showSubstitution = (\x -> if null x then "true" else x)
                        . intercalate ",\n"
                        -- convert equivalent variables to chain of equals and possibly the last one equal to the resulting structure
                        . map (\(k, v) -> intercalate ", " . filter (not . null) $ chainEqual k:lastEqual (last k) v)
                        . varsToShow
    where
        chainEqual :: [String] -> String
        chainEqual k = intercalate ", " . map (\(x, y) -> x ++ " = " ++ y) $ zip k (tail k)

        lastEqual :: String -> (Maybe Term) -> [String]
        lastEqual _ Nothing = []
        lastEqual v (Just t) = [v ++ " = " ++ showTerm t]

        getTermVars :: Term -> Set.Set String
        getTermVars (Assignable _ s) = if isPrefixOf "$" s then Set.singleton s else Set.empty
        getTermVars (Structure _ args) = foldr Set.union Set.empty . map getTermVars $ args

        onlyExternalVars :: Substitution -> Set.Set String
        -- filter out wildcards and internally renamed variables and then filter out empty resulting sets or sets with only one unexpanded single variable.
        -- Put all the remaining variables together
        onlyExternalVars = foldr (\(k, _) a -> Set.union k a) Set.empty
                        . filter (\(k, v) -> not $ Set.null k || Set.size k == 1 && isNothing v)
                        . Map.foldrWithKey (\k v a -> (Set.filter (not . isPrefixOf "$") k, v):a) []

        necessaryVars :: Substitution -> Set.Set String -> Set.Set String -> Set.Set String
        -- take the onlyExternalVars, and check what internal variables they depend on.  Then for the newly found variables repeat the
        -- process until no new variables are found.
        necessaryVars subst vars result = if Set.null news then result else newsRecursive
            where
                terms = catMaybes . Map.foldrWithKey (\_ v a -> v:a) [] . Map.filterWithKey (\k _ -> not . Set.null $ Set.intersection k vars) $ subst
                deps = foldr Set.union Set.empty . map getTermVars $ terms
                news = Set.difference deps result
                newsRecursive = necessaryVars subst news (Set.union result news)

        varsToShow :: Substitution -> [([String], Maybe Term)]
        -- reduce the substitutions to a list, filtering out unnecessary variables and then
        -- filter out empty resulting sets or sets with only one unexpanded single variable.
        varsToShow subst = filter (\(k, v) -> not $ null k || length k == 1 && isNothing v)
                    . Map.foldrWithKey (\k v a -> (Set.toAscList . Set.filter (\s -> Set.member s necessaries) $ k, v):a) [] $ subst
                        where
                             exts = onlyExternalVars subst
                             necessaries = necessaryVars subst exts exts
showSubstitution :: Substitution -> String
showSubstitution = (\x -> if null x then "true" else x)
                        . intercalate ",\n"
                        -- convert equivalent variables to chain of equals and possibly the last one equal to the resulting structure
                        . map (\(k, v) -> intercalate ", " . filter (not . null) $ chainEqual k:lastEqual (last k) v)
                        . varsToShow
    where
        chainEqual :: [String] -> String
        chainEqual k = intercalate ", " . map (\(x, y) -> x ++ " = " ++ y) $ zip k (tail k)

        lastEqual :: String -> (Maybe Term) -> [String]
        lastEqual _ Nothing = []
        lastEqual v (Just t) = [v ++ " = " ++ showTerm t]

        getTermVars :: Term -> Set.Set String
        getTermVars (Assignable _ s) = if isPrefixOf "$" s then Set.singleton s else Set.empty
        getTermVars (Structure _ args) = foldr Set.union Set.empty . map getTermVars $ args

        onlyExternalVars :: Substitution -> Set.Set String
        -- filter out wildcards and internally renamed variables and then filter out empty resulting sets or sets with only one unexpanded single variable.
        -- Put all the remaining variables together
        onlyExternalVars = foldr (\(k, _) a -> Set.union k a) Set.empty
                        . filter (\(k, v) -> not $ Set.null k || Set.size k == 1 && isNothing v)
                        . Map.foldrWithKey (\k v a -> (Set.filter (not . isPrefixOf "$") k, v):a) []

        necessaryVars :: Substitution -> Set.Set String -> Set.Set String -> Set.Set String
        -- take the onlyExternalVars, and check what internal variables they depend on.  Then for the newly found variables repeat the
        -- process until no new variables are found.
        necessaryVars subst vars result = if Set.null news then result else newsRecursive
            where
                terms = catMaybes . Map.foldrWithKey (\_ v a -> v:a) [] . Map.filterWithKey (\k _ -> not . Set.null $ Set.intersection k vars) $ subst
                deps = foldr Set.union Set.empty . map getTermVars $ terms
                news = Set.difference deps result
                newsRecursive = necessaryVars subst news (Set.union result news)

        varsToShow :: Substitution -> [([String], Maybe Term)]
        -- reduce the substitutions to a list, filtering out unnecessary variables and then
        -- filter out empty resulting sets or sets with only one unexpanded single variable.
        varsToShow subst = filter (\(k, v) -> not $ null k || length k == 1 && isNothing v)
                    . Map.foldrWithKey (\k v a -> (Set.toAscList . Set.filter (\s -> Set.member s necessaries) $ k, v):a) [] $ subst
                        where
                             exts = onlyExternalVars subst
                             necessaries = necessaryVars subst exts exts
showSubstitution = (\x -> if null x then "true" else x)
                        . intercalate ",\n"
                        -- convert equivalent variables to chain of equals and possibly the last one equal to the resulting structure
                        . map (\(k, v) -> intercalate ", " . filter (not . null) $ chainEqual k:lastEqual (last k) v)
                        . varsToShow
    where
        chainEqual :: [String] -> String
        chainEqual k = intercalate ", " . map (\(x, y) -> x ++ " = " ++ y) $ zip k (tail k)

        lastEqual :: String -> (Maybe Term) -> [String]
        lastEqual _ Nothing = []
        lastEqual v (Just t) = [v ++ " = " ++ showTerm t]

        getTermVars :: Term -> Set.Set String
        getTermVars (Assignable _ s) = if isPrefixOf "$" s then Set.singleton s else Set.empty
        getTermVars (Structure _ args) = foldr Set.union Set.empty . map getTermVars $ args

        onlyExternalVars :: Substitution -> Set.Set String
        -- filter out wildcards and internally renamed variables and then filter out empty resulting sets or sets with only one unexpanded single variable.
        -- Put all the remaining variables together
        onlyExternalVars = foldr (\(k, _) a -> Set.union k a) Set.empty
                        . filter (\(k, v) -> not $ Set.null k || Set.size k == 1 && isNothing v)
                        . Map.foldrWithKey (\k v a -> (Set.filter (not . isPrefixOf "$") k, v):a) []

        necessaryVars :: Substitution -> Set.Set String -> Set.Set String -> Set.Set String
        -- take the onlyExternalVars, and check what internal variables they depend on.  Then for the newly found variables repeat the
        -- process until no new variables are found.
        necessaryVars subst vars result = if Set.null news then result else newsRecursive
            where
                terms = catMaybes . Map.foldrWithKey (\_ v a -> v:a) [] . Map.filterWithKey (\k _ -> not . Set.null $ Set.intersection k vars) $ subst
                deps = foldr Set.union Set.empty . map getTermVars $ terms
                news = Set.difference deps result
                newsRecursive = necessaryVars subst news (Set.union result news)

        varsToShow :: Substitution -> [([String], Maybe Term)]
        -- reduce the substitutions to a list, filtering out unnecessary variables and then
        -- filter out empty resulting sets or sets with only one unexpanded single variable.
        varsToShow subst = filter (\(k, v) -> not $ null k || length k == 1 && isNothing v)
                    . Map.foldrWithKey (\k v a -> (Set.toAscList . Set.filter (\s -> Set.member s necessaries) $ k, v):a) [] $ subst
                        where
                             exts = onlyExternalVars subst
                             necessaries = necessaryVars subst exts exts