bool equal_stacmd(ctx_t &ctx, const kernel_t *x, const kernel_t *y)
{
    DASSERT(x->size == y->size);

    const stacmd_t *p = x->stacmd, *q = y->stacmd;
    for (; p && q; p = p->next, q = q->next) {
        const size_t t = p->tag;

        if (t != q->tag
            || p->kind != q->kind
            || p->lhs != q->lhs
            || p->rhs != q->rhs) return false;

        hidx_t i = p->hist, j = q->hist;
        if (i != j) {
            if (history(ctx.dfa.tags[t])) {
                tag_path_t &p1 = ctx.dc_path1, &p2 = ctx.dc_path2;
                unwind_tag<ctx_t>(ctx.history, p1, i, t);
                unwind_tag<ctx_t>(ctx.history, p2, j, t);
                if (p1 != p2) return false;
            }
            else if (last(ctx.history, i, t) != last(ctx.history, j, t)) {
                return false;
            }
        }
    }
    return !p && !q;
}
bool equal_stacmd(ctx_t &ctx, const kernel_t *x, const kernel_t *y)
{
    DASSERT(x->size == y->size);

    const stacmd_t *p = x->stacmd, *q = y->stacmd;
    for (; p && q; p = p->next, q = q->next) {
        const size_t t = p->tag;

        if (t != q->tag
            || p->kind != q->kind
            || p->lhs != q->lhs
            || p->rhs != q->rhs) return false;

        hidx_t i = p->hist, j = q->hist;
        if (i != j) {
            if (history(ctx.dfa.tags[t])) {
                tag_path_t &p1 = ctx.dc_path1, &p2 = ctx.dc_path2;
                unwind_tag<ctx_t>(ctx.history, p1, i, t);
                unwind_tag<ctx_t>(ctx.history, p2, j, t);
                if (p1 != p2) return false;
            }
            else if (last(ctx.history, i, t) != last(ctx.history, j, t)) {
                return false;
            }
        }
    }
    return !p && !q;
}