Powersort as implemented in CPython's listsort.c: every
run stays exactly where it is in the input array A — nothing ever moves to a second
full-size array. When two adjacent runs on the merge stack are combined, only the shorter
of the two is copied into a scratch buffer of at most ⌈n/2⌉ elements; the merge then
writes the combined result back into the gap the shorter run left behind (left-to-right if the
left run was shorter, right-to-left if the right run was). Pushing a new run costs nothing — it
already sits in A. This view shows exactly that data movement to and from the buffer;
it does not depict CPython's galloping mode, which only changes how fast comparisons find
the next element, not where data moves.
press play to run listsort Powersort.
detected runsmerged run (output of a merge)buffer empty (between merges)not yet scanned or a gap left behind after its run moved to the buffer2 3 1 — run-boundary powers (shown once computed, removed once merged away)
CPython Powersort
procedure ListsortPowersort(A[0, n)): run := next run in A▹ runs never move — they always stay in A pending.push(run)while more runs remain: run := next run in A p := power of the boundary between pending.top and runwhile pending.size ≥ 2 and pending.second.power > p:merge_at(pending.second, pending.top)▹ shorter side → scratch buffer; result fills the gap pending.top.power := p pending.push(run)▹ free — run already sits in Awhile pending.size ≥ 2:▹ final cascadeif pending.size ≥ 3 and pending.third.len < pending.top.len:merge_at(pending.third, pending.second)▹ keeps the final cascade balancedelse:merge_at(pending.second, pending.top)▹ done: A[0, n) is sorted; the scratch buffer is free again