def assign_gold_actions(x: ATree, mode="default"):
    """
    :param x:
    :param mode:    "default" (all) or "ltr" (only first one)
    :return:
    """
    """ assigns actions that can be taken at every node of the given tree """
    for xe in x:
        assign_gold_actions(xe, mode=mode)
    if not x.is_open:
        x.gold_actions = []
    else:
        if x.label() == ")" or x.label() == "(":
            x.is_open = False
            x.gold_actions = []
        elif x.label() == "@SLOT@":
            if len(x.parent) == 1:
                raise Exception()
            # get this slots's siblings
            x.gold_actions = []
            xpos = child_number_of(x)
            if xpos == 0:
                leftsibling = None
                leftsibling_nr = None
            else:
                leftsibling = x.parent[xpos - 1]
                leftsibling_nr = child_number_of(leftsibling.align)
            if xpos == len(x.parent) - 1:
                rightsibling = None
                rightsibling_nr = None
            else:
                rightsibling = x.parent[xpos + 1]
                rightsibling_nr = child_number_of(rightsibling.align)

            if leftsibling is None and rightsibling is None:
                # slot is only child, can use any descendant
                x.gold_actions = x.parent.align.descendants
                if mode == "ltr" and len(x.gold_actions) > 0:
                    x.gold_actions = [x.gold_actions[0]]
                assert (False
                        )  # should not happen if deletion actions are not used
            else:
                p = leftsibling.align.parent if leftsibling is not None else rightsibling.align.parent
                slicefrom = leftsibling_nr + 1 if leftsibling_nr is not None else None
                slicer = slice(slicefrom, rightsibling_nr)
                x.gold_actions = p[slicer]
                if mode == "ltr" and len(x.gold_actions) > 0:
                    x.gold_actions = [x.gold_actions[0]]
            if len(x.gold_actions) == 0:
                x.gold_actions = ["@CLOSE@"]
        else:  # not a sibling slot ("@SLOT@"), not a "(" or ")"
            x.gold_actions = []
            if len(x) == 0:
                x.gold_actions = list(x.align._descendants)
                if mode == "ltr" and len(x.gold_actions) > 0:
                    x.gold_actions = [x.gold_actions[0]]
            else:
                realchildren = [xe for xe in x if xe.label() != "@SLOT@"]
                childancestors = realchildren[0].align._ancestors[::-1]
                for child in realchildren:
                    assert (childancestors == child.align._ancestors[::-1])
                for ancestor in childancestors:
                    if ancestor is x.align:
                        break
                    else:
                        x.gold_actions.append(ancestor)
                if mode == "ltr" and len(x.gold_actions) > 0:
                    x.gold_actions = [x.gold_actions[0]]
        if len(x.gold_actions) == 0 and x.is_open:
            x.gold_actions = ["@CLOSE@"]

    if len(x.gold_actions) > 0:
        # x._chosen_action = x.gold_actions[0]
        x._chosen_action = random.choice(x.gold_actions)
    else:
        x._chosen_action = None
    return x
def execute_chosen_actions(x: ATree, _budget=[np.infty], mode="full"):
    if x._chosen_action is None or not x.is_open:
        iterr = list(x)
        for xe in iterr:
            execute_chosen_actions(xe, _budget=_budget, mode=mode)
        return x
    if x.label() == "(":  # insert a parent before current parent
        pass
    elif x.label() == ")":
        pass
    elif x.label() == "@SLOT@":
        if x._chosen_action == "@CLOSE@":
            del x.parent[child_number_of(x)]
            # if parentheses became empty, remove
            _budget[0] += 1
            if len(x.parent) == 2 and x.parent[0].label(
            ) == "(" and x.parent[1].label() == ")":
                x.parent[:] = []
        else:
            if _budget[0] <= 0:
                return x
            if isinstance(x._chosen_action, Tree):
                x.set_label(x._chosen_action.label())
            else:
                x.set_label(x._chosen_action)

            if isinstance(x._chosen_action, Tree):
                x.align = x._chosen_action
            x.is_open = True

            leftslot = ATree("@SLOT@", [], is_open=True)
            leftslot.parent = x.parent
            rightslot = ATree("@SLOT@", [], is_open=True)
            rightslot.parent = x.parent

            if mode != "ltr":
                x.parent.insert(child_number_of(x), leftslot)
                _budget[0] -= 1

            x.parent.insert(child_number_of(x) + 1, rightslot)
            _budget[0] -= 1

    else:
        iterr = list(x)
        for xe in iterr:
            execute_chosen_actions(xe, _budget=_budget, mode=mode)
        if _budget[0] <= 0:
            return x
        if x._chosen_action == "@CLOSE@":
            x.is_open = False  # this node can't generate children anymore
        else:
            # X(A, B, C) -> X _( _@SLOT _Y (A, B, C) [_@SLOT] _)
            # add child, with "(" and ")" and "@SLOT@" nodes

            if isinstance(x._chosen_action, Tree):
                newnode = ATree(x._chosen_action.label(), [])
            else:
                newnode = ATree(x._chosen_action, [])

            newnode.is_open = True
            if mode == "ltr":
                x.is_open = False

            if isinstance(x._chosen_action, Tree):
                newnode.align = x._chosen_action
            newnode.parent = x

            newnode[:] = x[:]
            for xe in newnode:
                xe.parent = newnode

            leftslot = ATree("@SLOT@", [], is_open=True)
            leftslot.parent = newnode.parent

            rightslot = ATree("@SLOT@", [], is_open=True)
            rightslot.parent = newnode.parent

            if mode != "ltr":
                x[:] = [leftslot, newnode, rightslot]
                _budget[0] -= 3
            else:
                x[:] = [newnode, rightslot]
                _budget[0] -= 2
    return x