import ast
import operator as op

_ALLOWED = {
    ast.Add: op.add, ast.Sub: op.sub, ast.Mult: op.mul, ast.Div: op.truediv, ast.Mod: op.mod,
    ast.Pow: op.pow,
    ast.USub: op.neg, ast.UAdd: op.pos,
    ast.Eq: op.eq, ast.NotEq: op.ne, ast.Lt: op.lt, ast.LtE: op.le, ast.Gt: op.gt, ast.GtE: op.ge,
    ast.And: lambda a,b: a and b,
    ast.Or:  lambda a,b: a or b,
}

def safe_eval(expr: str, variables: dict):
    """
    Safely evaluate math/boolean expressions with provided variables.
    Supports: + - * / % **, comparisons, and/or, parentheses.
    """
    def _eval(node):
        if isinstance(node, ast.Expression):
            return _eval(node.body)

        if isinstance(node, ast.Constant):  # numbers / True / False
            return node.value

        if isinstance(node, ast.Name):
            return variables.get(node.id, 0)

        if isinstance(node, ast.BinOp):
            return _ALLOWED[type(node.op)](_eval(node.left), _eval(node.right))

        if isinstance(node, ast.UnaryOp):
            return _ALLOWED[type(node.op)](_eval(node.operand))

        if isinstance(node, ast.BoolOp):
            if isinstance(node.op, ast.And):
                out = True
                for v in node.values:
                    out = out and bool(_eval(v))
                return out
            if isinstance(node.op, ast.Or):
                out = False
                for v in node.values:
                    out = out or bool(_eval(v))
                return out

        if isinstance(node, ast.Compare):
            left = _eval(node.left)
            for op_node, comparator in zip(node.ops, node.comparators):
                right = _eval(comparator)
                if not _ALLOWED[type(op_node)](left, right):
                    return False
                left = right
            return True

        if isinstance(node, ast.IfExp):
            return _eval(node.body) if _eval(node.test) else _eval(node.orelse)

        raise ValueError("Unsupported expression")

    tree = ast.parse(expr, mode="eval")
    return _eval(tree)