# Path: liam2/utils.py
# def prod(values):
#     res = 1
#     for value in values:
#         res *= value
#     return res


import math
    import numpy as np
from types import BuiltinFunctionType, FunctionType
from liam2.utils import prod

    def decorate(self, sizefunc, func):
        def decorated(*args, **kwargs):
            return self.safe_call(sizefunc(*args, **kwargs),
                                  func, *args, **kwargs)
        decorated.__name__ = func.__name__
        return decorated

    def free(self, nbytes):
        print("mem is full, puring %d elements from cache" % len(self.cache))
        self.cache.clear()


class ManagedModule(object):
    def __init__(self, module, manager, sizefuncs):
        self.module = module
        self.manager = manager
        self.sizefuncs = sizefuncs

    def __getattr__(self, key):
        func = getattr(self.module, key)
        assert isinstance(func, (BuiltinFunctionType, FunctionType))
        return self.manager.decorate(self.sizefuncs[key], func)

if __name__ == '__main__':

    def generic(shape, dtype=float, *_, **__):
        if not isinstance(shape, tuple):
            shape = (shape,)
        dt = np.dtype(dtype)

# Path: liam2/utils.py
# def prod(values):
#     res = 1
#     for value in values:
#         res *= value
#     return res


# Path: liam2/memory.py
import math
    import numpy as np
from types import BuiltinFunctionType, FunctionType
from liam2.utils import prod


    def decorate(self, sizefunc, func):
        def decorated(*args, **kwargs):
            return self.safe_call(sizefunc(*args, **kwargs),
                                  func, *args, **kwargs)
        decorated.__name__ = func.__name__
        return decorated

    def free(self, nbytes):
        print("mem is full, puring %d elements from cache" % len(self.cache))
        self.cache.clear()


class ManagedModule(object):
    def __init__(self, module, manager, sizefuncs):
        self.module = module
        self.manager = manager
        self.sizefuncs = sizefuncs

    def __getattr__(self, key):
        func = getattr(self.module, key)
        assert isinstance(func, (BuiltinFunctionType, FunctionType))
        return self.manager.decorate(self.sizefuncs[key], func)

if __name__ == '__main__':

    def generic(shape, dtype=float, *_, **__):
        if not isinstance(shape, tuple):
            shape = (shape,)
        dt = np.dtype(dtype)

        return prod(shape) * dt.itemsize