# Path: soap/common/base.py
# def base_dispatcher(dispatch_name='execute'):
#     class Dispatcher(BaseDispatcher):
#         pass
#     Dispatcher.dispatch_name = dispatch_name
#     return Dispatcher
# 
# Path: soap/common/cache.py
# def cached(f):
#     class NonLocals(object):
#         pass
#     CACHE_CAPACITY = 1000000
#     cache = {}
#     NonLocals.full = False
#     NonLocals.hits = NonLocals.misses = NonLocals.currsize = 0
#     NonLocals.root = []
#     NonLocals.root[:] = [NonLocals.root, NonLocals.root, None, None]
#     PREV, NEXT, KEY, RESULT = range(4)
# 
#     def decorated(*args, **kwargs):
#         if not kwargs:
#             key_tuple = args
#         else:
#             key_tuple = (args, tuple(sorted(kwargs.items(), key=hash)))
#         key = pickle.dumps(key_tuple)
#         link = cache.get(key)
#         if link is not None:
#             p, n, k, r = link
#             p[NEXT] = n
#             n[PREV] = p
#             last = NonLocals.root[PREV]
#             last[NEXT] = NonLocals.root[PREV] = link
#             link[PREV] = last
#             link[NEXT] = NonLocals.root
#             NonLocals.hits += 1
#             return r
#         r = f(*args, **kwargs)
#         if NonLocals.full:
#             NonLocals.root[KEY] = key
#             NonLocals.root[RESULT] = r
#             cache[key] = NonLocals.root
#             NonLocals.root = NonLocals.root[NEXT]
#             del cache[NonLocals.root[KEY]]
#             NonLocals.root[KEY] = NonLocals.root[RESULT] = None
#         else:
#             last = NonLocals.root[PREV]
#             link = [last, NonLocals.root, key, r]
#             cache[key] = last[NEXT] = NonLocals.root[PREV] = link
#             NonLocals.currsize += 1
#             NonLocals.full = (NonLocals.currsize == CACHE_CAPACITY)
#         NonLocals.misses += 1
#         return r
# 
#     def cache_info():
#         return NonLocals.hits, NonLocals.misses, NonLocals.currsize
# 
#     def cache_clear():
#         cache.clear()
#         NonLocals.root[:] = [NonLocals.root, NonLocals.root, None, None]
#         NonLocals.hits = NonLocals.misses = NonLocals.currsize = 0
#         NonLocals.full = False
# 
#     d = functools.wraps(f)(decorated)
#     d.cache_info = cache_info
#     d.cache_clear = cache_clear
#     d.wrapped_func = f
# 
#     global _cached_funcs
#     _cached_funcs.append(d)
# 
#     return d


from soap.common.base import base_dispatcher
from soap.common.cache import cached
    from soap.expression.variable import Variable
    from soap.expression.variable import VariableTuple
    from soap.expression.base import Expression
    from soap.expression.variable import Variable, VariableTuple
    from soap.expression.arithmetic import ArithExpr
    from soap.expression.boolean import BoolExpr
    from soap.expression.operators import BARRIER_OP
    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
    from soap.expression.fixpoint import FixExpr

@cached
def expression_factory(op, *args):

    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)



# Path: soap/common/base.py
# def base_dispatcher(dispatch_name='execute'):
#     class Dispatcher(BaseDispatcher):
#         pass
#     Dispatcher.dispatch_name = dispatch_name
#     return Dispatcher
# 
# Path: soap/common/cache.py
# def cached(f):
#     class NonLocals(object):
#         pass
#     CACHE_CAPACITY = 1000000
#     cache = {}
#     NonLocals.full = False
#     NonLocals.hits = NonLocals.misses = NonLocals.currsize = 0
#     NonLocals.root = []
#     NonLocals.root[:] = [NonLocals.root, NonLocals.root, None, None]
#     PREV, NEXT, KEY, RESULT = range(4)
# 
#     def decorated(*args, **kwargs):
#         if not kwargs:
#             key_tuple = args
#         else:
#             key_tuple = (args, tuple(sorted(kwargs.items(), key=hash)))
#         key = pickle.dumps(key_tuple)
#         link = cache.get(key)
#         if link is not None:
#             p, n, k, r = link
#             p[NEXT] = n
#             n[PREV] = p
#             last = NonLocals.root[PREV]
#             last[NEXT] = NonLocals.root[PREV] = link
#             link[PREV] = last
#             link[NEXT] = NonLocals.root
#             NonLocals.hits += 1
#             return r
#         r = f(*args, **kwargs)
#         if NonLocals.full:
#             NonLocals.root[KEY] = key
#             NonLocals.root[RESULT] = r
#             cache[key] = NonLocals.root
#             NonLocals.root = NonLocals.root[NEXT]
#             del cache[NonLocals.root[KEY]]
#             NonLocals.root[KEY] = NonLocals.root[RESULT] = None
#         else:
#             last = NonLocals.root[PREV]
#             link = [last, NonLocals.root, key, r]
#             cache[key] = last[NEXT] = NonLocals.root[PREV] = link
#             NonLocals.currsize += 1
#             NonLocals.full = (NonLocals.currsize == CACHE_CAPACITY)
#         NonLocals.misses += 1
#         return r
# 
#     def cache_info():
#         return NonLocals.hits, NonLocals.misses, NonLocals.currsize
# 
#     def cache_clear():
#         cache.clear()
#         NonLocals.root[:] = [NonLocals.root, NonLocals.root, None, None]
#         NonLocals.hits = NonLocals.misses = NonLocals.currsize = 0
#         NonLocals.full = False
# 
#     d = functools.wraps(f)(decorated)
#     d.cache_info = cache_info
#     d.cache_clear = cache_clear
#     d.wrapped_func = f
# 
#     global _cached_funcs
#     _cached_funcs.append(d)
# 
#     return d


# Path: soap/expression/common.py
from soap.common.base import base_dispatcher
from soap.common.cache import cached
    from soap.expression.variable import Variable
    from soap.expression.variable import VariableTuple
    from soap.expression.base import Expression
    from soap.expression.variable import Variable, VariableTuple
    from soap.expression.arithmetic import ArithExpr
    from soap.expression.boolean import BoolExpr
    from soap.expression.operators import BARRIER_OP
    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
    from soap.expression.fixpoint import FixExpr


@cached
def expression_factory(op, *args):

    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)



class GenericExecuter(base_dispatcher()):