# UNDF: UNDF-2026-000000631
# elixir-0001: Mix.Dep.Converger.topological_sort — O(N²) Enum.find in Enum.map

## Severity: HIGH

## Location
- `lib/mix/lib/mix/dep/converger.ex:33-35`
- Called from: `converge/4` at line 76, `Mix.Dep.Umbrella.converger.ex:75`

## Description
`topological_sort/1` takes the sorted atom list returned by `:digraph_utils.topsort/1`
and reconstructs the ordered `Mix.Dep` struct list by calling `Enum.find/2` (O(N) linear
scan) inside `Enum.map/2` (O(N) loop). The result is O(N²) where N = total flattened
dependency count for the Mix project.

`topological_sort` is called on every invocation of `Mix.Dep.Converger.converge/4`,
which runs during `mix deps.get`, `mix deps.compile`, `mix compile`, and umbrella builds.
For large Mix projects with hundreds of transitive dependencies (common in Erlang/Elixir
umbrella apps), this is a visible compilation bottleneck.

## Root Cause
```elixir
# converger.ex:32-35
if apps = :digraph_utils.topsort(graph) do
  Enum.map(apps, fn app ->
    Enum.find(deps, fn %Mix.Dep{app: other_app} -> app == other_app end)  # O(N) per app
  end)
```

`:digraph_utils.topsort/1` returns a list of atom application names.
`deps` is a list of N `Mix.Dep` structs.
For each of the N apps, `Enum.find` walks up to N deps → O(N²) total.

## Fix
Build a map from app atom to dep struct before the loop, then do O(1) lookups:

```elixir
if apps = :digraph_utils.topsort(graph) do
  dep_index = Map.new(deps, fn %Mix.Dep{app: app} = dep -> {app, dep} end)
  Enum.map(apps, fn app -> dep_index[app] end)
```

`Map.new/2` is O(N), `dep_index[app]` is O(1), total O(N).

## Complexity
| | Before | After |
|---|---|---|
| topological_sort | O(N²) | O(N) |
| per mix compile | O(N²) | O(N) |

## Impact
A Mix umbrella application with 200 apps triggers 200 × 200 = 40,000 comparisons per
compile. With N=500 (Nerves embedded frameworks, large Phoenix monorepos) that is
250,000 comparisons. The fix reduces this to 500 comparisons.
