xmlRelaxNGSimplify(xmlRelaxNGParserCtxtPtr ctxt,
                   xmlRelaxNGDefinePtr cur, xmlRelaxNGDefinePtr parent)
{
    xmlRelaxNGDefinePtr prev = NULL;

    while (cur != NULL) {
        if ((cur->type == XML_RELAXNG_REF) ||
            (cur->type == XML_RELAXNG_PARENTREF)) {
            if (cur->depth != -3) {
                cur->depth = -3;
                xmlRelaxNGSimplify(ctxt, cur->content, cur);
            }
        } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
            cur->parent = parent;
            if ((parent != NULL) &&
                ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
                 (parent->type == XML_RELAXNG_LIST) ||
                 (parent->type == XML_RELAXNG_GROUP) ||
                 (parent->type == XML_RELAXNG_INTERLEAVE) ||
                 (parent->type == XML_RELAXNG_ONEORMORE) ||
                 (parent->type == XML_RELAXNG_ZEROORMORE))) {
                parent->type = XML_RELAXNG_NOT_ALLOWED;
                break;
            }
            if ((parent != NULL) && (parent->type == XML_RELAXNG_CHOICE)) {
                prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
            } else
                prev = cur;
        } else if (cur->type == XML_RELAXNG_EMPTY) {
            cur->parent = parent;
            if ((parent != NULL) &&
                ((parent->type == XML_RELAXNG_ONEORMORE) ||
                 (parent->type == XML_RELAXNG_ZEROORMORE))) {
                parent->type = XML_RELAXNG_EMPTY;
                break;
            }
            if ((parent != NULL) &&
                ((parent->type == XML_RELAXNG_GROUP) ||
                 (parent->type == XML_RELAXNG_INTERLEAVE))) {
                prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
            } else
                prev = cur;
        } else {
            cur->parent = parent;
            if (cur->content != NULL)
                xmlRelaxNGSimplify(ctxt, cur->content, cur);
            if ((cur->type != XML_RELAXNG_VALUE) && (cur->attrs != NULL))
                xmlRelaxNGSimplify(ctxt, cur->attrs, cur);
            if (cur->nameClass != NULL)
                xmlRelaxNGSimplify(ctxt, cur->nameClass, cur);
            /*
             * On Elements, try to move attribute only generating rules on
             * the attrs rules.
             */
            if (cur->type == XML_RELAXNG_ELEMENT) {
                int attronly;
                xmlRelaxNGDefinePtr tmp, pre;

                while (cur->content != NULL) {
                    attronly =
                        xmlRelaxNGGenerateAttributes(ctxt, cur->content);
                    if (attronly == 1) {
                        /*
                         * migrate cur->content to attrs
                         */
                        tmp = cur->content;
                        cur->content = tmp->next;
                        tmp->next = cur->attrs;
                        cur->attrs = tmp;
                    } else {
                        /*
                         * cur->content can generate elements or text
                         */
                        break;
                    }
                }
                pre = cur->content;
                while ((pre != NULL) && (pre->next != NULL)) {
                    tmp = pre->next;
                    attronly = xmlRelaxNGGenerateAttributes(ctxt, tmp);
                    if (attronly == 1) {
                        /*
                         * migrate tmp to attrs
                         */
                        pre->next = tmp->next;
                        tmp->next = cur->attrs;
                        cur->attrs = tmp;
                    } else {
                        pre = tmp;
                    }
                }
            }
            /*
             * This may result in a simplification
             */
            if ((cur->type == XML_RELAXNG_GROUP) ||
                (cur->type == XML_RELAXNG_INTERLEAVE)) {
                if (cur->content == NULL)
                    cur->type = XML_RELAXNG_EMPTY;
                else if (cur->content->next == NULL) {
                    if ((parent == NULL) && (prev == NULL)) {
                        cur->type = XML_RELAXNG_NOOP;
                    } else if (prev == NULL) {
                        parent->content = cur->content;
                        cur->content->next = cur->next;
                        cur = cur->content;
                    } else {
                        cur->content->next = cur->next;
                        prev->next = cur->content;
                        cur = cur->content;
                    }
                }
            }
            /*
             * the current node may have been transformed back
             */
            if ((cur->type == XML_RELAXNG_EXCEPT) &&
                (cur->content != NULL) &&
                (cur->content->type == XML_RELAXNG_NOT_ALLOWED)) {
                prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
            } else if (cur->type == XML_RELAXNG_NOT_ALLOWED) {
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_ATTRIBUTE) ||
                     (parent->type == XML_RELAXNG_LIST) ||
                     (parent->type == XML_RELAXNG_GROUP) ||
                     (parent->type == XML_RELAXNG_INTERLEAVE) ||
                     (parent->type == XML_RELAXNG_ONEORMORE) ||
                     (parent->type == XML_RELAXNG_ZEROORMORE))) {
                    parent->type = XML_RELAXNG_NOT_ALLOWED;
                    break;
                }
                if ((parent != NULL) &&
                    (parent->type == XML_RELAXNG_CHOICE)) {
                    prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                } else
                    prev = cur;
            } else if (cur->type == XML_RELAXNG_EMPTY) {
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_ONEORMORE) ||
                     (parent->type == XML_RELAXNG_ZEROORMORE))) {
                    parent->type = XML_RELAXNG_EMPTY;
                    break;
                }
                if ((parent != NULL) &&
                    ((parent->type == XML_RELAXNG_GROUP) ||
                     (parent->type == XML_RELAXNG_INTERLEAVE) ||
                     (parent->type == XML_RELAXNG_CHOICE))) {
                    prev = xmlRelaxNGTryUnlink(ctxt, cur, parent, prev);
                } else
                    prev = cur;
            } else {
                prev = cur;
            }
        }
        cur = cur->next;
    }
}