def merge_at(ms, keys, values, i):
        """
        Merge the two runs at stack indices i and i+1.

        An updated MergeState is returned.
        """
        n = ms.n
        assert n >= 2
        assert i >= 0
        assert i == n - 2 or i == n - 3

        ssa, na = ms.pending[i]
        ssb, nb = ms.pending[i + 1]
        assert na > 0 and nb > 0
        assert ssa + na == ssb

        # Record the length of the combined runs; if i is the 3rd-last
        # run now, also slide over the last run (which isn't involved
        # in this merge).  The current run i+1 goes away in any case.
        ms.pending[i] = MergeRun(ssa, na + nb)
        if i == n - 3:
            ms.pending[i + 1] = ms.pending[i + 2]
        ms = merge_pop(ms)

        # Where does b start in a?  Elements in a before that can be
        # ignored (already in place).
        k = gallop_right(keys[ssb], keys, ssa, ssa + na, ssa)
        # [k, ssa + na) remains to be merged
        na -= k - ssa
        ssa = k
        if na == 0:
            return ms

        # Where does a end in b?  Elements in b after that can be
        # ignored (already in place).
        k = gallop_left(keys[ssa + na - 1], keys, ssb, ssb + nb, ssb + nb - 1)
        # [ssb, k) remains to be merged
        nb = k - ssb

        # Merge what remains of the runs, using a temp array with
        # min(na, nb) elements.
        if na <= nb:
            return merge_lo(ms, keys, values, ssa, na, ssb, nb)
        else:
            return merge_hi(ms, keys, values, ssa, na, ssb, nb)

        Merge the two runs at stack indices i and i+1.

        An updated MergeState is returned.
        
numba/misc/timsort.py