import ast
from .util import to_taichi_type
import copy
from .exception import TaichiSyntaxError

class ScopeGuard:
    def __init__(self, t, stmt_block=None):
        self.t = t
        self.stmt_block = stmt_block

    def __enter__(self):
        self.t.local_scopes.append([])

    def __exit__(self, exc_type, exc_val, exc_tb):
        local = self.t.local_scopes[-1]
        if self.stmt_block is not None:
            for var in reversed(local):
                stmt = ASTTransformer.parse_stmt('del var')
                stmt.targets[0].id = var
                self.stmt_block.append(stmt)
        self.t.local_scopes = self.t.local_scopes[:-1]

# Single-pass transform
class ASTTransformer(ast.NodeTransformer):
    def __init__(self,
                 excluded_paremeters=(),
                 is_kernel=True,
                 is_classfunc=False,
                 func=None,
                 arg_features=None):
        super().__init__()
        self.local_scopes = []
        self.excluded_parameters = excluded_paremeters
        self.is_kernel = is_kernel
        self.func = func
        self.arg_features = arg_features
        self.returns = None

    def variable_scope(self, *args):
        return ScopeGuard(self, *args)

    def current_scope(self):
        return self.local_scopes[-1]

    def var_declared(self, name):
        for s in self.local_scopes:
            if name in s:
                return True
        return False

    def is_creation(self, name):
        return not self.var_declared(name)

    def create_variable(self, name):
        assert name not in self.current_scope(
        ), "Recreating variables is not allowed"
        self.current_scope().append(name)

    def generic_visit(self, node, body_names=[]):
        assert isinstance(body_names, list)
        for field, old_value in ast.iter_fields(node):
            if field in body_names:
                list_stmt = old_value
                with self.variable_scope(list_stmt):
                    for i, l in enumerate(list_stmt):
                        list_stmt[i] = self.visit(l)

            elif isinstance(old_value, list):
                new_values = []
                for value in old_value:
                    if isinstance(value, ast.AST):
                        value = self.visit(value)
                        if value is None:
                            continue
                        elif not isinstance(value, ast.AST):
                            new_values.extend(value)
                            continue
                    new_values.append(value)
                old_value[:] = new_values
            elif isinstance(old_value, ast.AST):
                new_node = self.visit(old_value)
                if new_node is None:
                    delattr(node, field)
                else:
                    setattr(node, field, new_node)
        return node

    def visit_AugAssign(self, node):
        self.generic_visit(node)
        template = 'x.augassign(0, 0)'
        t = ast.parse(template).body[0]
        t.value.func.value = node.target
        t.value.func.value.ctx = ast.Load()
        t.value.args[0] = node.value
        t.value.args[1] = ast.Str(s=type(node.op).__name__, ctx=ast.Load())
        return ast.copy_location(t, node)

    @staticmethod
    def make_single_statement(stmts):
        template = 'if 1: pass'
        t = ast.parse(template).body[0]
        t.body = stmts
        return t

    def visit_Assign(self, node):
        assert (len(node.targets) == 1)
        self.generic_visit(node)

        decorated = isinstance(node.value, ast.Call) and isinstance(
            node.value.func, ast.Attribute) and isinstance(node.value.func.value, ast.Name) \
                    and node.value.func.value.id == 'ti'

        is_static_assign = False

        if decorated:
            attr = node.value.func
            if attr.attr == 'static':
                is_static_assign = True
            else:
                pass  # eg. x = ti.cast(xx) will reach here, but they're not decorators, so no raising errors here

        if is_static_assign:
            return node

        if isinstance(node.targets[0], ast.Tuple):
            targets = node.targets[0].elts

            # Create
            stmts = []

            holder = self.parse_stmt('__tmp_tuple = ti.expr_init_list(0, '
                                     f'{len(targets)})')
            holder.value.args[0] = node.value

            stmts.append(holder)

            def tuple_indexed(i):
                indexing = self.parse_stmt('__tmp_tuple[0]')
                indexing.value.slice.value = self.parse_expr("{}".format(i))
                return indexing.value

            for i, target in enumerate(targets):
                is_local = isinstance(target, ast.Name)
                if is_local and self.is_creation(target.id):
                    var_name = target.id
                    target.ctx = ast.Store()
                    # Create
                    init = ast.Attribute(value=ast.Name(id='ti',
                                                        ctx=ast.Load()),
                                         attr='expr_init',
                                         ctx=ast.Load())
                    rhs = ast.Call(
                        func=init,
                        args=[tuple_indexed(i)],
                        keywords=[],
                    )
                    self.create_variable(var_name)
                    stmts.append(ast.Assign(targets=[target], value=rhs))
                else:
                    # Assign
                    target.ctx = ast.Load()
                    func = ast.Attribute(value=target,
                                         attr='assign',
                                         ctx=ast.Load())
                    call = ast.Call(func=func,
                                    args=[tuple_indexed(i)],
                                    keywords=[])
                    stmts.append(ast.Expr(value=call))

            for stmt in stmts:
                ast.copy_location(stmt, node)
            stmts.append(self.parse_stmt('del __tmp_tuple'))
            return self.make_single_statement(stmts)
        else:
            is_local = isinstance(node.targets[0], ast.Name)
            if is_local and self.is_creation(node.targets[0].id):
                var_name = node.targets[0].id
                # Create
                init = ast.Attribute(value=ast.Name(id='ti', ctx=ast.Load()),
                                     attr='expr_init',
                                     ctx=ast.Load())
                rhs = ast.Call(
                    func=init,
                    args=[node.value],
                    keywords=[],
                )
                self.create_variable(var_name)
                return ast.copy_location(
                    ast.Assign(targets=node.targets, value=rhs), node)
            else:
                # Assign
                node.targets[0].ctx = ast.Load()
                func = ast.Attribute(value=node.targets[0],
                                     attr='assign',
                                     ctx=ast.Load())
                call = ast.Call(func=func, args=[node.value], keywords=[])
                return ast.copy_location(ast.Expr(value=call), node)

    def visit_Try(self, node):
        raise TaichiSyntaxError(
            "Keyword 'try' not supported in Taichi kernels")

    def visit_Import(self, node):
        raise TaichiSyntaxError(
            "Keyword 'import' not supported in Taichi kernels")

    def visit_While(self, node):
        if node.orelse:
            raise TaichiSyntaxError(
                "'else' clause for 'while' not supported in Taichi kernels")

        template = '''
if 1:
  ti.core.begin_frontend_while(ti.Expr(1).ptr)
  __while_cond = 0
  if __while_cond:
    pass
  else:
    break
  ti.core.pop_scope()
'''
        cond = node.test
        t = ast.parse(template).body[0]
        t.body[1].value = cond
        t.body = t.body[:3] + node.body + t.body[3:]

        self.generic_visit(t, ['body'])
        return ast.copy_location(t, node)

    def visit_block(self, list_stmt):
        for i, l in enumerate(list_stmt):
            list_stmt[i] = self.visit(l)

    def visit_If(self, node):
        self.generic_visit(node, ['body', 'orelse'])

        is_static_if = isinstance(node.test, ast.Call) and isinstance(
            node.test.func, ast.Attribute)
        if is_static_if:
            attr = node.test.func
            if attr.attr == 'static':
                is_static_if = True
            else:
                is_static_if = False

        if is_static_if:
            # Do nothing
            return node

        template = '''
if 1:
  __cond = 0
  ti.core.begin_frontend_if(ti.Expr(__cond).ptr)
  ti.core.begin_frontend_if_true()
  ti.core.pop_scope()
  ti.core.begin_frontend_if_false()
  ti.core.pop_scope()
'''
        t = ast.parse(template).body[0]
        cond = node.test
        t.body[0].value = cond
        t.body = t.body[:5] + node.orelse + t.body[5:]
        t.body = t.body[:3] + node.body + t.body[3:]
        return ast.copy_location(t, node)

    def check_loop_var(self, loop_var):
        if self.var_declared(loop_var):
            raise TaichiSyntaxError(
                "Variable '{}' is already declared in the outer scope and cannot be used as loop variable"
                .format(loop_var))

    @staticmethod
    def get_decorator(iter):
        if not (isinstance(iter, ast.Call)
                and isinstance(iter.func, ast.Attribute)
                and isinstance(iter.func.value, ast.Name)
                and iter.func.value.id == 'ti' and
                (iter.func.attr == 'static' or iter.func.attr == 'grouped'
                 or iter.func.attr == 'ndrange')):
            return ''
        return iter.func.attr

    @staticmethod
    def get_targets(node):
        if isinstance(node.target, ast.Name):
            return [node.target.id]
        else:
            assert isinstance(node.target, ast.Tuple)
            return [name.id for name in node.target.elts]

    def visit_static_for(self, node, is_grouped):
        # for i in ti.static(range(n))
        # for i, j in ti.static(ti.ndrange(n))
        # for I in ti.static(ti.grouped(ti.ndrange(n, m)))
        self.generic_visit(node, ['body'])
        if is_grouped:
            assert len(node.iter.args[0].args) == 1
            template = '''
if 1:
    __ndrange_arg = 0
    from taichi.lang.exception import TaichiSyntaxError
    if not isinstance(__ndrange_arg, ti.ndrange):
        raise TaichiSyntaxError("Only 'ti.ndrange' is allowed in 'ti.static(ti.grouped(...))'.")
    pass
    del a
            '''
            t = ast.parse(template).body[0]
            t.body[0].value = node.iter.args[0].args[0]
            t.body[3] = node
            t.body[3].iter.args[0].args[0] = self.parse_expr('__ndrange_arg')
        else:
            t = self.parse_stmt('if 1: pass; del a')
            t.body[0] = node
        target = copy.deepcopy(node.target)
        target.ctx = ast.Del()
        if isinstance(target, ast.Tuple):
            for tar in target.elts:
                tar.ctx = ast.Del()
        t.body[-1].targets = [target]
        return t

    def visit_range_for(self, node):
        # for i in range(n)
        self.generic_visit(node, ['body'])
        loop_var = node.target.id
        self.check_loop_var(loop_var)
        template = '''
if 1:
    {} = ti.Expr(ti.core.make_id_expr(''))
    ___begin = ti.Expr(0)
    ___end = ti.Expr(0)
    ___begin = ti.cast(___begin, ti.i32)
    ___end = ti.cast(___end, ti.i32)
    ti.core.begin_frontend_range_for({}.ptr, ___begin.ptr, ___end.ptr)
    ti.core.end_frontend_range_for()
        '''.format(loop_var, loop_var)
        t = ast.parse(template).body[0]

        assert len(node.iter.args) in [1, 2]
        if len(node.iter.args) == 2:
            bgn = node.iter.args[0]
            end = node.iter.args[1]
        else:
            bgn = self.make_constant(value=0)
            end = node.iter.args[0]

        t.body[1].value.args[0] = bgn
        t.body[2].value.args[0] = end
        t.body = t.body[:6] + node.body + t.body[6:]
        t.body.append(self.parse_stmt('del {}'.format(loop_var)))
        return ast.copy_location(t, node)

    def visit_ndrange_for(self, node):
        # for i, j in ti.ndrange(n)
        template = '''
if ti.static(1):
    __ndrange = 0
    for __ndrange_I in range(0):
        __I = __ndrange_I
        '''
        t = ast.parse(template).body[0]
        t.body[0].value = node.iter
        t_loop = t.body[1]
        t_loop.iter.args[0] = self.parse_expr('__ndrange.acc_dimensions[0]')
        targets = self.get_targets(node)
        targets_tmp = ['__' + name for name in targets]
        loop_body = t_loop.body
        for i in range(len(targets)):
            if i + 1 < len(targets):
                stmt = '{} = __I // __ndrange.acc_dimensions[{}]'.format(
                    targets_tmp[i], i + 1)
            else:
                stmt = '{} = __I'.format(targets_tmp[i])
            loop_body.append(self.parse_stmt(stmt))
            stmt = '{} = {} + __ndrange.bounds[{}][0]'.format(
                targets[i], targets_tmp[i], i)
            loop_body.append(self.parse_stmt(stmt))
            if i + 1 < len(targets):
                stmt = '__I = __I - {} * __ndrange.acc_dimensions[{}]'.format(
                    targets_tmp[i], i + 1)
                loop_body.append(self.parse_stmt(stmt))
        loop_body += node.body

        node = ast.copy_location(t, node)
        return self.visit(node)  # further translate as a range for

    def visit_grouped_ndrange_for(self, node):
        # for I in ti.grouped(ti.ndrange(n, m))
        self.generic_visit(node, ['body'])
        target = node.target.id
        template = '''
if ti.static(1):
    __ndrange = 0
    {} = ti.expr_init(ti.Vector([0] * len(__ndrange.dimensions)))
    ___begin = ti.Expr(0)
    ___end = __ndrange.acc_dimensions[0]
    ___begin = ti.cast(___begin, ti.i32)
    ___end = ti.cast(___end, ti.i32)
    __ndrange_I = ti.Expr(ti.core.make_id_expr(''))
    ti.core.begin_frontend_range_for(__ndrange_I.ptr, ___begin.ptr, ___end.ptr)
    __I = __ndrange_I
    for __grouped_I in range(len(__ndrange.dimensions)):
        __grouped_I_tmp = 0
        if __grouped_I + 1 < len(__ndrange.dimensions):
            __grouped_I_tmp = __I // __ndrange.acc_dimensions[__grouped_I + 1]
        else:
            __grouped_I_tmp = __I
        ti.subscript({}, __grouped_I).assign(__grouped_I_tmp + __ndrange.bounds[__grouped_I][0])
        if __grouped_I + 1 < len(__ndrange.dimensions):
            __I = __I - __grouped_I_tmp * __ndrange.acc_dimensions[__grouped_I + 1]
    ti.core.end_frontend_range_for()
        '''.format(target, target)
        t = ast.parse(template).body[0]
        t.body[0].value = node.iter.args[0]
        cut = len(t.body) - 1
        t.body = t.body[:cut] + node.body + t.body[cut:]
        return ast.copy_location(t, node)

    def visit_struct_for(self, node, is_grouped):
        # for i, j in x
        # for I in ti.grouped(x)
        self.generic_visit(node, ['body'])
        targets = self.get_targets(node)

        for loop_var in targets:
            self.check_loop_var(loop_var)

        var_decl = ''.join(
            '    {} = ti.Expr(ti.core.make_id_expr(""))\n'.format(name)
            for name in targets)  # indent: 4 spaces
        vars = ', '.join(targets)
        if is_grouped:
            template = '''
if 1:
    ___loop_var = 0
    {} = ti.make_var_vector(size=___loop_var.loop_range().dim())
    ___expr_group = ti.make_expr_group({})
    ti.core.begin_frontend_struct_for(___expr_group, ___loop_var.loop_range().ptr)
    ti.core.end_frontend_range_for()
            '''.format(vars, vars)
            t = ast.parse(template).body[0]
            cut = 4
            t.body[0].value = node.iter
            t.body = t.body[:cut] + node.body + t.body[cut:]
        else:
            template = '''
if 1:
{}
    ___loop_var = 0
    ___expr_group = ti.make_expr_group({})
    ti.core.begin_frontend_struct_for(___expr_group, ___loop_var.loop_range().ptr)
    ti.core.end_frontend_range_for()
            '''.format(var_decl, vars)
            t = ast.parse(template).body[0]
            cut = len(targets) + 3
            t.body[cut - 3].value = node.iter
            t.body = t.body[:cut] + node.body + t.body[cut:]
        for loop_var in reversed(targets):
            t.body.append(self.parse_stmt('del {}'.format(loop_var)))
        return ast.copy_location(t, node)

    def visit_For(self, node):
        if node.orelse:
            raise TaichiSyntaxError(
                "'else' clause for 'for' not supported in Taichi kernels")

        decorator = self.get_decorator(node.iter)
        double_decorator = ''
        if decorator != '' and len(node.iter.args) == 1:
            double_decorator = self.get_decorator(node.iter.args[0])
        ast.fix_missing_locations(node)

        if decorator == 'static':
            if double_decorator == 'static':
                raise TaichiSyntaxError("'ti.static' cannot be nested")
            return self.visit_static_for(node, double_decorator == 'grouped')
        elif decorator == 'ndrange':
            if double_decorator != '':
                raise TaichiSyntaxError(
                    "No decorator is allowed inside 'ti.ndrange")
            return self.visit_ndrange_for(node)
        elif decorator == 'grouped':
            if double_decorator == 'static':
                raise TaichiSyntaxError(
                    "'ti.static' is not allowed inside 'ti.grouped'")
            elif double_decorator == 'ndrange':
                return self.visit_grouped_ndrange_for(node)
            elif double_decorator == 'grouped':
                raise TaichiSyntaxError("'ti.grouped' cannot be nested")
            else:
                return self.visit_struct_for(node, is_grouped=True)
        elif isinstance(node.iter, ast.Call) and isinstance(
                node.iter.func, ast.Name) and node.iter.func.id == 'range':
            return self.visit_range_for(node)
        else:  # Struct for
            return self.visit_struct_for(node, is_grouped=False)

    @staticmethod
    def parse_stmt(stmt):
        return ast.parse(stmt).body[0]

    @staticmethod
    def parse_expr(expr):
        return ast.parse(expr).body[0].value

    @staticmethod
    def func_call(name, *args):
        return ast.Call(func=ASTTransformer.parse_expr(name).value,
                        args=list(args),
                        keywords=[])

    def visit_Subscript(self, node):
        self.generic_visit(node)

        value = node.value
        indices = node.slice
        if isinstance(indices.value, ast.Tuple):
            indices = indices.value.elts
        else:
            indices = [indices.value]

        call = ast.Call(func=self.parse_expr('ti.subscript'),
                        args=[value] + indices,
                        keywords=[])
        return ast.copy_location(call, node)

    def visit_IfExp(self, node):
        raise TaichiSyntaxError(
            'Ternary operator ("a if cond else b") is not yet supported in Taichi kernels. Please walk around by changing loop conditions.'
        )

    def visit_Break(self, node):
        return self.parse_stmt('ti.core.insert_break_stmt()')

    def visit_Continue(self, node):
        return self.parse_stmt('ti.core.insert_continue_stmt()')

    def visit_Call(self, node):
        if not (isinstance(node.func, ast.Attribute)
                and isinstance(node.func.value, ast.Name)
                and node.func.value.id == 'ti' and node.func.attr == 'static'):
            # Do not apply the generic visitor if the function called is ti.static
            self.generic_visit(node)
        if isinstance(node.func, ast.Name):
            func_name = node.func.id
            if func_name == 'print':
                node.func = self.parse_expr('ti.ti_print')
            elif func_name == 'min':
                node.func = self.parse_expr('ti.ti_min')
            elif func_name == 'max':
                node.func = self.parse_expr('ti.ti_max')
            elif func_name == 'int':
                node.func = self.parse_expr('ti.ti_int')
            elif func_name == 'float':
                node.func = self.parse_expr('ti.ti_float')
            elif func_name == 'any':
                node.func = self.parse_expr('ti.ti_any')
            elif func_name == 'all':
                node.func = self.parse_expr('ti.ti_all')
            else:
                pass
        return node

    def visit_Module(self, node):
        with self.variable_scope():
            self.generic_visit(node)
        return node

    def visit_Global(self, node):
        with self.variable_scope():
            self.generic_visit(node)
        for name in node.names:
            self.create_variable(name)
        return node

    @staticmethod
    def make_constant(value):
        # Do not use ast.Constant which does not exist in python3.5
        node = ASTTransformer.parse_expr('0')
        node.value = value
        return node

    def visit_FunctionDef(self, node):
        args = node.args
        assert args.vararg is None
        assert args.kwonlyargs == []
        assert args.kw_defaults == []
        assert args.kwarg is None
        import taichi as ti
        if self.is_kernel:  # ti.kernel
            for decorator in node.decorator_list:
                if (isinstance(decorator, ast.Attribute)
                        and isinstance(decorator.value, ast.Name)
                        and decorator.value.id == 'ti'
                        and decorator.attr == 'func'):
                    raise TaichiSyntaxError(
                        "Function definition not allowed in 'ti.kernel'.")
            # Transform as kernel
            arg_decls = []

            # Treat return type
            if node.returns is not None:
                ret_init = self.parse_stmt('ti.decl_scalar_ret(0)')
                ret_init.value.args[0] = node.returns
                self.returns = node.returns
                arg_decls.append(ret_init)
                node.returns = None

            for i, arg in enumerate(args.args):
                # Directly pass in template arguments,
                # such as class instances ("self"), tensors, SNodes, etc.
                if isinstance(self.func.arguments[i], ti.template):
                    continue
                import taichi as ti
                if isinstance(self.func.arguments[i], ti.ext_arr):
                    arg_init = self.parse_stmt('x = ti.decl_ext_arr_arg(0, 0)')
                    arg_init.targets[0].id = arg.arg
                    self.create_variable(arg.arg)
                    array_dt = self.arg_features[i][0]
                    array_dim = self.arg_features[i][1]
                    import numpy as np
                    array_dt = to_taichi_type(array_dt)
                    dt_expr = 'ti.' + ti.core.data_type_short_name(array_dt)
                    dt = self.parse_expr(dt_expr)
                    arg_init.value.args[0] = dt
                    arg_init.value.args[1] = self.parse_expr(
                        "{}".format(array_dim))
                    arg_decls.append(arg_init)
                else:
                    arg_init = self.parse_stmt('x = ti.decl_scalar_arg(0)')
                    arg_init.targets[0].id = arg.arg
                    dt = arg.annotation
                    arg_init.value.args[0] = dt
                    arg_decls.append(arg_init)
            # remove original args
            node.args.args = []

        else:  # ti.func
            for decorator in node.decorator_list:
                if (isinstance(decorator, ast.Attribute)
                        and isinstance(decorator.value, ast.Name)
                        and decorator.value.id == 'ti'
                        and decorator.attr == 'func'):
                    raise TaichiSyntaxError(
                        "Function definition not allowed in 'ti.func'.")
            # Transform as func (all parameters passed by value)
            arg_decls = []
            for i, arg in enumerate(args.args):
                # Directly pass in template arguments,
                # such as class instances ("self"), tensors, SNodes, etc.
                if isinstance(self.func.arguments[i], ti.template):
                    continue
                # Create a copy for non-template arguments,
                # so that they are passed by value.
                arg_init = self.parse_stmt('x = ti.expr_init_func(0)')
                arg_init.targets[0].id = arg.arg
                self.create_variable(arg.arg)
                arg_init.value.args[0] = self.parse_expr(arg.arg +
                                                         '_by_value__')
                args.args[i].arg += '_by_value__'
                arg_decls.append(arg_init)

        with self.variable_scope():
            self.generic_visit(node)

        node.body = arg_decls + node.body
        return node

    def visit_UnaryOp(self, node):
        self.generic_visit(node)
        if isinstance(node.op, ast.Not):
            # Python does not support overloading logical and & or
            new_node = self.parse_expr('ti.logical_not(0)')
            new_node.args[0] = node.operand
            node = new_node
        return node

    def visit_Compare(self, node):
        self.generic_visit(node)
        comparators = [node.left] + node.comparators
        ops = []
        for i in range(len(node.ops)):
            if isinstance(node.ops[i], ast.Lt):
                op_str = 'Lt'
            elif isinstance(node.ops[i], ast.LtE):
                op_str = 'LtE'
            elif isinstance(node.ops[i], ast.Gt):
                op_str = 'Gt'
            elif isinstance(node.ops[i], ast.GtE):
                op_str = 'GtE'
            elif isinstance(node.ops[i], ast.Eq):
                op_str = 'Eq'
            elif isinstance(node.ops[i], ast.NotEq):
                op_str = 'NotEq'
            elif isinstance(node.ops[i], ast.In):
                raise TaichiSyntaxError(
                    '"in" is not supported in Taichi kernels.')
            elif isinstance(node.ops[i], ast.NotIn):
                raise TaichiSyntaxError(
                    '"not in" is not supported in Taichi kernels.')
            elif isinstance(node.ops[i], ast.Is):
                raise TaichiSyntaxError(
                    '"is" is not supported in Taichi kernels.')
            elif isinstance(node.ops[i], ast.IsNot):
                raise TaichiSyntaxError(
                    '"is not" is not supported in Taichi kernels.')
            else:
                raise Exception(f'Unknown operator {node.ops[i]}')
            ops += [ast.copy_location(ast.Str(s=op_str), node)]

        call = ast.Call(
            func=self.parse_expr('ti.chain_compare'),
            args=[
                ast.copy_location(ast.List(elts=comparators, ctx=ast.Load()),
                                  node),
                ast.copy_location(ast.List(elts=ops, ctx=ast.Load()), node)
            ],
            keywords=[])
        call = ast.copy_location(call, node)
        return call

    def visit_BoolOp(self, node):
        self.generic_visit(node)

        def make_node(a, b, token):
            new_node = self.parse_expr('ti.logical_{}(0, 0)'.format(token))
            new_node.args[0] = a
            new_node.args[1] = b
            return new_node

        token = ''
        if isinstance(node.op, ast.And):
            token = 'and'
        elif isinstance(node.op, ast.Or):
            token = 'or'
        else:
            print(node.op)
            print("BoolOp above not implemented")
            exit(0)

        new_node = node.values[0]
        for i in range(1, len(node.values)):
            new_node = make_node(new_node, node.values[i], token)

        return new_node

    def visit_Assert(self, node):
        import astor
        msg = astor.to_source(node.test)
        self.generic_visit(node)
        new_node = self.parse_stmt(
            'ti.core.create_assert_stmt(ti.Expr(0).ptr, 0)')
        new_node.value.args[0].value.args[0] = node.test
        new_node.value.args[1] = self.parse_expr("'{}'".format(msg.strip()))
        return new_node

    def visit_Return(self, node):
        self.generic_visit(node)
        if self.is_kernel:
            # TODO: check if it's at the end of a kernel, throw TaichiSyntaxError if not
            if node.value is not None:
                if self.returns is None:
                    raise TaichiSyntaxError(
                        'kernel with return value must be '
                        'annotated with a return type, e.g. def func() -> ti.f32'
                    )
                ret_expr = self.parse_expr('ti.cast(ti.Expr(0), 0)')
                ret_expr.args[0].args[0] = node.value
                ret_expr.args[1] = self.returns
                ret_stmt = self.parse_stmt(
                    'ti.core.create_kernel_return(ret.ptr)')
                ret_stmt.value.args[0].value = ret_expr
                return ret_stmt
        return node


"""Galaxy ellipticity module.

This module provides facilities to sample the ellipticities of galaxies.
"""

import numpy as np
from scipy import stats

__all__ = [
    'beta_ellipticity',
    'ryden04',
]

def beta_ellipticity(e_ratio, e_sum, size=None):
    r'''Galaxy ellipticities sampled from a reparameterized beta distribution.

    The ellipticities follow a beta distribution parameterized by
    :math:`e_{\rm ratio}` and :math:`e_{\rm sum}` as presented in [1]_ Section
    III.A.

    Parameters
    ----------
    e_ratio : array_like
        Mean ellipticity of the distribution, must be between 0 and 1.
    e_sum : array_like
        Parameter controlling the width of the distribution, must be positive.

    Notes
    -----
    The probability distribution function :math:`p(e)` for ellipticity
    :math:`e` is given by a beta distribution:

    .. math::

        p(e) \sim \frac{\Gamma(a+b)}{\Gamma(a) \Gamma(b)} x^{a-1} (1-x)^{b-1}

    for :math:`0 <= e <= 1`, :math:`a = e_{\rm sum} e_{\rm ratio}`,
    :math:`b = e_{\rm sum} (1 - e_{\rm ratio})`, :math:`0 < e_{\rm ratio} < 1`
    and :math:`e_{\rm sum} > 0`, where :math:`\Gamma` is the gamma function.

    References
    ----------
    .. [1] Kacprzak T., Herbel J., Nicola A. et al., arXiv:1906.01018

    Examples
    --------
    Sample 10000 random variates from the Kacprzak model with
    :math:`e_{\rm ratio} = 0.5` and :math:`e_{\rm sum} = 1.0`:

    >>> from skypy.galaxy.ellipticity import beta_ellipticity
    >>> ellipticity = beta_ellipticity(0.5, 1.0, size=10000)

    '''

    # convert to beta distribution parameters
    a = e_sum * e_ratio
    b = e_sum * (1.0 - e_ratio)

    # sample from the beta distribution
    return np.random.beta(a, b, size)

def ryden04(mu_gamma, sigma_gamma, mu, sigma, size=None):
    r'''Ellipticity distribution of Ryden (2004).

    The ellipticity is sampled by randomly projecting a 3D ellipsoid with
    principal axes :math:`A > B > C` [1]_. The distribution of the axis ratio
    :math:`\gamma = C/A` is a truncated normal with mean :math:`\mu_\gamma` and
    standard deviation :math:`\sigma_\gamma`. The distribution of
    :math:`\epsilon = \log(1 - B/A)` is truncated normal with mean :math:`\mu`
    and standard deviation :math:`\sigma`.

    Parameters
    ----------
    mu_gamma : array_like
        Mean of the truncated Gaussian for :math:`\gamma`.
    sigma_gamma : array_like
        Standard deviation for :math:`\gamma`.
    mu : array_like
        Mean of the truncated Gaussian for :math:`\epsilon`.
    sigma : array_like
        Standard deviation for :math:`\epsilon`.
    size : int or tuple of ints or None
        Size of the sample. If `None` the size is inferred from the parameters.

    References
    ----------
    .. [1] Ryden B. S., 2004, ApJ, 601, 214

    Examples
    --------
    Sample 10000 random variates from the Ryden (2004) model with parameters
    :math:`\mu_\gamma = 0.222`, :math:`\sigma_\gamma = 0.056`,
    :math:`\mu = -1.85`, and :math:`\sigma = 0.89`.

    >>> from skypy.galaxy.ellipticity import ryden04
    >>> ellipticity = ryden04(0.222, 0.056, -1.85, 0.89, size=10000)

    '''

    # get size if not given
    if size is None:
        size = np.broadcast(mu_gamma, sigma_gamma, mu, sigma).shape

    # truncation for gamma standard normal
    a_gam = np.divide(np.negative(mu_gamma), sigma_gamma)
    b_gam = np.divide(np.subtract(1, mu_gamma), sigma_gamma)

    # truncation for log(epsilon) standard normal
    a_eps = -np.inf
    b_eps = np.divide(np.negative(mu), sigma)

    # draw gamma and epsilon from truncated normal -- eq.s (10)-(11)
    gam = stats.truncnorm.rvs(a_gam, b_gam, mu_gamma, sigma_gamma, size=size)
    eps = np.exp(stats.truncnorm.rvs(a_eps, b_eps, mu, sigma, size=size))

    # scipy 1.5.x bug: make scalar if size is empty
    if size == () and not np.isscalar(gam):  # pragma: no cover
        gam, eps = gam.item(), eps.item()

    # draw random viewing angle (theta, phi)
    cos2_theta = np.random.uniform(low=-1., high=1., size=size)**2
    cos2_phi = np.cos(np.random.uniform(low=0., high=2*np.pi, size=size))**2
    sin2_theta = 1 - cos2_theta
    sin2_phi = 1 - cos2_phi

    # compute Binney's ABC -- eq.s (13)-(15)
    A = (1 - eps*(2-eps)*sin2_phi)*cos2_theta + gam**2*sin2_theta
    B = (2*eps*(2-eps))**2*cos2_theta*sin2_phi*cos2_phi
    C = 1 - eps*(2-eps)*cos2_phi

    # compute axis ratio q -- eq. (12)
    q = np.sqrt((A+C-np.sqrt((A-C)**2+B))/(A+C+np.sqrt((A-C)**2+B)))

    # return the ellipticity
    return (1-q)/(1+q)


#!/usr/bin/env python
#
# ======================================================================
#
# Brad T. Aagaard, U.S. Geological Survey
# Charles A. Williams, GNS Science
# Matthew G. Knepley, University of Chicago
#
# This code was developed as part of the Computational Infrastructure
# for Geodynamics (http://geodynamics.org).
#
# Copyright (c) 2010-2017 University of California, Davis
#
# See COPYING for license information.
#
# ======================================================================
#

import numpy

def test_scalararray(obj, valuesE, values, places=6):
    """
    Check whether arrays containing double values match.

    @param obj Test object
    @valuesE Array of expected values
    @values Array of values to check
    """

    # Check shape
    obj.assertEqual(len(valuesE.shape), len(values.shape))
    for (dimE, dim) in zip(valuesE.shape, values.shape):
        obj.assertEqual(dimE, dim)

    # Check values
    for (vE, v) in zip(numpy.reshape(valuesE, -1),
                       numpy.reshape(values, -1)):
        if vE == 0.0:
            obj.assertAlmostEqual(v, vE, places)
        else:
            obj.assertAlmostEqual(1.0, v/vE, places)

    return

# End of file


import os
import random
import numpy as np

# variable definitions
inriapath = "data/INRIA"
nb_testimg = 37

# initialize strings which contain the content of the csv file
csv_str_train = "Filename;Width;Height;Roi.X1;Roi.Y1;Roi.X2;Roi.Y2;ClassId"
csv_str_test = "Filename;Width;Height;Roi.X1;Roi.Y1;Roi.X2;Roi.Y2;ClassId"

# determine which images shall be used as test images
test_images = np.empty((15,nb_testimg))
for id, row in enumerate(test_images):
	test_images[id] = random.sample(range(186), nb_testimg)
test_images = test_images.astype(np.int16)

# iterate over files
for path, subdirs, files in os.walk(inriapath):
	# check whether we are in folder 'deFace' and skip it
	if not path.endswith("deFace"):
		# iterate over file
		for idx, f in enumerate(files):
			# check whether file is an image
			if f.endswith(".jpg"):
				obj_id = int(path[-2:])
				for i in range(nb_testimg):
					if idx == test_images[int(obj_id) - 1, i]:
						csv_str_test += "\nPersonne" + str(obj_id).zfill(2) + "/" + f + ";384;288;0;0;384;288;" + str(obj_id - 1)
						break
				else:
					csv_str_train += "\nPersonne" + str(obj_id).zfill(2) + "/" + f + ";384;288;0;0;384;288;" + str(obj_id - 1)

with open("data/INRIA/INRIA_TRAIN.csv", "w") as text_file:
	text_file.write(csv_str_train)
with open("data/INRIA/INRIA_TEST.csv", "w") as text_file:
	text_file.write(csv_str_test)


# -*- coding: utf-8 -*-
from __future__ import print_function
from flask import Flask, jsonify,request
from flask import make_response
from impala.util import as_pandas
import json
from utils import get_sql,get_conn,get_now_and_7days_time,get_yes_time,trans_array,trans_array2,trans_array3
import numpy as np

app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
cur=get_conn()

'''获得省'''
@app.route('/evcard-datav/api/province', methods=['GET'])
def get_province():
    sql_pro = get_sql("sql", "sql_pro")
    cur.execute(sql_pro)
    df = as_pandas(cur)
    return json.dumps(df.to_dict(orient='records'), ensure_ascii=False)
'''获得市'''
@app.route('/evcard-datav/api/city/<provinceid>', methods=['GET'])
def get_city(provinceid):
    tmpsql =  get_sql("sql","sql_city")+provinceid
    cur.execute(tmpsql)
    df = as_pandas(cur)
    return json.dumps(df.to_dict(orient='records'), ensure_ascii=False)
'''获得区'''
@app.route('/evcard-datav/api/area/<cityid>', methods=['GET'])
def get_area(cityid):
    tmpsql = get_sql("sql","sql_area") + cityid
    cur.execute(tmpsql)
    df = as_pandas(cur)
    return json.dumps(df.to_dict(orient='records'), ensure_ascii=False)

'''1网点response'''
@app.route('/evcard-datav/api/shop')
def api_shop():
    yes_time = get_yes_time()
    sql = get_sql("sql","sql_shop")\
        .replace("long1",str(float(request.args['longitude1'])*1000000)) \
        .replace("long2", str(float(request.args['longitude2'])*1000000))\
        .replace("lati1",str(float(request.args['latitude1'])*1000000))\
        .replace("lati2",str(float(request.args['latitude2'])*1000000))\
        .replace("yes_time",yes_time)
    print (sql)
    cur.execute(sql)
    df = as_pandas(cur)
    print (len(df))
    res=trans_array2(df)
    return res

'''2支付比例折线图'''
@app.route('/evcard-datav/api/payrate_zhexian')
def api_payrate_zhexian():
    now_time, seven_days_time = get_now_and_7days_time()
    if 'provinceid' in request.args and 'cityid' in request.args and "areaid" in request.args and "shopid" not in request.args:
        sql=get_sql("sql","sql_payrate_zhexian1")\
            .replace("get_provinced_id",request.args['provinceid'])\
            .replace("get_city_id",request.args['cityid'])\
            .replace("get_area_id",request.args['areaid'])\
            .replace("seven_days_time","'"+seven_days_time+"'")\
            .replace("now_time","'"+now_time+"'")
        print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array(df)
        return res
    if 'provinceid' in request.args and 'cityid' in request.args and "shopid" not in request.args:
        sql = get_sql("sql", "sql_payrate_zhexian2") \
            .replace("get_provinced_id", request.args['provinceid']) \
            .replace("get_city_id", request.args['cityid']) \
            .replace("seven_days_time", "'"+seven_days_time+"'") \
            .replace("now_time", "'"+now_time+"'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array(df)
        return res
    if 'provinceid' in request.args and "shopid" not in request.args:
        sql = get_sql("sql", "sql_payrate_zhexian3") \
            .replace("get_provinced_id", request.args['provinceid']) \
            .replace("seven_days_time", "'"+seven_days_time+"'") \
            .replace("now_time", "'"+now_time+"'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array(df)
        return res
    if 'shopid' in request.args :
        sql = get_sql("sql", "sql_payrate_zhexian4") \
            .replace("shop__", request.args['shopid']) \
            .replace("seven_days_time", "'" + seven_days_time + "'") \
            .replace("now_time", "'" + now_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array(df)
        return res
    else:
        sql = get_sql("sql", "sql_payrate_zhexian5") \
            .replace("seven_days_time", "'"+seven_days_time+"'") \
            .replace("now_time", "'"+now_time+"'")
        print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array(df)
        return res

'''3支付比例饼图'''
@app.route('/evcard-datav/api/payrate_bing')
def api_payrate_bing():
    yes_time = get_yes_time()
    if 'provinceid' in request.args and 'cityid' in request.args and "areaid" in request.args and "shopid" not in request.args:
        sql=get_sql("sql","sql_payrate1")\
            .replace("province_",request.args['provinceid'])\
            .replace("city_",request.args['cityid'])\
            .replace("area_",request.args['areaid']) \
            .replace("yes_time", "'" + yes_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array3(df)
        return res
    if 'provinceid' in request.args and 'cityid' in request.args and "shopid" not in request.args:
        sql = get_sql("sql", "sql_payrate2") \
            .replace("province_", request.args['provinceid']) \
            .replace("city_", request.args['cityid']) \
            .replace("yes_time", "'" + yes_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array3(df)
        return res
    if 'provinceid' in request.args and "shopid" not in request.args:
        sql = get_sql("sql", "sql_payrate3") \
            .replace("province_", request.args['provinceid']) \
            .replace("yes_time", "'" + yes_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array3(df)
        return res
    if 'shopid' in request.args :
        sql = get_sql("sql", "sql_payrate4") \
            .replace("shop__", request.args['shopid']) \
            .replace("yes_time", "'" + yes_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array3(df)
        return res

    else:
        sql = get_sql("sql", "sql_payrate5") \
            .replace("yes_time", "'" + yes_time + "'")
        #print (sql)
        cur.execute(sql)
        df = as_pandas(cur)
        res = trans_array3(df)
        return res
@app.errorhandler(404)
def not_found(error):
    return make_response(jsonify({'error': 'Not found'}), 404)

if __name__ == '__main__':
    app.run(debug=False,port=5000,host='0.0.0.0')


import numpy as np
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader, TensorDataset
from sklearn.metrics import accuracy_score
import time

class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)
        self.out = torch.nn.Linear(n_hidden, n_output)

    def forward(self, x):
        x = F.relu(self.hidden(x))
        x = self.out(x)
        return x

def get_kfold_data(k, i, X, y):
    #split the data
    #get the training set and validaion set of each fold

    fold_size = X.shape[0] // k
    val_start = i * fold_size
    if i != k - 1:
        val_end = (i + 1) * fold_size
        X_valid, y_valid = X[val_start:val_end], y[val_start:val_end]
        X_train = torch.cat((X[0:val_start], X[val_end:]), dim=0)
        y_train = torch.cat((y[0:val_start], y[val_end:]), dim=0)
    else:
        #put more data in the last fold if not divisible
        X_valid, y_valid = X[val_start:], y[val_start:]
        X_train = X[0:val_start]
        y_train = y[0:val_start]

    return X_train, y_train, X_valid, y_valid

def train_fold(model, X_train, y_train, X_val, y_val, BATCH_SIZE, learning_rate, EPOCHS):

    #package the data
    train_loader = DataLoader(TensorDataset(X_train, y_train), BATCH_SIZE, shuffle=True)
    val_loader = DataLoader(TensorDataset(X_val, y_val), BATCH_SIZE, shuffle=True)
    #loss function
    criterion = nn.CrossEntropyLoss()
    #optimizer
    optimizer = torch.optim.SGD(params=model.parameters(), lr=learning_rate)

    losses = []
    val_losses = []
    train_acc = []
    val_acc = []

    for epoch in range(EPOCHS):
        correct = 0
        for i, (features, labels) in enumerate(train_loader):
            optimizer.zero_grad()
            outputs = model(features)

            #calculate the loss function
            loss = criterion(outputs, labels)
            loss.backward()
            optimizer.step()
            losses.append(loss.item())

            #calculate the accuracy of each epoch in the training set
            y_pred = model(features)
            pred = torch.max(y_pred, 1)[1]
            correct += pred.eq(labels.view_as(pred)).sum().item()
        accuracy = 100. * correct / len(X_train)
        train_acc.append(accuracy)

        #calculate the accuracy of each epoch in the validation set
        val_loss = 0
        correct = 0

        with torch.no_grad():
            for i, (features, labels) in enumerate(val_loader):
                optimizer.zero_grad()
                y_pred = model(features)
                #calculate batch average loss
                loss = criterion(y_pred, labels).item()
                #sum up batch loss
                val_loss += loss * len(labels)
                pred = torch.max(y_pred, 1)[1]
                correct += pred.eq(labels.view_as(pred)).sum().item()
        val_losses.append(val_loss / len(X_val))
        accuracy = 100. * correct / len(X_val)
        val_acc.append(accuracy)

    return losses, val_losses, train_acc, val_acc

def k_fold(Net, k, X_train, y_train, num_epochs=50, learning_rate=0.0001, batch_size=20):

    train_loss_sum, valid_loss_sum = 0, 0
    train_acc_sum, valid_acc_sum = 0, 0

    for i in range(k):
        #get the spilt data
        data = get_kfold_data(k, i, X_train, y_train)
        #My neural network
        net = Net
        #train each part of the data
        train_loss, val_loss, train_acc, val_acc = train_fold(net, *data, num_epochs, learning_rate, batch_size)

        train_loss_sum += train_loss[-1]
        valid_loss_sum += val_loss[-1]
        train_acc_sum += train_acc[-1]
        valid_acc_sum += val_acc[-1]
    print('average train loss:{:.4f}, average train accuracy:{:.3f}%'.format(train_loss_sum / k, train_acc_sum / k))
    print('average valid loss:{:.4f}, average valid accuracy:{:.3f}%'.format(valid_loss_sum / k, valid_acc_sum / k))

    return

if __name__ == '__main__':

    #load and process the data
    wine_data = np.load('wine.npz')
    # print(breast_cancer_data.files)
    # ['train_X', 'train_Y', 'test_X', 'test_Y']

    train_X = wine_data['train_X']
    train_Y = wine_data['train_Y']
    test_X = wine_data['test_X']
    test_Y = wine_data['test_Y']

    input = torch.FloatTensor(train_X)
    label = torch.LongTensor(train_Y)
    test_input = torch.FloatTensor(test_X)
    test_label = torch.LongTensor(test_Y)

    MyNet = Net(n_feature=13, n_hidden=10, n_output=2) #You can change the parameters here!

    #10-fold cross validation to choose the best hidden units
    k_fold(MyNet,10,input,label)

    #Testing
    start = time.time()
    loss_func = torch.nn.CrossEntropyLoss()
    optimizer = torch.optim.SGD(MyNet.parameters(), lr=0.0001)

    for i in range(10000):
        out = MyNet(test_input)
        loss = loss_func(out, test_label)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    out = MyNet(test_input)
    prediction = torch.max(out, 1)[1]
    pred_y = prediction.numpy()
    target_y = test_label.data.numpy()
    print(accuracy_score(target_y, pred_y))
    end = time.time()
    running_time = end - start
    print('time cost : %.5f sec' % running_time)


## 1. Реализовать вывод информации о промежутке времени в зависимости от
# его продолжительности duration в секундах: до минуты: <s> сек; до
# часа: <m> мин <s> сек; до суток: <h> час <m> мин <s> сек;
# * в остальных случаях: <d> дн <h> час <m> мин <s> сек.

duration = int(input("Введите число: "))

ss = duration % 60
mm = duration // 60
hh = duration // 3600
dd = duration // 86400

if mm < 1:
    print(f"{ss} сек")
elif hh < 1:
    print(f"{mm} мин {ss} сек")
elif dd < 1:
    mm = mm % 60
    print(f"{hh} час {mm} мин {ss} сек")
else:
    mm = mm % 60
    hh = hh % 24
    print(f"{dd} дн {hh} час {mm} мин {ss} сек")


"""
BaseOAuth is inspired from Darren Kempiners YahooAPI https://github.com/dkempiners/python-yahooapi/blob/master/yahooapi.py
"""
from __future__ import absolute_import

try:
    input = raw_input
except NameError:
    pass

import pdb
import json
import time
import logging
import webbrowser

import base64

from rauth import OAuth1Service, OAuth2Service
from rauth.utils import parse_utf8_qsl

logging.basicConfig(level=logging.DEBUG, format="[%(asctime)s %(levelname)s] [%(name)s.%(module)s.%(funcName)s] %(message)s")
logging.getLogger('yahoo-oauth')

services = {
    'oauth1': dict(
        SERVICE = OAuth1Service,
        REQUEST_TOKEN_URL = "https://api.login.yahoo.com/oauth/v2/get_request_token",
        ACCESS_TOKEN_URL = "https://api.login.yahoo.com/oauth/v2/get_token",
        AUTHORIZE_TOKEN_URL = "https://api.login.yahoo.com/oauth/v2/request_auth"
    ),
    'oauth2': dict(
        SERVICE = OAuth2Service,
        AUTHORIZE_TOKEN_URL = "https://api.login.yahoo.com/oauth2/request_auth",
        ACCESS_TOKEN_URL = "https://api.login.yahoo.com/oauth2/get_token"
    )
}
CALLBACK_URI = 'oob'

def json_write_data(json_data, filename):
    """Write json data into a file
    """
    with open(filename, 'w') as fp:
        json.dump(json_data, fp, indent=4, sort_keys=True, ensure_ascii=False)
        return True
    return False

def json_get_data(filename):
    """Get data from json file
    """
    with open(filename) as fp:
        json_data = json.load(fp)
        return json_data

    return False

class BaseOAuth(object):
    """
    """
    def __init__(self, oauth_version, consumer_key, consumer_secret, **kwargs):
        """
        consumer_key : client key
        consumer_secret : client secret
        access_token : access token
        access_token_secret : access token secret
        from_file : file containing the credentials
        """
        self.oauth_version = oauth_version

        if kwargs.get('from_file'):
            logging.debug("Checking ")
            self.from_file = kwargs.get('from_file')
            json_data = json_get_data(self.from_file)
            vars(self).update(json_data)
        else:
            self.consumer_key = consumer_key
            self.consumer_secret = consumer_secret

        vars(self).update(kwargs)

        self.oauth_version = oauth_version
        self.callback_uri = vars(self).get('callback_uri',CALLBACK_URI)

        # Init OAuth
        if self.oauth_version == 'oauth1':
            service_params = {
                'consumer_key': self.consumer_key,
                'consumer_secret' : self.consumer_secret,
                'request_token_url': services[self.oauth_version]['REQUEST_TOKEN_URL']
            }
        else:
            service_params = {
                'client_id': self.consumer_key,
                'client_secret': self.consumer_secret
            }

        service_params.update({
            'name' : 'yahoo',
            'access_token_url' : services[self.oauth_version]['ACCESS_TOKEN_URL'],
            'authorize_url' : services[self.oauth_version]['AUTHORIZE_TOKEN_URL'],
            'base_url': vars(self).get('base_url',None)
        })

        # Defining oauth service
        self.oauth = services[oauth_version]['SERVICE'](**service_params)

        if vars(self).get('access_token') and vars(self).get('access_token_secret') and vars(self).get('session_handle'):
            if not self.token_is_valid():
                json_data.update(self.refresh_access_token())
        elif vars(self).get('access_token') and vars(self).get('token_type') and vars(self).get('refresh_token'):
            if not self.token_is_valid():
                json_data.update(self.refresh_access_token())
        else:
            json_data.update(self.handler())

        # Getting session
        if self.oauth_version == 'oauth1':
            self.session = self.oauth.get_session((self.access_token, self.access_token_secret))
        else:
            self.session = self.oauth.get_session(token=self.access_token)

        json_write_data(json_data, self.from_file)

    def handler(self,):
        """* get request token if OAuth1
            * Get user authorization
            * Get access token
        """

        if self.oauth_version == 'oauth1':
            request_token, request_token_secret = self.oauth.get_request_token(params={'oauth_callback': self.callback_uri})
            logging.debug("REQUEST_TOKEN = {0}\n REQUEST_TOKEN_SECRET = {1}\n".format(request_token, request_token_secret))
            authorize_url = self.oauth.get_authorize_url(request_token)
        else:
            authorize_url = self.oauth.get_authorize_url(client_secret=self.consumer_secret, redirect_uri=self.callback_uri, response_type='code')

        logging.debug("AUTHORISATION URL : {0}".format(authorize_url))
        # Open authorize_url
        webbrowser.open(authorize_url)
        self.verifier = input("Enter verifier : ")

        self.token_time = time.time()

        credentials = {'token_time': self.token_time}

        if self.oauth_version == 'oauth1':
            raw_access = self.oauth.get_raw_access_token(request_token, request_token_secret, params={"oauth_verifier": verifier})
            parsed_access = parse_utf8_qsl(raw_access.content)

            self.access_token = parsed_access['oauth_token']
            self.access_token_secret = parsed_access['oauth_token_secret']
            self.session_handle = parsed_access['oauth_session_handle']

            # Updating credentials
            credentials.update({
                'access_token': self.access_token,
                'access_token_secret': self.access_token_secret,
                'session_handle': self.session_handle
            })
        else:
            # Building headers
            headers = self.generate_oauth2_headers()
            # Getting access token
            raw_access = self.oauth.get_raw_access_token(data={"code": self.verifier, 'redirect_uri': self.callback_uri,'grant_type':'authorization_code'}, headers=headers)
            #parsed_access = parse_utf8_qsl(raw_access.content.decode('utf-8'))
            credentials.update(self.oauth2_access_parser(raw_access))

        return credentials

    def generate_oauth2_headers(self):
        """Generates header for oauth2
        """
        encoded_credentials = base64.b64encode(('{0}:{1}'.format(self.consumer_key,self.consumer_secret)).encode('utf-8'))
        headers={'Authorization':'Basic {0}'.format(encoded_credentials.decode('utf-8'))}

        return headers

    def oauth2_access_parser(self, raw_access):
        """Parse oauth2 access
        """
        parsed_access = json.loads(raw_access.content.decode('utf-8'))
        self.access_token = parsed_access['access_token']
        self.token_type = parsed_access['token_type']
        self.refresh_token = parsed_access['refresh_token']

        credentials = {
            'access_token': self.access_token,
            'token_type': self.token_type,
            'refresh_token': self.refresh_token
        }

        return credentials

    def refresh_access_token(self,):
        """Refresh access token
        """
        logging.debug("REFRESHING TOKEN")
        self.token_time = time.time()
        credentials = {
            'token_time': self.token_time
        }

        if self.oauth_version == 'oauth1':
            self.access_token, self.access_token_secret = self.oauth.get_access_token(self.access_token, self.access_token_secret, params={"oauth_session_handle": self.session_handle})
            credentials.update({
                'access_token': self.access_token,
                'access_token_secret': self.access_token_secret,
                'session_handle': self.session_handle,
                'token_time': self.token_time
            })
        else:
            headers = self.generate_oauth2_headers()

            raw_access = self.oauth.get_raw_access_token(data={"refresh_token": self.refresh_token, 'redirect_uri': self.callback_uri,'grant_type':'refresh_token'}, headers=headers)
            credentials.update(self.oauth2_access_parser(raw_access))

        return credentials

    def token_is_valid(self,):
        """Check the validity of the token :3600s
        """
        elapsed_time = time.time() - self.token_time
        logging.debug("ELAPSED TIME : {0}".format(elapsed_time))
        if elapsed_time > 3540: # 1 minute before it expires
            logging.debug("TOKEN HAS EXPIRED")
            return False

        logging.debug("TOKEN IS STILL VALID")
        return True

class OAuth1(BaseOAuth):
    """Class handling OAuth v1
    """

    def __init__(self, consumer_key, consumer_secret, **kwargs):

        super(OAuth1, self).__init__('oauth1', consumer_key, consumer_secret, **kwargs)

class OAuth2(BaseOAuth):
    """Calss handling OAuth v2
    """

    def __init__(self, consumer_key, consumer_secret, **kwargs):

        super(OAuth2, self).__init__('oauth2', consumer_key, consumer_secret, **kwargs)


__author__ = 'abdul'

from task import EVENT_TYPE_INFO
from utils import listify
from makerpy.object_collection import ObjectCollection
from mongo_utils import objectiditify

###############################################################################
# MBSObjectCollection class
###############################################################################
class MBSObjectCollection(ObjectCollection):
    ###########################################################################
    def __init__(self, collection, clazz=None, type_bindings=None):
        # call super
        ObjectCollection.__init__(self, collection, clazz=clazz,
                                  type_bindings=type_bindings)

    ###########################################################################
    def get_by_id(self, task_id):
        task_id = objectiditify(task_id)
        q = {
            "_id": task_id
        }
        return self.find_one(q)

###############################################################################
# MBSTaskCollection class
###############################################################################
class MBSTaskCollection(MBSObjectCollection):
    ###########################################################################
    def __init__(self, collection, clazz=None, type_bindings=None):
        # call super
        MBSObjectCollection.__init__(self, collection, clazz=clazz,
                                     type_bindings=type_bindings)

    ###########################################################################
    def update_task(self, task, properties=None, event_name=None,
                    event_type=EVENT_TYPE_INFO, message=None, details=None):
        """
            Updates the specified properties of the specified MBSTask object
        """
        task_doc = task.to_document()
        q = {
            "_id": task.id
        }

        u = {}
        # construct $set operator
        if properties:
            properties = listify(properties)
            u["$set"] = {}
            for prop in properties:
                u["$set"][prop] = task_doc.get(prop)

        # construct the $push

        if event_name or message:
            log_entry = task.log_event(name=event_name, event_type=event_type,
                                       message=message, details=details)
            u["$push"] = {"logs": log_entry.to_document()}

        self.update(spec=q, document=u)


import io
import pathlib
import re
import os
from pathlib import Path

test_data_types = ['Method_RW_Method', 'Method_RW_Var', 'Multiple_Transform', 'VAR_RW_Method', 'VAR_RW_VAR', 'binOperatorModif', 'constChange', 'expLogicExpand', 'expLogicReduce', 'unwrapIfElse', 'unwrapMethod', 'wrapsIfElse_NULL', 'wrapsIfElse_Others', 'wrapsIf_NULL', 'wrapsIf_Others', 'wrapsMethod', 'wrapsTryCatch']

def main():
    experiment_path = pathlib.Path(__file__).absolute().parent.parent / 'experiment'
    for experiment_type_path in experiment_path.iterdir():
        for parameter_sweep_path in experiment_type_path.rglob('*_parameter_sweep'):
            models_path = parameter_sweep_path / 'models'
            if not models_path.exists():
                continue

            average_cmd = 'onmt-average-checkpoints'
            average_cmd += ' --model_dir {}'.format(str(models_path.resolve()))
            average_cmd += ' --output_dir {}'.format(str((models_path/'avg_5_model').resolve()))
            average_cmd += ' --max_count 5'
            print('Running cmd: ' + average_cmd)
            os.system(average_cmd)
            average_mode_path = (models_path / 'avg_5_model') / 'model.ckpt-300000'

            config_path = parameter_sweep_path / 'data.yml'
            for test_data_type in test_data_types:
                eval_test_cmd = 'onmt-main infer'
                eval_test_cmd += ' --config {}'.format(str(config_path.resolve()))
                test_node_labels_path = Path(str(experiment_type_path).replace('experiment', 'test_data')) / '{}_data_for_node_label.txt'.format(test_data_type)
                test_node_types_path = Path(str(experiment_type_path).replace('experiment', 'test_data')) / '{}_data_for_node_type.txt'.format(test_data_type)
                eval_test_cmd += ' --features_file {} {}'.format(str(test_node_labels_path.resolve()), str(test_node_types_path.resolve()))
                prediction_label_transform_path = parameter_sweep_path / '{}_data_for_node_transform.txt.prediction'.format(test_data_type)
                eval_test_cmd += ' --predictions_file {}'.format(str(prediction_label_transform_path.resolve()))
                eval_test_cmd += ' --checkpoint_path {}'.format(str(average_mode_path.resolve()))
                print('Running cmd: ' + eval_test_cmd)
                os.system(eval_test_cmd)

if __name__=="__main__":
    main()


# -*- coding:utf8 -*-
# 直接根据https://github.com/knightsj/awesome-algorithm-question-solution中数组的顺序

# 4.1数组中是否包含重复元素。只要有包含重复元素，就返回true，否则就返回false
def duplicated(array):
    dict = {}
    for num in array:
        if dict.has_key(num):
            dict[num] += 1
        else:
            dict[num] = 1
    for value in dict.itervalues():
        if value > 1:
            return True
    return  False

array1 = [1, 2, 5, 6, 8, 11, 323, 4]
# print(duplicated(array1))

# 4.2 出现次数超过数组长度一半的元素
# 这种题没有意义,如果没有特殊要求，还是像上面那样字典解决

# 4.3 数组中只出现过一次的数字
# Given a **non-empty** array of integers, every element appears *twice* except for one. Find that single one.
# 题目本身不难，但是它有额外的要求 "线性时间复杂度，不使用额外空间"，所以还是值得一做。66题书的第40题简化版
# 记住成对的数字在异或运算中会被抵消，最终剩下单个数字  -------
def singleOne(array):
    result = 0
    for n in array:
        result ^= n
        # print(result)
    return result
array3 = [15,2,2,3,5,1,1,5,15,8,8]
# print(singleOne(array3))

# 4.4 寻找数组中缺失的数字
# Given an array containing *n* distinct numbers taken from `0, 1, 2, ..., n`, find the one that is missing from the array.
# 这是方差为1的等差数列，直接用公式理论求和，然后依次减掉数组中的每一个数，最终剩下的结果，就是缺失的数了。
def findLackNum(array):
    n = len(array)
    total = (n+1)*n/2
    for i in array:
        total -= i
    return total
array4 = [9,6,4,2,3,5,7,0,1]
# print(findLackNum(array4))

# 4.5 将所有的0移动到数组末尾
# 要求非0的元素顺序不变，必须原地处理，不能使用辅助数组,使用尽可能少的操作。也就是时间复杂度一般要O(n). 一般这种"原地处理"的要求，就要依赖指针了。
# 这种思路比较精巧，基本还是靠熟练度了
def moveZeroEnd(array):
    k = 0
    for i in range(len(array)):
        if array[i] != 0:
            array[k],array[i] = array[i],array[k]
            k += 1

array5 =  [9,0,4,2,0,5,7,0,1]
moveZeroEnd(array5)
#print(array5)

# 4.6 移除数组中等于某个值的元素， 返回新的数组长度
# 要求原地解决，空间复杂度为0(1), 其实和上面那题没什么差别
def remvoeSome(array,val):
    k = 0
    for i in range(len(array)):
        if array[i] != 0:
            array[k],array[i] = array[i],array[k]
            k += 1
    return k

#print(remvoeSome(array5,0))

# 4.7 三色旗问题
# Given an array with *n* objects colored red, white or blue, sort them **in-place **so that objects of the same color are adjacent, with the colors in the order red, white and blue.
#
# Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively.
# 要求： 空间复杂度O(1)， 时间复杂度为一趟遍历
# 这种原地操作的，基本就等同于指针操作
def threeColor(array):
    low,mid = 0,0
    hight = len(array)-1

    while mid<=hight:
        if array[mid] == 0:
            array[low],array[mid] = array[mid],array[low]
            low +=1
            mid +=1
        elif array[mid] == 1:
            mid +=1
        else:
            array[mid],array[hight] = array[hight],array[mid]
            hight-=1

array7 = [0,2,2,2,0,1,1,0,2]
threeColor(array7)
#print(array7)

# 4.8 有序数组内部的两个元素的和为目标值
# 这题很像动态规划的画表格思路，考虑到重复问题，建立二维数组后，只需要遍历上三角
def twoSum(array,val):

    result = []
    for i in range(len(array)-1):
        k = i+1
        for k in range(len(array)):
            if array[i]+array[k] == val:
                result.append(i)
                result.append(k)
                return result

    return []

array8 = [31,2,7,11,5]
val = 9
# print(twoSum(array8,9))

# 4.9 无序数组和大于或等于某值的最小子数组
# Given an array of **n** positive integers and a positive integer **s**,
#  find the minimal length of a **contiguous** subarray of which the sum ≥ **s**. If there isn't one, return 0 instead.
# 其实这题按照动态规划建表的思路也是可以做的，但是时间复杂度比较大，尝试滑动窗口, 它这里求的是子序列的长度，而不是子序列本身
# 这题和 "和至少为K的子数组没什么差别"
def minSubArray(nums,val):
    if nums == None:
        return 0
    start,end,sum,min = 0,0,0,

nums = [2,7,5,11]
val = 14
# print(minSubArray(nums,val))

# 4.10 两数组的交点元素


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# ---------------------------------------------------------------------
# Copyright 2018(c). All rights reserved.
#
# This is free software; you can do what the LICENCE file allows you to.
# Author: Ing. Oraldo Jacinto Simon

import os
import re
from line import CountLine
from texttable import Texttable, get_color_string as color, bcolors

class main(object):
    '''Calculate the number of coding lines per class and the number of functions.

    '''

    def __init__(self):
        # Going through the python directory of the program code 1.
        #rootDir = '../../Programa1/Code'
        rootDir = '.'
        res = []
        line = CountLine()
        for dirName, subdirList, fileList in os.walk(rootDir):
            for fname in fileList:
                # Processing name's file
                mo = re.search(".py$", fname)
                if mo:
                    path = './{fichero}'
                    # path = '../../Programa1/Code/{fichero}'
                    path_fname = path.format(fichero=fname)
                    try:
                        fichero = open(path_fname, "r")
                        result = fichero.read()
                        lines = result.split('\n')
                        res.append(line.count_line(lines))
                        fichero.close()
                    except:
                        print('No existe el fichero %s' % fname)
        table = Texttable()
        table.set_cols_align(["c", "c", "c", "c"])
        table.set_cols_valign(["t", "m", "b", "m"])
        head = [
        color(bcolors.GREEN,"Part Name"),
        color(bcolors.GREEN,"Number of item"),
        color(bcolors.GREEN,"Part size"),
        color(bcolors.GREEN,"Total size")]
        rows = []
        rows.append(head)
        total = 0
        for element in res:
            total += element['total']
            rows.append(element['values'])
        total = ['','', '',color(bcolors.RED,"%d" % total)]
        rows.append(total)
        table.add_rows(rows)
        print(table.draw() + "\n")


import sys

sys.path.append("./scalecodec")
sys.path.append("./substrateinterface")

from substrateinterface import SubstrateInterface
from scalecodec.type_registry import load_type_registry_file

FILE = 'network_fees.csv'

substrate = SubstrateInterface('wss://mof2.sora.org', ss58_format=69,
                               type_registry=load_type_registry_file('custom_types.json'), type_registry_preset='default')

def get_event_param(event, param_idx):
    return event.value['params'][param_idx]['value']

def parse_block(block_num):
    result = []
    block_hash = substrate.get_block_hash(block_num)
    events = substrate.get_events(block_hash)

    for idx, e in enumerate(events):
        module = e.value['module_id']
        event = e.value['event_id']
        if module == 'XorFee' and event == 'FeeWithdrawn':
            extrinsic_id = e.value['extrinsic_idx']
            xor_total_fee = get_event_param(e, 1)
            xor_referrer_estimated = int(xor_total_fee * 0.1) # no events with this info, only estimation
            xor_burned_estimated = int(xor_total_fee * 0.4) # no events with this info, only estimation
            xor_dedicated_for_buy_back = 0
            val_burned = 0
            val_reminted_parliament = 0
            # there are free tx's, thus handled via check
            if xor_total_fee != 0:
                # 50% xor is exchanged to val
                xor_dedicated_for_buy_back = get_event_param(events[idx + 2], 2)
                if len(events) > idx + 9:
                    # exchanged val burned
                    event_with_val_burned = events[idx + 9]
                    # 10% burned val is reminted to parliament
                    event_with_val_reminted_parliament = events[idx + 10]
                    if event_with_val_burned.value['extrinsic_idx'] == extrinsic_id and event_with_val_reminted_parliament.value['extrinsic_idx'] == extrinsic_id:
                        if event_with_val_burned.value['event_id'] == 'Withdrawn' and event_with_val_reminted_parliament.value['event_id'] == 'Deposited':
                            # if this branch is not executed then exchange has failed, reserved 50% xor is burned
                            val_burned = get_event_param(event_with_val_burned, 2)
                            val_reminted_parliament = get_event_param(event_with_val_reminted_parliament, 2)
            result.append((xor_total_fee, xor_referrer_estimated, xor_burned_estimated, xor_dedicated_for_buy_back, val_burned, val_reminted_parliament))
    return result

def collect_network_fee_events():
    head_num = substrate.get_block_header()['header']['number']

    # NOTE: each line is individual extrinsic, therefore blocks numbers are repeated
    # NOTE: restarting script wipes existing data
    print("Writing to '%s'" % FILE)
    with open(FILE, 'w') as f:
        f.write('block_num, xor_total_fee, xor_referrer_estimated, xor_burned_estimated, xor_dedicated_for_buy_back, val_burned, val_reminted_parliament\n')
        for block_num in range(0, head_num):
            for elem in parse_block(block_num):
                print(str(block_num) + ', ' + ', '.join(str(x) for x in elem) + '\n')
                f.write(str(block_num) + ', ' + ', '.join(str(x) for x in elem) + '\n')
            if block_num % 100 == 0:
                print('Done: %d/%d\r' % (block_num, head_num), end='')
    print("\nDONE")

if __name__ == "__main__":
    collect_network_fee_events()


from django.shortcuts import get_object_or_404, render
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.template import loader
from .models import Student

from .forms import StudentForm

# Create your views here.

def index(request):

    #template = loader.get_template('alumni/index.html')
    #student = get_object_or_404(Student)
    if request.method == 'POST':
        form = StudentForm(request.POST)

        if form.is_valid():
            #form.save()
            post = form.save(commit=False)
            post.save()
            #return HttpResponseRedirect(reverse('alumni:student', args=(post.id)))
            #return HttpResponseRedirect('alumni/thankyou.html') # need to map in url
            return render(request, 'alumni/thankyou.html')      # just goes to thank you page

    else:
        form = StudentForm()
    return render(request, 'alumni/index.html', {'form': form })

def student(request, student_id):
    template = loader.get_template('alumni/student.html')
    student = Student.objects.get(id=student_id)
    context = {
        'student': student,
    }
    return HttpResponse(template.render(context, request))

def students(request):
    template = loader.get_template('alumni/students.html')
    student_list = Student.objects.all()
    context = {
        'students': student_list,
    }

    return HttpResponse(template.render(context, request))


import pygame
import sprite_class as sc
import before_game
import game_function as gf

WHITE = (255,255,255)

def main():
    pygame.init()
    screen = pygame.display.set_mode((600,450))
    pygame.display.set_caption('Ninja vs Zombie')
    clock = pygame.time.Clock()

    ninja = sc.Ninja(0,423)
    bg = before_game.BeforeGame()
    bg.create_sprite_list(ninja)

    spr_star_list = pygame.sprite.Group()
    star_ticker = 0
    done = False
    while not done:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                done = True
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    if ninja.gravity_flag[0] == 0 and  ninja.gravity_flag[1] == 0:
                        ninja.jump()
                if event.key == pygame.K_RIGHT:
                    ninja.speed[0] = 2
                elif event.key == pygame.K_LEFT:
                    ninja.speed[0] = -2
                elif event.key == pygame.K_a:
                    if star_ticker == 0:
                        spr_star = sc.SprStar(ninja.rect.midright[0],ninja.rect.midright[1] - 4)
                        spr_star.speed[0] = 5
                        spr_star_list.add(spr_star)
                        bg.all_drawed_list.add(spr_star)
                        star_ticker = 20
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_LEFT:
                    ninja.speed[0] = 0
                if event.key == pygame.K_RIGHT:
                    ninja.speed[0] = 0

        screen.fill(WHITE)

        if star_ticker > 0:
            star_ticker -= 1

        gf.zombie_ai(bg.zombie_list)

        for star in spr_star_list:
           star.move(star.speed[0],star.speed[1])
           if pygame.sprite.spritecollide(star,bg.all_wall_list,False):
               star.kill()
               continue
           if pygame.sprite.spritecollide(star,bg.zombie_list,False):
               for zombie in \
               pygame.sprite.spritecollide(star,bg.zombie_list,False):
                   smoke = sc.Smoke(zombie.rect.x,zombie.rect.y)
                   bg.all_drawed_list.add(smoke)
                   bg.all_smoke_list.add(smoke)
                   bg.move_wall_list.add(smoke)
                   zombie.kill()
               star.kill()

        for smoke in bg.all_smoke_list:
            smoke.suicide()

        gf.check_gravity(ninja,bg.all_wall_list)
        gf.gravity(ninja)
        gf.sprite_pos_fix(ninja,bg.all_wall_list)
        gf.back_groud_move(ninja,bg.move_wall_list)

        if not bg.move_wall_list:
            pass
        elif ninja.rect.x > 366:
            ninja.rect.x = 366

        for drawed in bg.all_drawed_list:
            screen.blit(drawed.image,[drawed.rect.x,drawed.rect.y])

        pygame.display.flip()
        clock.tick(60)

    pygame.quit()

if __name__ == '__main__':
    main()


from init_logger import init_logger

logger = init_logger(__name__, testing_mode=False)

def binary_sum(S, start, stop):
    '''
    Return the sum of the numbers in implicit slice S[sart:stop]
    '''
    if start >= stop:       # zero elments in slice
        return 0
    elif start == stop -1: # one element in slice
        logger.info(S[start])
        return S[start]
    else:                   # two or more elements in slice
        mid = (start + stop) // 2
        logger.info('mid %s ', mid)
        return binary_sum(S, start, mid) + binary_sum(S, mid, stop)

if __name__ == '__main__':
    S = list(range(1, 9))
    logger.info(binary_sum(S, 0, (len(S))))


#!/usr/bin/env python
"""
Modelica AST definitions
"""
from __future__ import print_function, absolute_import, division, print_function, unicode_literals

import copy
import json
import sys
from collections import OrderedDict

VALIDATE_AST = True

nan = float('nan')

"""
AST Node Type Hierarchy

Root Class
    Class
        Equation
            ComponentRef
            Expression
            Primary
        IfEquation
            Expression
            Equation
        ForEquation
            Expression
            Equation
        ConnectClause
            ComponentRef
        Symbol
"""

def to_json(var):
    if isinstance(var, list):
        res = [to_json(item) for item in var]
    elif isinstance(var, dict):
        res = {key: to_json(var[key]) for key in var.keys()}
    elif isinstance(var, Node):
        res = {key: to_json(var.__dict__[key]) for key in var.__dict__.keys()}
    else:
        res = var
    return res

class Field(object):
    def __init__(self, types, default=None):
        if type(types) is type or not hasattr(types, '__iter__'):
            types = [types]
        types = list(types)
        if sys.version_info < (3,):
            if str in types:
                types += [unicode]
        self.types = types
        self.default = default

    def validate(self, name, key, val, throw=True):
        if not type(val) in self.types:
            if throw:
                raise IOError('{:s}.{:s} requires types ({:s}), but got {:s}'.format(
                    name, key, ','.join([t.__name__ for t in self.types]), type(val).__name__))
            else:
                return False
        else:
            return True

class FieldList(list):
    def __init__(self, types, default=[]):
        super(FieldList, self).__init__()
        if type(types) is type or not hasattr(types, '__iter__'):
            types = [types]
        types = list(types)
        if sys.version_info < (3,):
            if str in types:
                types += [unicode]
        self.types = types
        self.default = default

    def validate(self, name, key, val_list, throw=True):
        if type(val_list) != list:
            if throw:
                raise IOError('{:s}.{:s} requires types {:s}, but got {:s}'.format(
                name, key, 'list', type(val_list).__name__))
            else:
                return False

        for val in val_list:
            if not type(val) in self.types:
                if throw:
                    raise IOError('{:s}.{:s} requires list items of type ({:s}), but got {:s}'.format(
                        name, key, ','.join([t.__name__ for t in self.types]), type(val).__name__))
                else:
                    return False
            else:
                return True

    def __add__(self, other):
        if other in self.types:
            super(FieldList, self).__add__(other)
        else:
            raise IOError('List requires elements of type', self.types)

# TODO get dict validation working
class FieldDict(OrderedDict):
    def __init__(self, types, default):
        super(FieldDict, self).__init__()
        if type(types) is type or not hasattr(types, '__iter__'):
            types = [types]
        types = list(types)
        if sys.version_info < (3,):
            if str in types:
                types += [unicode]
        self.types = types
        self.default = default

    def validate(self, name, key, val_dict, throw=True):
        if type(val_dict) != dict:
            if throw:
                raise IOError('{:s}.{:s} requires types ({:s}), but got {:s}'.format(
                name, key, 'dict', type(val).__name__))
            else:
                return False

        for val in val_dict.values():
            if not type(val) in self.types:
                if throw:
                    raise IOError('{:s}.{:s} requires list items of type ({:s}), but got {:s}'.format(
                        name, key, ','.join([t.__name__ for t in self.types]), type(val).__name__))
                else:
                    return False
            else:
                return True

    def __setitem__(self, key, value):
        if VALIDATE_AST:
            if not type(value) in self.types:
                raise IOError('{:s} requires dict values of type ({:s}), but got {:s}'.format(
                    key, ','.join([t.__name__ for t in self.types]), type(value).__name__))
        super(FieldDict, self).__setitem__(key, value)

class Node(object):
    ast_spec = {}

    def __init__(self, **kwargs):
        for key in self.ast_spec.keys():
            # make sure we don't share ast_spec default data by using deep copy
            field_type = type(self.ast_spec[key])
            default = self.ast_spec[key].default
            self.__dict__[key] = copy.deepcopy(default)
        for key in kwargs.keys():
            # types = self.ast_spec[key].types
            val = kwargs[key]
            self.set_field(key, val)

    def set_field(self, key, value):
        if VALIDATE_AST:
            name = self.__class__.__name__
            if key not in self.ast_spec.keys():
                raise IOError('{:s} not a child of {:s}'.format(key, name))
            self.ast_spec[key].validate(name, key, value)
        self.__dict__[key] = value

    def __setattr__(self, key, value):
        self.set_field(key, value)

    def __repr__(self):
        return json.dumps(to_json(self), indent=2, sort_keys=True)

    __str__ = __repr__

class Primary(Node):
    def __init__(self, **kwargs):
        super(Primary, self).__init__(**kwargs)

class Array(Node):
    def __init__(self, **kwargs):
        super(Array, self).__init__(**kwargs)

class Slice(Node):
    def __init__(self, **kwargs):
        super(Slice, self).__init__(**kwargs)

class ComponentRef(Node):
    def __init__(self, **kwargs):
        super(ComponentRef, self).__init__(**kwargs)

class Expression(Node):
    def __init__(self, **kwargs):
        super(Expression, self).__init__(**kwargs)

class IfExpression(Node):
    def __init__(self, **kwargs):
        super(IfExpression, self).__init__(**kwargs)

class Equation(Node):
    def __init__(self, **kwargs):
        super(Equation, self).__init__(**kwargs)

class IfEquation(Node):
    def __init__(self, **kwargs):
        super(IfEquation, self).__init__(**kwargs)

class ForIndex(Node):
    def __init__(self, **kwargs):
        super(ForIndex, self).__init__(**kwargs)

class ForEquation(Node):
    def __init__(self, **kwargs):
        super(ForEquation, self).__init__(**kwargs)

class ConnectClause(Node):
    def __init__(self, **kwargs):
        super(ConnectClause, self).__init__(**kwargs)

class AssignmentStatement(Node):
    def __init__(self, **kwargs):
        super(AssignmentStatement, self).__init__(**kwargs)

class IfStatement(Node):
    def __init__(self, **kwargs):
        super(IfStatement, self).__init__(**kwargs)

class ForStatement(Node):
    def __init__(self, **kwargs):
        super(ForStatement, self).__init__(**kwargs)

class Symbol(Node):
    def __init__(self, **kwargs):
        super(Symbol, self).__init__(**kwargs)

    ATTRIBUTES = ['min', 'max', 'start', 'fixed', 'nominal']

class ComponentClause(Node):
    def __init__(self, **kwargs):
        super(ComponentClause, self).__init__(**kwargs)

class EquationSection(Node):
    def __init__(self, **kwargs):
        super(EquationSection, self).__init__(**kwargs)

class AlgorithmSection(Node):
    def __init__(self, **kwargs):
        super(AlgorithmSection, self).__init__(**kwargs)

class ImportAsClause(Node):
    def __init__(self, **kwargs):
        super(ImportAsClause, self).__init__(**kwargs)

class ImportFromClause(Node):
    def __init__(self, **kwargs):
        super(ImportFromClause, self).__init__(**kwargs)

class ElementModification(Node):
    def __init__(self, **kwargs):
        super(ElementModification, self).__init__(**kwargs)

class ShortClassDefinition(Node):
    def __init__(self, **kwargs):
        super(ShortClassDefinition, self).__init__(**kwargs)

class ElementReplaceable(Node):
    def __init__(self, **kwargs):
        super(ElementReplaceable, self).__init__(**kwargs)

class ClassModification(Node):
    def __init__(self, **kwargs):
        super(ClassModification, self).__init__(**kwargs)

class ExtendsClause(Node):
    def __init__(self, **kwargs):
        super(ExtendsClause, self).__init__(**kwargs)

class Class(Node):
    def __init__(self, **kwargs):
        super(Class, self).__init__(**kwargs)

class File(Node):
    def __init__(self, **kwargs):
        super(File, self).__init__(**kwargs)

    def find_class(self, component_ref):
        for c in self.classes:
            if not component_ref.child:
                return self.classes[component_ref.name]

        return self.classes[component_ref.name]

    def find_symbol(self, c, component_ref):
        sym = c.symbols[component_ref.name]
        if len(component_ref.child) > 0:
            c = self.find_class(sym.type)
            return self.find_symbol(c, component_ref.child[0])
        else:
            return sym

class Collection(Node):
    def __init__(self, **kwargs):
        super(Collection, self).__init__(**kwargs)

    def extend(self, other):
        self.files.extend(other.files)

    def find_class(self, component_ref, within=[]):
        if isinstance(component_ref, str):
            assert component_ref.find('.') == -1
            component_ref = ComponentRef(name=component_ref)

        # First we try to the find the file matching the right 'within'
        if not component_ref.child and not within:
            for f in self.files:
                if not f.within:
                    try:
                        return f.classes[component_ref.name]
                    except KeyError:
                        continue

            # Could not find symbol. Assume it is an elementary type
            # TODO: Is this a correct assumption? What if we have an undefined type that is not elementary?
            raise KeyError
        else:
            # TODO: Should we move this to a 'get_parent' method in the ComponentRef class
            c_within = copy.deepcopy(component_ref)

            n = c_within
            while n.child[0].child:
                n = n.child[0]
            class_name = n.child[0].name
            n.child = []

            # Merge the within passed in, and the within we split from the
            # class to be looked up
            extended_within = copy.deepcopy(within)

            if extended_within:
                n = extended_within[0]
                while n.child:
                    n = n.child[0]
                n.child.append(c_within)
                extended_within = extended_within[0]
            else:
                extended_within = c_within

            c = next((f.classes[class_name] for f in self.files if f.within and repr(f.within[0]) == repr(extended_within) and class_name in f.classes), None)

            # TODO: This could probably be cleaner if we do nested classes.
            # Then we could traverse up the tree until we found a match,
            # instead of just trying twice (once with, once without prepending
            # the passed-in 'within').
            if c is None:
                # Try again with root node lookup instead of relative
                # NOTE: We are using repr() to compare, because implementing
                # __eq__ on the ComponentRef class will make it unhashable.
                c = next((f.classes[class_name] for f in self.files if f.within and repr(f.within[0]) == repr(c_within) and class_name in f.classes), None)
                if c is None:
                    # TODO: How long do we traverse? Do we somehow force a stop at Real, Boolean, etc?
                    #       Now a force is stopped on anything in the Modelica library.
                    if c_within.name == "Modelica":
                        raise KeyError
                    else:
                        raise Exception("Could not find class {} in {}".format(class_name, c_within))
                else:
                    return c
            else:
                return c

    def find_symbol(self, c, component_ref):
        sym = c.symbols[component_ref.name]
        if len(component_ref.child) > 0:
            c = self.find_class(sym.type)
            return self.find_symbol(c, component_ref.child[0])
        else:
            return sym

VISIBILITY_PRIVATE = 0
VISIBILITY_PROTECTED = 1
VISIBILITY_PUBLIC = 2

# Here we define the AST specifications for all nodes
# these are static variables shared between class instances
# and are defined here to allow a class to list itself in
# the allowed field types, this self referencing is not
# possible when initially declaring a class in python

Primary.ast_spec = {
    'value' : Field([bool, float, int, str]),
}

Array.ast_spec = {
    'values' : FieldList([Expression, Primary, ComponentRef, Array]),
}

Slice.ast_spec = {
    'start' : Field([Expression, Primary, ComponentRef], Primary(value=0)),
    'stop' : Field([Expression, Primary, ComponentRef], Primary(value=-1)),
    'step' : Field([Expression, Primary, ComponentRef], Primary(value=1)),
}

ComponentRef.ast_spec = {
    'name' : Field([str]),
    'indices' : FieldList([Expression, Slice, Primary, ComponentRef], []),
    'child' : FieldList([ComponentRef], []),
}

Expression.ast_spec = {
    'operator' : Field([str, ComponentRef]),
    'operands' : FieldList([Expression, Primary, ComponentRef, Array, IfExpression]),
}

IfExpression.ast_spec = {
    'conditions' : FieldList([Expression, Primary, ComponentRef, Array, IfExpression]),
    'expressions' : FieldList([Expression, Primary, ComponentRef, Array, IfExpression]),
}

Equation.ast_spec = {
    'left' : Field([Expression, Primary, ComponentRef]),
    'right' : Field([Expression, Primary, ComponentRef]),
    'comment' : Field([str]),
}

IfEquation.ast_spec = {
    'conditions' : FieldList([Expression, Primary, ComponentRef]),
    'equations' : FieldList([Equation, ForEquation, ConnectClause], []),
    'comment' : Field([str]),
}

ForIndex.ast_spec = {
    'name' : Field([str]),
    'expression' : Field([Expression, Primary, Slice]),
}

ForEquation.ast_spec = {
    'indices' : FieldList([ForIndex]),
    'equations' : FieldList([Equation, ForEquation, ConnectClause], []),
    'comment' : Field([str]),
}

ConnectClause.ast_spec = {
    'left' : Field([ComponentRef]),
    'right' : Field([ComponentRef]),
    'comment' : Field([str]),
}

AssignmentStatement.ast_spec = {
    'left' : FieldList([ComponentRef]),
    'right' : Field([Expression, Primary, ComponentRef]),
    'comment' : Field([str]),
}

IfStatement.ast_spec = {
    'expressions' : FieldList([Expression, Primary, ComponentRef]),
    'statements' : FieldList([AssignmentStatement, ForStatement], []),
    'comment' : Field([str]),
}

ForStatement.ast_spec = {
    'indices' : FieldList([ForIndex]),
    'statements' : FieldList([AssignmentStatement, ForStatement], []),
    'comment' : Field([str]),
}

Symbol.ast_spec = {
    'name' : Field([str], ''),
    'type' : Field([ComponentRef], ComponentRef()),
    'prefixes' : FieldList([str], []),
    'redeclare' : Field([bool], False),
    'final' : Field([bool], False),
    'inner' : Field([bool], False),
    'outer' : Field([bool], False),
    'dimensions' : FieldList([Expression, Primary, ComponentRef], [Primary(value=1)]),
    'comment' : Field([str], ''),
    'start' : Field([Expression, Primary, ComponentRef, Array], Primary(value=nan)),
    'min' : Field([Expression, Primary, ComponentRef, Array], Primary(value=nan)),
    'max' : Field([Expression, Primary, ComponentRef, Array], Primary(value=nan)),
    'nominal' : Field([Expression, Primary, ComponentRef, Array], Primary(value=nan)),
    'value' : Field([Expression, Primary, ComponentRef, Array], Primary(value=nan)),
    'fixed' : Field([Primary], False),
    'id' : Field([int], 0),
    'order' : Field([int], 0),
    'visibility' : Field(int, VISIBILITY_PRIVATE),
    'class_modification' : Field(ClassModification),
}

ComponentClause.ast_spec = {
    'prefixes' : FieldList([str], []),
    'type' : Field([ComponentRef], ComponentRef()),
    'dimensions' : FieldList([Expression, Primary, ComponentRef], [Primary(value=1)]),
    'comment' : FieldList([str], []),
    'symbol_list' : FieldList([Symbol], []),
}

EquationSection.ast_spec = {
    'initial' : Field([bool], False),
    'equations' : FieldList([Equation, ForEquation, ConnectClause], []),
}

AlgorithmSection.ast_spec = {
    'initial' : Field([bool], False),
    'statements' : FieldList([AssignmentStatement, ForStatement], []),
}

ImportAsClause.ast_spec = {
    'component' : Field([ComponentRef]),
    'name' : Field([str]),
}

ImportFromClause.ast_spec = {
    'component' : Field([ComponentRef]),
    'symbols' : FieldList([str]),
}

# TODO: Check if ComponentRef modifiers are handled correctly. For example,
# check HomotopicLinear which extends PartialHomotopic with the modifier
# "H(min = H_b)".
ElementModification.ast_spec = {
    'component': Field([ComponentRef], [ComponentRef()]),
    'modifications' : FieldList([Primary, Expression, ClassModification, Array, ComponentRef], []),
}

ShortClassDefinition.ast_spec = {
    'name' : Field(str),
    'type' : Field(str, ''),
    'component' : Field(ComponentRef),
    'class_modification' : Field([ClassModification]),
}

ClassModification.ast_spec = {
    'arguments' : FieldList([ElementModification, ComponentClause, ShortClassDefinition], []),
}

ExtendsClause.ast_spec = {
    'component' : Field([ComponentRef]),
    'class_modification' : Field([ClassModification]),
    'visibility': Field(int, VISIBILITY_PRIVATE),
}

Class.ast_spec = {
    'name' : Field(str),
    'imports' : FieldList([ImportAsClause, ImportFromClause], []),
    'extends' : FieldList([ExtendsClause], []),
    'encapsulated' : Field([bool], False),
    'partial' : Field([bool], False),
    'final' : Field([bool], False),
    'type' : Field([str], ''),
    'comment' : Field(str, ''),
    'symbols' : FieldDict([Symbol], {}),
    'initial_equations' : FieldList([Equation, ForEquation], []),
    'equations' : FieldList([Equation, ForEquation, ConnectClause], []),
    'initial_statements' : FieldList([AssignmentStatement, ForStatement], []),
    'statements' : FieldList([AssignmentStatement, ForStatement], []),
    'within' : FieldList([ComponentRef], []),
}

File.ast_spec = {
    'within' : FieldList([ComponentRef], []),
    'classes' : FieldDict([Class], {}),
}

# TODO: The Modelica specification (v3.3, Ch. 4) seems to suggest that
# everything can/should be stored in one single (root) class, that can contain
# other classes (each in turn also storing classes). That would make both File
# and Collection types obsolete, but could make processing certain files (e.g.
# Package.mo) before others important. For example, a 'within' statement would
# only make sense if that package actually already exists in our tree of
# classes, otherwise we would have nowhere to add the classes in that file.
Collection.ast_spec = {
    'files': FieldList([File], []),
}


import tensorflow.keras
import numpy as np
import cv2

#레이블 가져오기
labels=[]
f=open("labels.txt", "r")
for x in f:
    labels.append(x.rstrip('\n'))
    label_count = len(labels)
f.close()

# e-04와 같은 scientific notation을 제거하고 싶을 때 사용하는 옵션
np.set_printoptions(suppress=True)

# Teachable Machine에서 학습시킨 모델 파일을 모델 파일을 컴파일을 다시 하지 않고, model 변수에 넣음
model = tensorflow.keras.models.load_model('keras_model.h5', compile=False)

# numpy를 이용해 이미지를 1차원, 높이 224pixel, 폭 224pixel, 색상 3채널(RGB)로 변환해서 data 변수에 넣음, 형식은 float32, 여기서는 data 변수를 만드는 의미로 쓰임
data = np.ndarray(shape=(1, 224, 224, 3), dtype=np.float32)

# OpenCV를 이용해 캠으로 들어오는 영상을 cap 변수에 넣음, '0'은 컴퓨터가 인식한 첫번째 카메라를 의미함
cap = cv2.VideoCapture(cv2.CAP_DSHOW+1)

print('Press "q", if you want to quit')

# while문 안의 내용을 계속 반복시킴. 캠에서 영상 프레임이 들어올 때마다 아래 프로그램을 실행함
while(True):

    ret, frame = cap.read()

    if ret:
        flip_frame = cv2.flip(frame, 1)

    h = flip_frame.shape[0]
    w = flip_frame.shape[1]

    crop_image = flip_frame[0:h, int((w-h)/2):int(w-((w-h)/2))]

    # 바이큐빅보간법(cv2.INTER_CUBIC, 이미지를 확대할 때 주로 사용)을 이용해 frame변수에 들어온 비디오 프레임의 사이즈를 224, 224로 다운사이징하여 image 변수에 넣음
    image = cv2.resize(crop_image, dsize=(224, 224), interpolation=cv2.INTER_CUBIC)

    # asarray메소드를 이용해 image에 들어있는 크기가 변형된 이미지를 numpy가 처리할 수 있는 배열로 만들어서 image_array 변수에 넣음
    image_array = np.asarray(image)

    # image_array에 들어있는 image의 변형된 배열을 정규화(normalized)하기 위해 수식을 적용함
    normalized_image_array = (image_array.astype(np.float32) / 127.0) - 1

    # 정규화된 배열을 data[0]에 넣음
    data[0] = normalized_image_array

    # 정규화된 배열값으로 정돈된 data를 Teachable Machine으로 학습시켜서 얻은 모델을 이용해 추론하고, 그 결과를 prediction 변수에 넣음
    prediction = model.predict(data)

    # 추론결과를 콘솔에 보여주기
    # print(prediction)

    # 글씨 넣기 준비
    font = cv2.FONT_HERSHEY_TRIPLEX
    fontScale = 1
    fontColor = (0,255,0)
    lineThickness = 1

    # 표기 문구 초기화
    scoreLabel = 0
    score = 0
    result = ''

    for x in range(0, label_count):
        #예측값 모니터링
        line=('%s=%0.0f' % (labels[x], int(round(prediction[0][x]*100)))) + "%"
        cv2.putText(crop_image, line, (10,(x+1)*35), font, fontScale, fontColor, lineThickness)

        # 가장 높은 예측 찾기
        if score < prediction[0][x]:
            scoreLabel = labels[x]
            score = prediction[0][x]
            result = str(scoreLabel) + " : " + str(score)
            print(result)

    # 최고 결과치 보여주기
    crop_image = cv2.putText(crop_image, result, (10, int(label_count+1)*35), font, 1, (0, 0, 255), 1, cv2.LINE_AA)

    # prediction의 첫번째 리스트값이 0.7을 넘으면 'a'를 utf-8 형태로 인코딩하여 시리얼 통신으로 송신함
    # 학습을 시킬 때는 반드시 디폴트 이미지를 학습시키는 것이 오류를 예방할 수 있는 지름길임
    if prediction[:, 0] > 0.7 :
    # 'a'를 utf-8 형식으로 인코딩 하여 send 변수에 넣음
        send = (str('a')+'\n').encode("utf-8")
    # send 변수에 들어있는 값을 시리얼통신으로 송신함
        #ser.write(send)
    # 송신이 되면 화면에 send 변수에 들어가 있는 값을 출력함
        print(send)

    # prediction의 두번째 리스트값이 0.7을 넘으면 'b'를 utf-8 형태로 인코딩하여 시리얼 통신으로 송신함
    if prediction[:, 1] > 0.7 :
        send = (str('b')+'\n').encode("utf-8")
        #ser.write(send)
        print(send)

    # prediction의 세번째 리스트값이 0.7을 넘으면 'c'를 utf-8 형태로 인코딩하여 시리얼 통신으로 송신함
    if prediction[:, 2] > 0.7 :
        send = (str('c')+'\n').encode("utf-8")
        #ser.write(send)
        print(send)

    # 줄바꿈
    print()

    # 원본이미지, 플립이미지, 크롭이미지, 추론용(축소)이미지 화면에 보여주기
    # cv2.imshow('frame',frame)
    # cv2.imshow('flip_frame',flip_frame)
    cv2.imshow('crop_image',crop_image)
    # cv2.imshow('image',image)

    # 키 입력을 기다림
    key = cv2.waitKey(1) & 0xFF

    # q 키를 눌렀다면 반복실행에서 종료함
    if key == ord("q"):
        print('Quit')
        break

    # 동작이 종료되면 비디오 프레임 캡쳐를 중단함
    cap.release()
    # 모든 창을 닫음
    cv2.destroyAllWindows()