# Path: soap/expression/common.py
# @cached
# def expression_factory(op, *args):
#     from soap.expression import operators
#     from soap.expression.arithmetic import (
#         UnaryArithExpr, BinaryArithExpr, TernaryArithExpr, SelectExpr
#     )
#     from soap.expression.boolean import (
#         UnaryBoolExpr, BinaryBoolExpr, TernaryBoolExpr
#     )
#     from soap.expression.fixpoint import FixExpr
#     from soap.expression.linalg import Subscript, AccessExpr, UpdateExpr
# 
#     global op_expr_cls_map
#     if not op_expr_cls_map:
#         op_expr_cls_map = {
#             operators.SUBSCRIPT_OP: Subscript,
#             operators.INDEX_ACCESS_OP: AccessExpr,
#             operators.INDEX_UPDATE_OP: UpdateExpr,
#             operators.FIXPOINT_OP: FixExpr,
#             operators.TERNARY_SELECT_OP: SelectExpr,
#         }
#     cls = op_expr_cls_map.get(op)
#     if cls:
#         return cls(*args)
# 
#     if op in operators.ARITHMETIC_OPERATORS:
#         class_list = [UnaryArithExpr, BinaryArithExpr, TernaryArithExpr]
#     elif op in operators.BOOLEAN_OPERATORS:
#         class_list = [UnaryBoolExpr, BinaryBoolExpr, TernaryBoolExpr]
#     else:
#         raise ValueError('Unknown operator {}.'.format(op))
#     try:
#         cls = class_list[len(args) - 1]
#     except IndexError:
#         raise ValueError('Too many arguments.')
#     return cls(op, *args)
# 
# def is_expression(e):
#     from soap.expression.base import Expression
#     from soap.expression.variable import Variable, VariableTuple
#     if not isinstance(e, Expression):
#         return False
#     if isinstance(e, Variable):
#         return False
#     if isinstance(e, VariableTuple):
#         return False
#     return True
# 
# def is_variable(e):
#     from soap.expression.variable import Variable
#     return isinstance(e, Variable)
# 
# Path: soap/expression/fixpoint.py
# class FixExpr(QuaternaryArithExpr):
#     """Fixpoint expression."""
# 
#     def __init__(self, a1, a2, a3, a4):
#         super().__init__(FIXPOINT_OP, a1, a2, a3, a4)
# 
#     @property
#     def bool_expr(self):
#         return self.a1
# 
#     @property
#     def loop_state(self):
#         return self.a2
# 
#     @property
#     def loop_var(self):
#         return self.a3
# 
#     @property
#     def init_state(self):
#         return self.a4
# 
#     def format(self):
#         fixpoint_var = underline('e')
#         s = ('{op}(λ{fvar}.({bool_expr} ? {fvar} % {loop_state} : {var}))'
#              ' % {init_state}')
#         return s.format(
#             fvar=fixpoint_var, op=self.op, bool_expr=self.bool_expr,
#             loop_state=self.loop_state.format(), var=self.loop_var,
#             init_state=self.init_state.format())
# 
# Path: soap/semantics/common.py
# def is_numeral(e):
#     from soap.semantics.error import (
#         mpz_type, mpfr_type, Interval, ErrorSemantics
#     )
#     from soap.semantics.linalg import MultiDimensionalArray
#     return isinstance(e, (
#         mpz_type, mpfr_type, Interval, ErrorSemantics, MultiDimensionalArray))


from soap.expression import (
    expression_factory, is_expression, is_variable, FixExpr
)
from soap.semantics.common import is_numeral


def expand_expr(expr, meta_state):
    if isinstance(expr, FixExpr):
        return expression_factory(
            expr.op, expr.bool_expr, expr.loop_state, expr.loop_var,
            expand_meta_state(expr.init_state, meta_state))
    if is_expression(expr):
        args = [expand_expr(a, meta_state) for a in expr.args]

# Path: soap/expression/common.py
# @cached
# def expression_factory(op, *args):
#     from soap.expression import operators
#     from soap.expression.arithmetic import (
#         UnaryArithExpr, BinaryArithExpr, TernaryArithExpr, SelectExpr
#     )
#     from soap.expression.boolean import (
#         UnaryBoolExpr, BinaryBoolExpr, TernaryBoolExpr
#     )
#     from soap.expression.fixpoint import FixExpr
#     from soap.expression.linalg import Subscript, AccessExpr, UpdateExpr
# 
#     global op_expr_cls_map
#     if not op_expr_cls_map:
#         op_expr_cls_map = {
#             operators.SUBSCRIPT_OP: Subscript,
#             operators.INDEX_ACCESS_OP: AccessExpr,
#             operators.INDEX_UPDATE_OP: UpdateExpr,
#             operators.FIXPOINT_OP: FixExpr,
#             operators.TERNARY_SELECT_OP: SelectExpr,
#         }
#     cls = op_expr_cls_map.get(op)
#     if cls:
#         return cls(*args)
# 
#     if op in operators.ARITHMETIC_OPERATORS:
#         class_list = [UnaryArithExpr, BinaryArithExpr, TernaryArithExpr]
#     elif op in operators.BOOLEAN_OPERATORS:
#         class_list = [UnaryBoolExpr, BinaryBoolExpr, TernaryBoolExpr]
#     else:
#         raise ValueError('Unknown operator {}.'.format(op))
#     try:
#         cls = class_list[len(args) - 1]
#     except IndexError:
#         raise ValueError('Too many arguments.')
#     return cls(op, *args)
# 
# def is_expression(e):
#     from soap.expression.base import Expression
#     from soap.expression.variable import Variable, VariableTuple
#     if not isinstance(e, Expression):
#         return False
#     if isinstance(e, Variable):
#         return False
#     if isinstance(e, VariableTuple):
#         return False
#     return True
# 
# def is_variable(e):
#     from soap.expression.variable import Variable
#     return isinstance(e, Variable)
# 
# Path: soap/expression/fixpoint.py
# class FixExpr(QuaternaryArithExpr):
#     """Fixpoint expression."""
# 
#     def __init__(self, a1, a2, a3, a4):
#         super().__init__(FIXPOINT_OP, a1, a2, a3, a4)
# 
#     @property
#     def bool_expr(self):
#         return self.a1
# 
#     @property
#     def loop_state(self):
#         return self.a2
# 
#     @property
#     def loop_var(self):
#         return self.a3
# 
#     @property
#     def init_state(self):
#         return self.a4
# 
#     def format(self):
#         fixpoint_var = underline('e')
#         s = ('{op}(λ{fvar}.({bool_expr} ? {fvar} % {loop_state} : {var}))'
#              ' % {init_state}')
#         return s.format(
#             fvar=fixpoint_var, op=self.op, bool_expr=self.bool_expr,
#             loop_state=self.loop_state.format(), var=self.loop_var,
#             init_state=self.init_state.format())
# 
# Path: soap/semantics/common.py
# def is_numeral(e):
#     from soap.semantics.error import (
#         mpz_type, mpfr_type, Interval, ErrorSemantics
#     )
#     from soap.semantics.linalg import MultiDimensionalArray
#     return isinstance(e, (
#         mpz_type, mpfr_type, Interval, ErrorSemantics, MultiDimensionalArray))


# Path: soap/semantics/functions/meta.py
from soap.expression import (
    expression_factory, is_expression, is_variable, FixExpr
)
from soap.semantics.common import is_numeral



def expand_expr(expr, meta_state):
    if isinstance(expr, FixExpr):
        return expression_factory(
            expr.op, expr.bool_expr, expr.loop_state, expr.loop_var,
            expand_meta_state(expr.init_state, meta_state))
    if is_expression(expr):
        args = [expand_expr(a, meta_state) for a in expr.args]

        return expression_factory(expr.op, *args)