void stadfa_to_tdfa_actions(ctx_t &ctx, tcmd_t **state_cmd, tcmd_t **final_cmd)
{
    tcpool_t &pool = ctx.dfa.tcpool;

    // Convert staDFA store/transfer/accept actions to tag commands:
    // map each (tag, index) pair to a unique tag version.
    tcmd_t *s = NULL, *f = NULL;
    tagver_t l, r;
    for (const stacmd_t *p = ctx.stadfa_actions; p; p = p->next) {
        switch (p->kind) {
        case stacmd_t::STORE:
            l = map_stadfa_ver(ctx, p->tag, p->lhs);
            if (history(ctx.dfa.tags[p->tag])) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                s = pool.make_add(s, l, r, ctx.history, p->hist, p->tag);
            }
            else {
                r = last(ctx.history, p->hist, p->tag);
                s = pool.make_set(s, l, r);
            }
            break;
        case stacmd_t::TRANSFER:
            if (p->lhs != p->rhs) {
                l = map_stadfa_ver(ctx, p->tag, p->lhs);
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                s = pool.make_copy(s, l, r);
            }
            break;
        case stacmd_t::ACCEPT:
            l = ctx.dfa.finvers[p->tag];
            if (p->hist == HROOT) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                f = pool.make_copy(f, l, r);
            }
            else if (history(ctx.dfa.tags[p->tag])) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                f = pool.make_add(f, l, r, ctx.history, p->hist, p->tag);
            }
            else {
                r = last(ctx.history, p->hist, p->tag);
                f = pool.make_set(f, l, r);
            }
            break;
        }
    }
    *state_cmd = s;
    *final_cmd = f;

    // Topologically sort tag commands. This may not be possible due to cycles
    // (in staDFA they are unavoidable because indices are determined by TNFA
    // substates, which may be flipped in the origin and target staDFA states).
    // Break each cycle by introducing a temporary tag version: e.g. 'x=y; y=x;'
    // is rewritten as 'z=x; x=y; y=z;'. Topsort gathers cycles at the end of
    // command list and returns a pointer to the first command of the cycle.
    // There can be multiple cycles, therefore the process is repeated until
    // topsort finds no more cycles.
    reserve_buffers<ctx_t, true>(ctx);
    tcmd_t **cycle = tcmd_t::topsort(state_cmd, NULL, ctx.dc_buffers.indegree);
    for (; cycle; ) {
        tcmd_t *c0 = *cycle;
        DASSERT(c0);

        const tagver_t tmp = ++ctx.dfa.maxtagver, lhs = c0->lhs;
        if (ctx.dfa.mtagvers.find(lhs) != ctx.dfa.mtagvers.end()) {
            ctx.dfa.mtagvers.insert(tmp);
        }

        *cycle = pool.make_copy(c0, tmp, lhs);

        for (tcmd_t *c = c0->next; c; c = c->next) {
            DASSERT(tcmd_t::iscopy(c) || tcmd_t::isadd(c));
            if (lhs == c->rhs) {
                c->rhs = tmp;
            }
        }

        reserve_buffers<ctx_t, true>(ctx);
        cycle = tcmd_t::topsort(&c0->next, NULL, ctx.dc_buffers.indegree);
    }
}
void stadfa_to_tdfa_actions(ctx_t &ctx, tcmd_t **state_cmd, tcmd_t **final_cmd)
{
    tcpool_t &pool = ctx.dfa.tcpool;

     
     
    tcmd_t *s = NULL, *f = NULL;
    tagver_t l, r;
    for (const stacmd_t *p = ctx.stadfa_actions; p; p = p->next) {
        switch (p->kind) {
        case stacmd_t::STORE:
            l = map_stadfa_ver(ctx, p->tag, p->lhs);
            if (history(ctx.dfa.tags[p->tag])) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                s = pool.make_add(s, l, r, ctx.history, p->hist, p->tag);
            }
            else {
                r = last(ctx.history, p->hist, p->tag);
                s = pool.make_set(s, l, r);
            }
            break;
        case stacmd_t::TRANSFER:
            if (p->lhs != p->rhs) {
                l = map_stadfa_ver(ctx, p->tag, p->lhs);
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                s = pool.make_copy(s, l, r);
            }
            break;
        case stacmd_t::ACCEPT:
            l = ctx.dfa.finvers[p->tag];
            if (p->hist == HROOT) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                f = pool.make_copy(f, l, r);
            }
            else if (history(ctx.dfa.tags[p->tag])) {
                r = map_stadfa_ver(ctx, p->tag, p->rhs);
                f = pool.make_add(f, l, r, ctx.history, p->hist, p->tag);
            }
            else {
                r = last(ctx.history, p->hist, p->tag);
                f = pool.make_set(f, l, r);
            }
            break;
        }
    }
    *state_cmd = s;
    *final_cmd = f;

     
     
     
     
     
     
     
     
    reserve_buffers<ctx_t, true>(ctx);
    tcmd_t **cycle = tcmd_t::topsort(state_cmd, NULL, ctx.dc_buffers.indegree);
    for (; cycle; ) {
        tcmd_t *c0 = *cycle;
        DASSERT(c0);

        const tagver_t tmp = ++ctx.dfa.maxtagver, lhs = c0->lhs;
        if (ctx.dfa.mtagvers.find(lhs) != ctx.dfa.mtagvers.end()) {
            ctx.dfa.mtagvers.insert(tmp);
        }

        *cycle = pool.make_copy(c0, tmp, lhs);

        for (tcmd_t *c = c0->next; c; c = c->next) {
            DASSERT(tcmd_t::iscopy(c) || tcmd_t::isadd(c));
            if (lhs == c->rhs) {
                c->rhs = tmp;
            }
        }

        reserve_buffers<ctx_t, true>(ctx);
        cycle = tcmd_t::topsort(&c0->next, NULL, ctx.dc_buffers.indegree);
    }
}