def free_variables(*terms, **kwargs):
    """
    Returns a frozenset of variables free in given terms.

    kwargs: by_name , False by default.
    If by_name=True, the result is a set of variable names, and
    binding occurs by name. Otherwise, the result is a set of Var
    objects, and ForAll X:s1 does not bind X:s2.
    """
    by_name = kwargs.get('by_name', False)
    _free_variables = partial(free_variables, by_name=by_name)

    t = terms[0] if len(terms) == 1 else terms

    if type(t) is Var:
        return frozenset((t.name if by_name else t,))

    elif type(t) in (tuple, Const, Apply, Eq, Ite, Not, And, Or,
                     Implies, Iff):
        return union(*(_free_variables(x) for x in t))

    elif type(t) in (ForAll, Exists):
        return _free_variables(t.body) - _free_variables(*t.variables)

    elif hasattr(t,'args'):
        return union(*(_free_variables(x) for x in t.args))

    else:
        assert False, type(t)
dataset/ETHPy150Open Microsoft/ivy/ivy/logic_util.py/free_variables/original