#!/usr/bin/env python
"""
Modelica parse Tree to AST tree.
"""
from __future__ import print_function, absolute_import, division, unicode_literals

import os
import glob
import unittest
import itertools

import casadi as ca
import numpy as np

import pymoca.backends.casadi.generator as gen_casadi
from pymoca.backends.casadi.alias_relation import AliasRelation
from pymoca.backends.casadi.model import CASADI_ATTRIBUTES, Model, Variable, DelayArgument
from pymoca.backends.casadi.api import transfer_model, CachedModel
from pymoca import parser, ast

MODEL_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'models')

# noinspection PyPep8Naming,PyUnresolvedReferences
class GenCasadiTest(unittest.TestCase):
    def assert_model_equivalent(self, A, B):
        def sstr(a): return set([str(e) for e in a])

        for l in ["states", "der_states", "inputs", "outputs", "constants", "parameters"]:
            self.assertEqual(sstr(getattr(A, l)), sstr(getattr(B, l)))

    def assert_model_equivalent_numeric(self, A, B, tol=1e-9):
        self.assertEqual(len(A.states), len(B.states))
        self.assertEqual(len(A.der_states), len(B.der_states))
        self.assertEqual(len(A.alg_states), len(B.alg_states))
        self.assertEqual(len(A.inputs), len(B.inputs))
        self.assertEqual(len(A.outputs), len(B.outputs))
        self.assertEqual(len(A.constants), len(B.constants))
        self.assertEqual(len(A.parameters), len(B.parameters))
        self.assertEqual(len(A.delay_states), len(B.delay_states))

        if not isinstance(A, CachedModel) and not isinstance(B, CachedModel):
            self.assertEqual(len(A.equations), len(B.equations))
            self.assertEqual(len(A.initial_equations), len(B.initial_equations))
            self.assertEqual(len(A.delay_arguments), len(B.delay_arguments))

        for f_name in ['dae_residual', 'initial_residual', 'variable_metadata', 'delay_arguments']:
            this = getattr(A, f_name + '_function')
            that = getattr(B, f_name + '_function')

            np.random.seed(0)

            args_in = []
            for i in range(this.n_in()):
                sp = this.sparsity_in(i)
                r = ca.DM(sp, np.random.random(sp.nnz()))
                args_in.append(r)

            this_out = this.call(args_in)
            that_out = that.call(args_in)

            # N.B. Here we require that the order of the equations in the two models is identical.
            for i, (a, b) in enumerate(zip(this_out, that_out)):
                for j in range(a.size1()):
                    for k in range(a.size2()):
                        if a[j, k].is_regular() or b[j, k].is_regular():
                            test = float(ca.norm_2(ca.vec(a[j, k] - b[j, k]))) <= tol
                            if not test:
                                print(j)
                                print(k)
                                print(a[j,k])
                                print(b[j,k])
                                print(f_name)
                            self.assertTrue(test)

        return True

    def test_spring(self):
        with open(os.path.join(MODEL_DIR, 'Spring.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'Spring')
        ref_model = Model()
        print(casadi_model)
        x = ca.MX.sym("x")
        v_x = ca.MX.sym("v_x")
        der_x = ca.MX.sym("der(x)")
        der_v_x = ca.MX.sym("der(v_x)")
        k = ca.MX.sym("k")
        c = ca.MX.sym("c")
        ref_model.states = list(map(Variable, [x, v_x]))
        ref_model.der_states = list(map(Variable, [der_x, der_v_x]))
        ref_model.parameters = list(map(Variable, [c, k]))
        ref_model.parameters[0].value = 0.1
        ref_model.parameters[1].value = 2
        ref_model.equations = [der_x - v_x, der_v_x - (-k * x - c * v_x)]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_estimator(self):
        with open(os.path.join(MODEL_DIR, 'Estimator.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'Estimator')
        ref_model = Model()
        print(casadi_model)

        x = ca.MX.sym("x")
        der_x = ca.MX.sym("der(x)")
        y = ca.MX.sym("y")

        ref_model.states = list(map(Variable, [x]))
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [y]))
        ref_model.outputs = list(map(Variable, [y]))
        ref_model.equations = [der_x + x, y - x]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_aircraft(self):
        with open(os.path.join(MODEL_DIR, 'Aircraft.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        # noinspection PyUnusedLocal
        casadi_model = gen_casadi.generate(ast_tree, 'Aircraft')
        # noinspection PyUnusedLocal
        ref_model = Model()
        self.assertTrue(True)

    def test_connector_hq(self):
        with open(os.path.join(MODEL_DIR, 'ConnectorHQ.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'System')
        ref_model = Model()
        print(casadi_model)

        a__up__H = ca.MX.sym("a.up.H")
        a__up__Q = ca.MX.sym("a.up.Q")
        a__down__H = ca.MX.sym("a.down.H")
        a__down__Q = ca.MX.sym("a.down.Q")

        b__up__H = ca.MX.sym("b.up.H")
        b__up__Q = ca.MX.sym("b.up.Q")
        b__down__H = ca.MX.sym("b.down.H")
        b__down__Q = ca.MX.sym("b.down.Q")

        c__up__H = ca.MX.sym("c.up.H")
        c__up__Q = ca.MX.sym("c.up.Q")
        c__down__H = ca.MX.sym("c.down.H")
        c__down__Q = ca.MX.sym("c.down.Q")

        qa__down__H = ca.MX.sym("qa.down.H")
        qa__down__Q = ca.MX.sym("qa.down.Q")

        p__H = ca.MX.sym("p.H")
        p__Q = ca.MX.sym("p.Q")

        hb__up__H = ca.MX.sym("hb.up.H")
        hb__up__Q = ca.MX.sym("hb.up.Q")

        zerotest__H = ca.MX.sym("zerotest.H")
        zerotest__Q = ca.MX.sym("zerotest.Q")

        ref_model.alg_states = list(map(Variable, [
            a__up__H, a__down__H, b__up__H, b__down__H, c__up__H, c__down__H,
            qa__down__H, p__H, hb__up__H, zerotest__H,
            a__up__Q, a__down__Q, b__up__Q, b__down__Q, c__up__Q, c__down__Q,
            qa__down__Q, p__Q, hb__up__Q, zerotest__Q]))
        ref_model.equations = [a__up__H - a__down__H,
                               b__up__H - b__down__H,
                               c__up__H - c__down__H,
                               qa__down__Q,
                               hb__up__H,
                               p__Q,
                               qa__down__H - a__up__H,
                               p__H - c__up__H,
                               a__down__H - b__up__H,
                               c__down__H - b__up__H,
                               b__down__H - hb__up__H,
                               a__up__Q + a__down__Q,
                               b__up__Q + b__down__Q,
                               c__up__Q + c__down__Q,
                               qa__down__Q + a__up__Q,
                               -p__Q + c__up__Q,
                               a__down__Q + (b__up__Q + c__down__Q),
                               b__down__Q + hb__up__Q,
                               zerotest__Q]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_connector_hqz(self):
        with open(os.path.join(MODEL_DIR, 'ConnectorHQZ.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'SystemZ')
        ref_model = Model()
        print(casadi_model)

        a__up__H = ca.MX.sym("a.up.H")
        a__up__Q = ca.MX.sym("a.up.Q")
        a__up__Z = ca.MX.sym("a.up.Z")
        a__down__H = ca.MX.sym("a.down.H")
        a__down__Q = ca.MX.sym("a.down.Q")
        a__down__Z = ca.MX.sym("a.down.Z")

        b__up__H = ca.MX.sym("b.up.H")
        b__up__Q = ca.MX.sym("b.up.Q")
        b__up__Z = ca.MX.sym("b.up.Z")
        b__down__H = ca.MX.sym("b.down.H")
        b__down__Q = ca.MX.sym("b.down.Q")
        b__down__Z = ca.MX.sym("b.down.Z")

        c__up__H = ca.MX.sym("c.up.H")
        c__up__Q = ca.MX.sym("c.up.Q")
        c__up__Z = ca.MX.sym("c.up.Z")
        c__down__H = ca.MX.sym("c.down.H")
        c__down__Q = ca.MX.sym("c.down.Q")
        c__down__Z = ca.MX.sym("c.down.Z")

        d__up__H = ca.MX.sym("d.up.H")
        d__up__Q = ca.MX.sym("d.up.Q")
        d__down__H = ca.MX.sym("d.down.H")
        d__down__Q = ca.MX.sym("d.down.Q")
        d__down__Z = ca.MX.sym("d.down.Z")

        qa__down__H = ca.MX.sym("qa.down.H")
        qa__down__Q = ca.MX.sym("qa.down.Q")
        qa__down__Z = ca.MX.sym("qa.down.Z")
        qc__down__H = ca.MX.sym("qc.down.H")
        qc__down__Q = ca.MX.sym("qc.down.Q")
        qc__down__Z = ca.MX.sym("qc.down.Z")

        hb__up__H = ca.MX.sym("hb.up.H")
        hb__up__Q = ca.MX.sym("hb.up.Q")
        hb__up__Z = ca.MX.sym("hb.up.Z")

        ref_model.alg_states = list(map(Variable, [
            a__up__H, a__down__H, b__up__H, b__down__H,
            c__up__H, c__down__H, d__up__H, d__down__H,
            qa__down__H, qc__down__H, hb__up__H,
            a__up__Q, a__down__Q, b__up__Q, b__down__Q,
            c__up__Q, c__down__Q, d__up__Q, d__down__Q,
            qa__down__Q, qc__down__Q, hb__up__Q,
            a__up__Z, a__down__Z, b__up__Z, b__down__Z,
            c__up__Z, c__down__Z, d__down__Z,
            qa__down__Z, qc__down__Z, hb__up__Z]))

        ref_model.equations = [a__up__H - a__down__H,
                               a__up__Q + a__down__Q,
                               b__up__H - b__down__H,
                               b__up__Q + b__down__Q,
                               c__up__H - c__down__H,
                               c__up__Q + c__down__Q,
                               d__up__H - d__down__H,
                               d__up__Q + d__down__Q,
                               qa__down__Q,
                               qc__down__Q,
                               hb__up__H,
                               qa__down__H - a__up__H,
                               qa__down__Z - a__up__Z,
                               qc__down__H - c__up__H,
                               qc__down__Z - c__up__Z,
                               a__down__H - b__up__H,
                               a__down__Z - b__up__Z,
                               c__down__H - b__up__H,
                               c__down__Z - b__up__Z,
                               b__down__H - hb__up__H,
                               b__down__Z - hb__up__Z,
                               qa__down__Q + a__up__Q,
                               qc__down__Q + c__up__Q,
                               a__down__Q + (b__up__Q + c__down__Q),
                               b__down__Q + hb__up__Q,
                               d__up__Q,
                               d__down__Q]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_tree_lookup(self):
        with open(os.path.join(MODEL_DIR, 'TreeLookup.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)

        casadi_model = gen_casadi.generate(ast_tree, 'Level1.Level2.Level3.Test')
        ref_model = Model()
        print(casadi_model)

        elem__tc__i = ca.MX.sym("elem.tc.i")
        elem__tc__a = ca.MX.sym("elem.tc.a")
        b = ca.MX.sym("b")

        ref_model.alg_states = list(map(Variable, [elem__tc__i, elem__tc__a, b]))

        ref_model.equations = [elem__tc__i - 1,
                               elem__tc__a - b]

        print(ref_model)
        self.assert_model_equivalent(ref_model, casadi_model)

    def test_duplicate(self):
        with open(os.path.join(MODEL_DIR, 'DuplicateState.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'DuplicateState')
        print(casadi_model)
        ref_model = Model()

        x = ca.MX.sym("x")
        der_x = ca.MX.sym("der(x)")
        y = ca.MX.sym("y")
        der_y = ca.MX.sym("der(y)")

        ref_model.states = list(map(Variable, [x, y]))
        ref_model.der_states = list(map(Variable, [der_x, der_y]))
        ref_model.equations = [der_x + der_y - 1, der_x - 2]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_if_else(self):
        with open(os.path.join(MODEL_DIR, 'IfElse.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'IfElse')
        ref_model = Model()

        x = ca.MX.sym("x")
        y1 = ca.MX.sym("y1")
        y2 = ca.MX.sym("y2")
        y3 = ca.MX.sym("y3")
        y_max = ca.MX.sym("y_max")

        ref_model.inputs = list(map(Variable, [x]))
        ref_model.outputs = list(map(Variable, [y1, y2, y3]))
        ref_model.alg_states = list(map(Variable, [y1, y2, y3]))
        ref_model.parameters = list(map(Variable, [y_max]))
        ref_model.parameters[0].value = 10
        ref_model.equations = [
            y1 - ca.if_else(x > 0, 1, 0) * y_max,
            ca.if_else(x > 1, ca.vertcat(y2 - y_max, y3 - 100),
                       ca.if_else(x > 2, ca.vertcat(y2 - y_max - 1, y3 - 1000),
                                  ca.vertcat(y2, y3 - 10000)))]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_inheritance(self):
        with open(os.path.join(MODEL_DIR, 'Inheritance.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'Sub')
        ref_model = Model()

        x = ca.MX.sym("x")
        der_x = ca.MX.sym("der(x)")
        y = ca.MX.sym("y")
        # noinspection PyUnusedLocal
        der_y = ca.MX.sym("y")
        k = ca.MX.sym("k")

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].max = 30.0
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [y]))
        ref_model.parameters = list(map(Variable, [k]))
        ref_model.parameters[0].value = -1.0
        ref_model.equations = [der_x - k * x, x + y - 3]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_inheritance_instantiation(self):
        with open(os.path.join(MODEL_DIR, 'InheritanceInstantiation.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'C2')
        ref_model = Model()
        print(casadi_model)

        bcomp1_a = ca.MX.sym('bcomp1.a')
        bcomp1_b = ca.MX.sym('bcomp1.b')
        bcomp2_a = ca.MX.sym('bcomp2.a')
        bcomp2_b = ca.MX.sym('bcomp2.b')
        bcomp3_a = ca.MX.sym('bcomp3.a')
        bcomp3_b = ca.MX.sym('bcomp3.b')

        bcomp1_v = ca.MX.sym('bcomp1.v', 3)
        bcomp2_v = ca.MX.sym('bcomp2.v', 4)
        bcomp3_v = ca.MX.sym('bcomp3.v', 2)

        ref_model.states = []
        ref_model.der_states = []
        ref_model.alg_states = list(map(Variable, [bcomp1_v, bcomp2_v, bcomp3_v]))
        ref_model.parameters = list(map(Variable, [bcomp1_a, bcomp2_a, bcomp3_a, bcomp1_b, bcomp2_b, bcomp3_b]))
        ref_model.parameters[0].value = 0
        ref_model.parameters[1].value = 0
        ref_model.parameters[2].value = 1
        ref_model.parameters[3].value = 3
        ref_model.parameters[4].value = 4
        ref_model.parameters[5].value = 2
        ref_model.equations = []

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_nested_classes(self):
        with open(os.path.join(MODEL_DIR, 'NestedClasses.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'C2')
        ref_model = Model()
        print(casadi_model)

        v1 = ca.MX.sym('v1')
        v2 = ca.MX.sym('v2')

        ref_model.states = []
        ref_model.der_states = []
        ref_model.alg_states = list(map(Variable, [v1, v2]))
        ref_model.equations = []
        ref_model.alg_states[0].nominal = 1000.0
        ref_model.alg_states[1].nominal = 1000.0

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_builtin(self):
        with open(os.path.join(MODEL_DIR, 'BuiltinFunctions.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'BuiltinFunctions')
        print("BuiltinFunctions", casadi_model)
        ref_model = Model()

        x = ca.MX.sym("x")
        y = ca.MX.sym("y")
        z = ca.MX.sym("z")
        w = ca.MX.sym("w")
        u = ca.MX.sym("u")

        ref_model.inputs = list(map(Variable, [x]))
        ref_model.outputs = list(map(Variable, [y, z, w, u]))
        ref_model.alg_states = list(map(Variable, [y, z, w, u]))
        ref_model.equations = [y - ca.sin(ref_model.time), z - ca.cos(x), w - ca.fmin(y, z), u - ca.fabs(w)]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_function_call(self):
        with open(os.path.join(MODEL_DIR, 'FunctionCall.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'FunctionCall')
        print("FunctionCall", casadi_model)
        ref_model = Model()

        radius = ca.MX.sym('radius')
        diameter = radius * 2
        circle_properties = ca.Function('circle_properties', [radius], [3.14159*diameter, 3.14159*radius**2, ca.if_else(3.14159*radius**2 > 10, 1, 2), ca.if_else(3.14159*radius**2 > 10, 10, 3.14159*radius**2), 8, 3, 12])

        c = ca.MX.sym("c")
        a = ca.MX.sym("a")
        d = ca.MX.sym("d")
        e = ca.MX.sym("e")
        S1 = ca.MX.sym("S1")
        S2 = ca.MX.sym("S2")
        r = ca.MX.sym("r")
        ref_model.alg_states = list(map(Variable, [r, c, a, d, e, S1, S2]))
        ref_model.outputs = list(map(Variable, [c, a, d, e, S1, S2]))
        ref_model.equations = [ca.vertcat(c, a, d, e, S1, S2) - ca.vertcat(*circle_properties.call([r])[0:-1])]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    @unittest.skip
    def test_double_function_call(self):
        with open(os.path.join(MODEL_DIR, 'DoubleFunctionCall.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'FunctionCall')
        print("FunctionCall", casadi_model)
        ref_model = Model()

        # Check that both instances of the function call refer to the same node in the tree
        func_a = casadi_model.equations[0].dep(1).dep(0).dep(0).dep(0).getFunction().__hash__()
        func_b = casadi_model.equations[1].dep(1).dep(0).dep(0).dep(0).getFunction().__hash__()

        self.assertEqual(func_a, func_b)

    def test_forloop(self):
        with open(os.path.join(MODEL_DIR, 'ForLoop.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'ForLoop')
        print(casadi_model)
        ref_model = Model()

        x = ca.MX.sym("x", 10)
        y = ca.MX.sym("y", 10)
        z = ca.MX.sym("z", 10)
        u = ca.MX.sym('u', 10, 2)
        v = ca.MX.sym('v', 2, 10)
        w = ca.MX.sym('w', 2, 10)
        b = ca.MX.sym("b")
        n = ca.MX.sym("n")
        s = ca.MX.sym('s', 10)
        Arr = ca.MX.sym('Arr', 2, 2)
        der_s = ca.MX.sym('der(s)', 10)

        ref_model.states = list(map(Variable, [s]))
        ref_model.der_states = list(map(Variable, [der_s]))
        ref_model.alg_states = list(map(Variable, [x, y, z, u, v, w, b, Arr]))
        ref_model.parameters = list(map(Variable, [n]))
        ref_model.parameters[0].value = 10
        ref_model.equations = [
            ca.horzcat(x - (np.arange(1, 11) + b), w[0, :].T - np.arange(1, 11), w[1, :].T - np.arange(2, 21, 2), u - np.ones((10, 2)), v.T - np.ones((10, 2))),
            y[0:5] - np.zeros(5), y[5:] - np.ones(5),
            ca.horzcat(z[0:5] - np.array([2, 2, 2, 2, 2]), z[5:10] - np.array([1, 1, 1, 1, 1])), der_s - np.ones(10),
            ca.horzcat(Arr[:, 1], Arr[:, 0]) - np.array([[2, 1], [2, 1]])]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_arrayexpressions(self):
        with open(os.path.join(MODEL_DIR, 'ArrayExpressions.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'ArrayExpressions')
        print(casadi_model)
        ref_model = Model()

        a = ca.MX.sym("a", 3)
        b = ca.MX.sym("b", 4)
        c = ca.MX.sym("c", 3)
        d = ca.MX.sym("d", 3)
        e = ca.MX.sym("e", 3)
        g = ca.MX.sym("g", 1)
        h = ca.MX.sym("h", 1)
        i = ca.MX.sym('i', 2, 3)
        B = ca.MX.sym("B", 3)
        C = ca.MX.sym("C", 2)
        D = ca.MX.sym("D", 3)
        E = ca.MX.sym("E", 2)
        arx = ca.MX.sym("ar.x", 3)
        arcy = ca.MX.sym("arc.y", 2)
        arcw = ca.MX.sym("arc.w", 2)
        nested1z = ca.MX.sym('nested1.z', 3)
        nested2z = ca.MX.sym('nested2.z', 2, 3)
        nested1n = ca.MX.sym('nested1.n', 1)
        nested2n = ca.MX.sym('nested2.n', 2)

        scalar_f = ca.MX.sym("scalar_f")
        c_dim = ca.MX.sym("c_dim")
        d_dim = ca.MX.sym("d_dim")

        ref_model.alg_states = list(map(Variable, [arx, arcy, arcw, nested1z, nested2z, a, c, d, e, scalar_f, g, h, i]))
        ref_model.alg_states[6].min = [0, 0, 0]
        ref_model.parameters = list(map(Variable, [nested2n, nested1n, d_dim]))
        parameter_values = [np.array([3, 3]), 3, 3]
        for const, val in zip(ref_model.parameters, parameter_values):
            const.value = val
        ref_model.outputs = list(map(Variable, [h]))
        ref_model.constants = list(map(Variable, [b, c_dim, B, C, D, E]))
        constant_values = [np.array([2.7, 3.7, 4.7, 5.7]), 2, ca.linspace(1., 2., 3), 1.7 * ca.DM.ones(2),
                                     ca.DM.zeros(3), ca.DM.ones(2)]
        for const, val in zip(ref_model.constants, constant_values):
            const.value = val
        ref_model.equations = [c - (a + b[0:3] * e), d - (ca.sin(a / b[1:4])), e - (d + scalar_f), g - ca.sum1(c),
                               h - B[1], arx[1] - scalar_f, nested1z - ca.DM.ones(3), nested2z[0, :].T - np.array([4, 5, 6]),
                               nested2z[1, 0] - 3, nested2z[1, 1] - 2, nested2z[1, 2] - 1, i[0, :] - ca.transpose(ca.DM.ones(3)), i[1, :] - ca.transpose(ca.DM.ones(3)), arcy[0] - arcy[1],
                               arcw[0] + arcw[1], a - np.array([1, 2, 3]), scalar_f - 1.3]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_matrixexpressions(self):
        with open(os.path.join(MODEL_DIR, 'MatrixExpressions.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'MatrixExpressions')
        print(casadi_model)
        ref_model = Model()

        A = ca.MX.sym("A", 3, 3)
        b = ca.MX.sym("b", 3)
        c = ca.MX.sym("c", 3)
        d = ca.MX.sym("d", 3)
        C = ca.MX.sym("C", 2, 3)
        D = ca.MX.sym("D", 3, 2)
        E = ca.MX.sym("E", 2, 3)
        I = ca.MX.sym("I", 5, 5)
        F = ca.MX.sym("F", 3, 3)

        ref_model.alg_states = list(map(Variable, [A, b, c, d]))
        ref_model.constants = list(map(Variable, [C, D, E, I, F]))
        constant_values = [1.7 * ca.DM.ones(2, 3), ca.DM.zeros(3, 2), ca.DM.ones(2, 3), ca.DM.eye(5),
                                     ca.DM.triplet([0, 1, 2], [0, 1, 2], [1, 2, 3], 3, 3)]
        for const, val in zip(ref_model.constants, constant_values):
            const.value = val
        ref_model.equations = [ca.mtimes(A, b) - c, ca.mtimes(A.T, b) - d, F[1, 2]]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_delay(self):
        with open(os.path.join(MODEL_DIR, 'Delay.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)

        casadi_model = gen_casadi.generate(ast_tree, 'Delay')
        # Important to also test expansion of delay expressions of scalar size
        ast_tree = parser.parse(txt)
        casadi_model_expanded = gen_casadi.generate(ast_tree, 'Delay')
        casadi_model_expanded.simplify({'expand_vectors': True})

        ref_model = Model()

        x = ca.MX.sym("x")
        x_delayed = ca.MX.sym("_pymoca_delay_0")
        y = ca.MX.sym("y")
        hour = ca.MX.sym("hour")

        ref_model.alg_states = list(map(Variable, [x, y]))
        ref_model.parameters = list(map(Variable, [hour]))
        ref_model.inputs = list(map(Variable, [x_delayed]))
        ref_model.parameters[0].value = 3600
        ref_model.equations = [y - x_delayed]
        ref_model.delay_states = [x_delayed]
        ref_model.delay_arguments = [DelayArgument(x, 6 * hour)]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)
        self.assert_model_equivalent_numeric(ref_model, casadi_model_expanded)

    def test_delay_for_loop(self):
        with open(os.path.join(MODEL_DIR, 'DelayForLoop.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'DelayForLoop')
        casadi_model.simplify({'detect_aliases': True})
        print(casadi_model)

        ref_model = Model()

        x = ca.MX.sym("x", 2)
        y = ca.MX.sym("y", 2)
        z = ca.MX.sym("z", 2)
        at3_delayed = ca.MX.sym("_pymoca_delay_0", 2)
        delay_time = ca.MX.sym("delay_time")

        ref_model.alg_states = list(map(Variable, [x, y]))
        ref_model.inputs = list(map(Variable, [at3_delayed, z, delay_time]))
        ref_model.inputs[-1].fixed = True
        ref_model.equations = [ca.horzcat(x - 5 * z, y - at3_delayed)]
        ref_model.delay_states = [at3_delayed]
        ref_model.delay_arguments = [DelayArgument(3 * x, delay_time)]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_delay_for_loop_with_expand_vectors(self):
        with open(os.path.join(MODEL_DIR, 'DelayForLoop.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'DelayForLoop')
        casadi_model.simplify({'expand_vectors': True, 'detect_aliases': True})
        print(casadi_model)

        ref_model = Model()

        def _array_mx(name, n):
            return np.array([ca.MX.sym("{}[{}]".format(name, i)) for i in range(n)])

        x = _array_mx("x", 2)
        y = _array_mx("y", 2)
        a = _array_mx("a", 2)
        z = _array_mx("z", 2)
        at3_delayed = _array_mx("_pymoca_delay_0", 2)
        delay_time = ca.MX.sym("delay_time")

        ref_model.alg_states = list(map(Variable, [*x, *y, *a]))
        ref_model.inputs = list(map(Variable, [*at3_delayed, *z, delay_time]))
        ref_model.inputs[-1].fixed = True
        # TODO: "a" is not yet deteced as an alias of "x" when expanding.
        ref_model.equations = [*(x - 5 * z), *(y - at3_delayed), *(a - x)]
        ref_model.delay_states = [*at3_delayed]
        ref_model.delay_arguments = [DelayArgument(3 * a_i, delay_time) for a_i in a]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_array_3d(self):
        casadi_model = transfer_model(MODEL_DIR, 'Array3D', {'expand_vectors': True})

        target_param_values = np.array([[[ -5.326999,  54.050758,  0.000000],
                                    [ -1.0,        0.0,       0.0]],
                                   [[  0.000426,  -0.001241,  2.564056],
                                    [ -1.0,        0.0,       0.0]],
                                   [[  2.577975,  -5.203480,  0.000000],
                                    [ -1.0,        0.0,       0.0]],
                                   [[ 13.219650,  -3.097600, -7.551339],
                                    [ -1.0,        0.0,       0.0]]])

        param_symbols = [x.symbol for x in casadi_model.parameters]

        for ind in np.ndindex(target_param_values.shape):
            flat_index = np.ravel_multi_index(ind, target_param_values.shape)
            self.assertEqual(param_symbols[flat_index].name(), "x[{}]".format(",".join((str(x + 1) for x in ind))))
            self.assertEqual(casadi_model.parameters[flat_index].value, target_param_values[ind])

    def test_attributes(self):
        with open(os.path.join(MODEL_DIR, 'Attributes.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'Attributes')
        print(casadi_model)
        ref_model = Model()

        nested_p1 = ca.MX.sym('nested.p1')
        nested_p = ca.MX.sym('nested.p')
        nested_s = ca.MX.sym('nested.s')
        i = ca.MX.sym("int")
        b = ca.MX.sym("bool")
        r = ca.MX.sym("real")
        der_r = ca.MX.sym("der(real)")
        test_state = ca.MX.sym("test_state")
        i1 = ca.MX.sym("i1")
        i2 = ca.MX.sym("i2")
        i3 = ca.MX.sym("i3")
        i4 = ca.MX.sym("i4")
        cst = ca.MX.sym("cst")
        prm = ca.MX.sym("prm")
        protected_variable = ca.MX.sym("protected_variable")

        ref_model.states = list(map(Variable, [r]))
        ref_model.states[0].start = 20
        ref_model.der_states = list(map(Variable, [der_r]))
        ref_model.alg_states = list(map(Variable, [nested_s, i, b, i4, test_state, protected_variable]))
        ref_model.alg_states[1].min = -5
        ref_model.alg_states[1].max = 10
        ref_model.inputs = list(map(Variable, [i1, i2, i3]))
        ref_model.inputs[0].fixed = True
        ref_model.outputs = list(map(Variable, [i4, protected_variable]))
        ref_model.constants = list(map(Variable, [cst]))
        constant_values = [1]
        for c, v in zip(ref_model.constants, constant_values):
            c.value = v
        ref_model.parameters = list(map(Variable, [nested_p1, nested_p, prm]))
        parameter_values = [1, 2 * nested_p1, 2]
        for c, v in zip(ref_model.parameters, parameter_values):
            c.value = v
        ref_model.equations = [i4 - ((i1 + i2) + i3), der_r - (i1 + ca.if_else(b, 1, 0, True) * i),
                               protected_variable - (i1 + i2), nested_s - 3 * nested_p, test_state - r]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_type(self):
        with open(os.path.join(MODEL_DIR, 'Type.mo'), 'r') as f:
            txt = f.read()
        # noinspection PyUnusedLocal
        ast_tree = parser.parse(txt)
        self.assertTrue(True)

    def test_variable_metadata_function(self):
        # Clear cache
        db_file = os.path.join(MODEL_DIR, 'ParameterAttributes')
        try:
            os.remove(db_file)
        except FileNotFoundError:
            pass

        # Alias detection results in fmin/fmax function calls in the attributes
        compiler_options = \
            {'cache': True,
             'detect_aliases': True}

        ref_model = transfer_model(MODEL_DIR, 'ParameterAttributes', compiler_options)
        self.assertIsInstance(ref_model, Model)
        self.assertNotIsInstance(ref_model, CachedModel)

        cached_model = transfer_model(MODEL_DIR, 'ParameterAttributes', compiler_options)
        self.assertIsInstance(cached_model, Model)
        self.assertIsInstance(cached_model, CachedModel)

        in_var = ca.veccat(*ref_model._symbols(ref_model.parameters))
        function_metadata = ref_model.variable_metadata_function(in_var)

        cached_in_var = ca.veccat(*cached_model._symbols(cached_model.parameters))

        orig_metadata = []
        cached_metadata = []

        variables_with_metadata = ['states', 'alg_states', 'inputs', 'parameters', 'constants']

        for model, metadata in [(ref_model, orig_metadata), (cached_model, cached_metadata)]:
            for variable_list in variables_with_metadata:
                attribute_lists = [[] for i in range(len(CASADI_ATTRIBUTES))]
                for variable in getattr(model, variable_list):
                    for attribute_list_index, attribute in enumerate(CASADI_ATTRIBUTES):
                        value = ca.MX(getattr(variable, attribute))
                        value = value if value.numel() != 1 else ca.repmat(
                            value, *variable.symbol.size())
                        attribute_lists[attribute_list_index].append(value)
                expr = ca.horzcat(*[
                    ca.veccat(*attribute_list) for attribute_list in attribute_lists])
                metadata.append(expr)

        # Check that the output of the variable metadata function matches the
        # original metadata and cached metadata
        for a, b, c in zip(orig_metadata, function_metadata, cached_metadata):
            f_a = ca.Function('tmp', [in_var], [a])
            f_b = ca.Function('tmp', [in_var], [b])
            f_c = ca.Function('tmp', [cached_in_var], [c])

            args_in = ca.DM([2, 11])

            a_out = np.array(f_a(args_in))
            b_out = np.array(f_b(args_in))
            c_out = np.array(f_c(args_in))

            self.assertTrue(np.allclose(a_out, b_out, 0, 0, True))
            self.assertTrue(np.allclose(a_out, c_out, 0, 0, True))

    def test_cache(self):
        # Clear cache
        db_file = os.path.join(MODEL_DIR, 'Aircraft')
        try:
            os.remove(db_file)
        except FileNotFoundError:
            pass

        # Create model, cache it, and load the cache
        compiler_options = \
            {'cache': True}

        ref_model = transfer_model(MODEL_DIR, 'Aircraft', compiler_options)
        self.assertIsInstance(ref_model, Model)
        self.assertNotIsInstance(ref_model, CachedModel)

        cached_model = transfer_model(MODEL_DIR, 'Aircraft', compiler_options)
        self.assertIsInstance(cached_model, Model)
        self.assertIsInstance(cached_model, CachedModel)

        # Compare
        self.assert_model_equivalent_numeric(ref_model, cached_model)

    def test_cache_delay_arguments(self):
        # Clear cache
        db_file = os.path.join(MODEL_DIR, 'Delay')
        try:
            os.remove(db_file)
        except FileNotFoundError:
            pass

        compiler_options = {'cache': True}

        ref_model = transfer_model(MODEL_DIR, 'Delay', compiler_options)
        self.assertIsInstance(ref_model, Model)
        self.assertNotIsInstance(ref_model, CachedModel)

        cached_model = transfer_model(MODEL_DIR, 'Delay', compiler_options)
        self.assertIsInstance(cached_model, Model)
        self.assertIsInstance(cached_model, CachedModel)

        self.assert_model_equivalent_numeric(ref_model, cached_model)

    def test_codegen(self):
        # Clear cache
        db_file = os.path.join(MODEL_DIR, 'Aircraft')
        try:
            os.remove(db_file)
        except FileNotFoundError:
            pass

        for f in glob.glob(os.path.join(MODEL_DIR, "Aircraft*.so")):
            os.remove(f)
        for f in glob.glob(os.path.join(MODEL_DIR, "Aircraft*.dll")):
            os.remove(f)
        for f in glob.glob(os.path.join(MODEL_DIR, "Aircraft*.dylib")):
            os.remove(f)

        # Create model, cache it, and load the cache
        compiler_options = \
            {'codegen': True}

        ref_model = transfer_model(MODEL_DIR, 'Aircraft', compiler_options)
        self.assertIsInstance(ref_model, Model)
        self.assertNotIsInstance(ref_model, CachedModel)

        cached_model = transfer_model(MODEL_DIR, 'Aircraft', compiler_options)
        self.assertIsInstance(cached_model, Model)
        self.assertIsInstance(cached_model, CachedModel)

        # Compare
        self.assert_model_equivalent_numeric(ref_model, cached_model)

    def test_simplify_replace_constant_values(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'replace_constant_values': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, []))
        constant_values = []
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - p1 - p2 - p3 - p4, alias - x, y - x - 3 - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_replace_parameter_expressions(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'replace_parameter_expressions': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p3 = ca.MX.sym('p3')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p3]))
        parameter_values = [2.0, np.nan]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - 3 * p1 - 3 * p3, alias - x, y - x - c - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_replace_parameter_values(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'replace_parameter_values': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p2, p3, p4]))
        parameter_values = [4, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - 2 - p2 - p3 - p4, alias - x, y - x - c - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_replace_parameter_values_and_expressions(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'replace_parameter_values': True,
             'replace_parameter_expressions': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p3 = ca.MX.sym('p3')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p3]))
        parameter_values = [np.nan]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - 6 - 3 * p3, alias - x, y - x - c - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        print(casadi_model)
        print(ref_model)
        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_eliminate_constant_assignments(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'eliminate_constant_assignments': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c, cst]))
        constant_values = [3, 4]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - p1 - p2 - p3 - p4, alias - x, y - x - c - _tmp - cst, _tmp - 0.1 * x]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_eliminable_variable_expression(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'eliminable_variable_expression': r'_\w+'}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - p1 - p2 - p3 - p4, alias - x, y - x - c - 0.1 * x - cst, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_detect_aliases(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'detect_aliases': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 1
        ref_model.states[0].max = 2
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [y, _tmp, cst]))
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - p1 - p2 - p3 - p4, y - x - c - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)
        self.assertEquals(casadi_model.states[0].aliases, ['alias'])

    def test_simplify_detect_negative_alias(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'detect_aliases': True}

        casadi_model = transfer_model(MODEL_DIR, 'NegativeAlias', compiler_options)

        ref_model = Model()

        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 1
        ref_model.states[0].max = 2
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, []))
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.equations = [der_x - x]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)
        self.assertEquals(casadi_model.states[0].aliases, ['-alias'])

    def test_simplify_expand_vectors(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'expand_vectors': True}

        casadi_model = transfer_model(MODEL_DIR, 'SimplifyVector', compiler_options)

        ref_model = Model()

        x0 = ca.MX.sym('x[0]')
        x1 = ca.MX.sym('x[1]')
        der_x0 = ca.MX.sym('der(x)[0]')
        der_x1 = ca.MX.sym('der(x)[1]')

        ref_model.states = list(map(Variable, [x0, x1]))
        ref_model.der_states = list(map(Variable, [der_x0, der_x1]))
        ref_model.alg_states = list(map(Variable, []))
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.equations = [der_x0 - x0, der_x1 - x1]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_reduce_affine_expression_loop(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'expand_vectors': True,
             'detect_aliases': True,
             'reduce_affine_expression': True,
             'replace_constant_expressions': True,
             'replace_constant_values': True,
             'replace_parameter_expressions': True,
             'replace_parameter_values': True}

        casadi_model = transfer_model(MODEL_DIR, 'SimplifyLoop', compiler_options)

        ref_model = Model()

        x = ca.MX.sym('x')
        y0 = ca.MX.sym('y[0]')
        y1 = ca.MX.sym('y[1]')

        A = ca.MX(2, 3)
        A[0, 0] = -1
        A[0, 1] = 1
        A[0, 2] = 0
        A[1, 0] = -2
        A[1, 1] = 0
        A[1, 2] = 1
        b = ca.MX(2, 1)
        b[0, 0] = 0
        b[1, 0] = 0

        ref_model.states = list(map(Variable, []))
        ref_model.der_states = list(map(Variable, []))
        ref_model.alg_states = list(map(Variable, [x, y0, y1]))
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        x = ca.vertcat(x, y0, y1)
        ref_model.equations = [ca.mtimes(A, x) + b]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_reduce_affine_expression(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'reduce_affine_expression': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v

        A = ca.MX(5, 6)
        A[0, 0] = -1.0
        A[0, 1] = 1.0
        A[1, 2] = 1.0
        A[1, 0] = -1.0
        A[2, 0] = -1.0
        A[2, 3] = 1.0
        A[2, 4] = -1.0
        A[2, 5] = -1.0
        A[3, 0] = -0.1
        A[3, 4] = 1.0
        A[4, 5] = 1.0
        b = ca.MX(5, 1)
        b[0] = -p1 - p2 - p3 - p4
        b[2] = -c
        b[4] = -4
        x = ca.vertcat(x, der_x, alias, y, _tmp, cst)
        ref_model.equations = [ca.mtimes(A, x) + b]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_all(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'expand_vectors': True,
             'replace_constant_values': True,
             'replace_constant_expressions': True,
             'replace_parameter_values': True,
             'replace_parameter_expressions': True,
             'eliminate_constant_assignments': True,
             'detect_aliases': True,
             'eliminable_variable_expression': r'_\w+',
             'reduce_affine_expression': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        p3 = ca.MX.sym('p3')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        y = ca.MX.sym('y')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 1
        ref_model.states[0].max = 2
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [y]))
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, []))
        constant_values = []
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p3]))
        parameter_values = [np.nan]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v

        A = ca.MX(2, 3)
        A[0, 0] = -1.0
        A[0, 1] = 1.0
        A[1, 0] = -1.1
        A[1, 2] = 1.0
        b = ca.MX(2, 1)
        b[0] = -6 - 3 * p3
        b[1] = -7
        x = ca.vertcat(x, der_x, y)
        ref_model.equations = [ca.mtimes(A, x) + b]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_simplify_expand_mx(self):
        # Create model, cache it, and load the cache
        compiler_options = \
            {'expand_mx': True}

        casadi_model = transfer_model(MODEL_DIR, 'Simplify', compiler_options)

        ref_model = Model()

        c = ca.MX.sym('c')
        p1 = ca.MX.sym('p1')
        p2 = ca.MX.sym('p2')
        p3 = ca.MX.sym('p3')
        p4 = ca.MX.sym('p4')
        x = ca.MX.sym('x')
        der_x = ca.MX.sym('der(x)')
        alias = ca.MX.sym('alias')
        y = ca.MX.sym('y')
        _tmp = ca.MX.sym('_tmp')
        cst = ca.MX.sym('cst')

        ref_model.states = list(map(Variable, [x]))
        ref_model.states[0].min = 0
        ref_model.states[0].max = 3
        ref_model.states[0].nominal = 10
        ref_model.der_states = list(map(Variable, [der_x]))
        ref_model.alg_states = list(map(Variable, [alias, y, _tmp, cst]))
        ref_model.alg_states[0].min = 1
        ref_model.alg_states[0].max = 2
        ref_model.alg_states[0].nominal = 1
        ref_model.inputs = list(map(Variable, []))
        ref_model.outputs = list(map(Variable, []))
        ref_model.constants = list(map(Variable, [c]))
        constant_values = [3]
        for _cst, v in zip(ref_model.constants, constant_values):
            _cst.value = v
        ref_model.parameters = list(map(Variable, [p1, p2, p3, p4]))
        parameter_values = [2.0, 2 * p1, np.nan, 2 * p3]
        for par, v in zip(ref_model.parameters, parameter_values):
            par.value = v
        ref_model.equations = [der_x - x - p1 - p2 - p3 - p4, alias - x, y - x - c - _tmp - cst, _tmp - 0.1 * x, cst - 4]

        # Compare
        self.assert_model_equivalent_numeric(casadi_model, ref_model)

    def test_state_annotator(self):
        with open(os.path.join(MODEL_DIR, 'StateAnnotator.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'StateAnnotator')
        print(casadi_model)
        ref_model = Model()

        x = ca.MX.sym('x')
        y = ca.MX.sym('y')
        z = ca.MX.sym('z')
        u = ca.MX.sym('u', 3)
        v = ca.MX.sym('v', 3)
        w = ca.MX.sym('w', 3)
        der_x = ca.MX.sym('der(x)')
        der_y = ca.MX.sym('der(y)')
        der_z = ca.MX.sym('der(z)')
        der_u = ca.MX.sym('der(u)', 3)
        der_v = ca.MX.sym('der(v)', 3)
        der_w = ca.MX.sym('der(w)', 3)

        ref_model.states = list(map(Variable, [x, y, z, u, v, w]))
        ref_model.der_states = list(map(Variable, [der_x, der_y, der_z, der_u, der_v, der_w]))
        ref_model.equations = [der_x + der_y - 1,
                               der_x * y + x * der_y - 2,
                               (der_x * y - x * der_y) / (y**2) - 3,
                               2 * x * der_x - 4,
                               der_z - 5,
                               der_x * z + x * der_z + der_y * z + y * der_z - 4,
                               0,
                               der_u - np.array([1, 2, 3]),
                               der_v[0] - 4,
                               der_v[1] - 5,
                               der_v[2] - 6,
                               der_w - np.array([7, 8, 9])]

        self.assert_model_equivalent_numeric(ref_model, casadi_model)

    def test_alias_relation(self):
        a = AliasRelation()
        self.assertEqual(a.canonical_signed('-a'), ('a', -1))
        a.add('a', '-b')
        a.add('b', 'c')
        a.add('d', '-b')
        self.assertEqual(list(a), [('d', ['a', '-b', '-c'])])

    def test_cat_params(self):
        casadi_model = transfer_model(MODEL_DIR, 'Concat', {'replace_constant_values': True})
        c = [0, 1, 2, 2, 2, 0, 1]
        for i, e in enumerate(c):
            self.assertEqual(casadi_model.parameters[1].value[i], e)

    def test_inline_input_assignment(self):
        casadi_model = transfer_model(MODEL_DIR, 'InlineAssignment')
        self.assertTrue(casadi_model.inputs[0].fixed)
        self.assertFalse(casadi_model.alg_states[0].fixed)
        casadi_model = transfer_model(MODEL_DIR, 'InlineAssignment', {'detect_aliases': True})
        self.assertTrue(casadi_model.inputs[0].fixed)

    def test_unspecified_dimensions(self):
        with open(os.path.join(MODEL_DIR, 'UnspecifiedDimensions.mo'), 'r') as f:
            txt = f.read()
        ast_tree = parser.parse(txt)
        casadi_model = gen_casadi.generate(ast_tree, 'B')

        a_x = casadi_model.parameters[0]
        a_y = casadi_model.parameters[1]

        self.assertTrue(a_x.symbol.name() == "a.x")
        self.assertEqual(a_x.value, [[ 88.3224,  281.642,  143.011],
                                     [ 58.8183, -24.9845,      0.0],
                                     [-1.45483,      0.0,      0.0]])

        self.assertTrue(a_y.symbol.name() == "a.y")
        self.assertEqual(a_y.value, [[1, 2],
                                     [3, 4],
                                     [5, 6]])

if __name__ == "__main__":
    unittest.main()


import multiprocessing
from http.server import BaseHTTPRequestHandler, HTTPServer
import logging
from . import playlocalsound as pls

class HandleSongRequest():
    def __init__(self):
        self.body = ""

    def playSong(filename):
        # strip the qoutes
        filename = filename.strip("\"")
        filename = filename.strip("%22")
        playProcess = multiprocessing.Process(target=pls.playLocalFileSystem, args=[filename])
        playProcess.start()
        playProcess.join()
        # print(f"Playing song: {filename}")
    def playSongPyDub(filename):
        filename = filename.strip("\"")
        filename = filename.strip("%22")
        playProcess = multiprocessing.Process(target=pls.playLocalFilePyDub, args=[filename])
        playProcess.start()
        playProcess.join()

class S(BaseHTTPRequestHandler):
    def _set_response(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_GET(self):
        logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
        self._set_response()
        self.wfile.write("GET request for {}".format(self.path).encode('utf-8'))

    def do_POST(self):
        content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
        post_data = self.rfile.read(content_length) # <--- Gets the data itself
        logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
                str(self.path), str(self.headers), post_data.decode('utf-8'))

        decoded_data = post_data.decode('utf-8')
        # if it is : seperated:
        if ":" in decoded_data:
            req_list = decoded_data.split(':')
        # If it is = seperated
        if "=" in decoded_data:
            req_list = decoded_data.split('=')
        print(f"post-DATA : : : {req_list[1]}")
        HandleSongRequest.playSongPyDub(req_list[1])
        print(f"post-DATA : : : {self.rfile}")
        self._set_response()
        self.wfile.write("POST request for {}".format(self.path).encode('utf-8'))

def run(server_class=HTTPServer, handler_class=S, port=8080):
    logging.basicConfig(level=logging.INFO)
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    logging.info('Starting httpd...\n')
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    logging.info('Stopping httpd...\n')

if __name__ == '__main__':
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()


import cv2
import os
import numpy as np
from PIL import Image

class ImgTools:

    @staticmethod
    def show_img(img):
        """
        以视窗的形式展现图片，视窗大小为图片的一半
        若图片太大，直接用cv2.imshow会以完整分辨率呈现，所以改用下面的nameWindow把图像放在里面限制大小
        :param img: 要显示的图像
        :return: None
        """
        # 冷知识:在imshow显示的时候，可以在选定的窗口中做图片的复制(Ctrl+C)与保存(Ctrl+S)
        cv2.namedWindow("windows", 0)
        # 设定窗口大小，("名称", x, y) x是宽度 y是高度，此处暂以缩小一半作呈现
        cv2.resizeWindow("windows", int(img.shape[1] / 2), int(img.shape[0] / 2))
        cv2.imshow("windows", img)
        cv2.waitKey()
        cv2.destroyAllWindows()

    @staticmethod
    def save_img(file_name, img, org_img_pixel, element_img_pixel, cal_color_method):
        """
        储存图像
        :param file_name:储存的档名
        :param img:要储存的图像资料
        :param org_img_pixel: 底图采样单位
        :param element_img_pixel: 单位元素图片的大小
        :param cal_color_method: 计算底图颜色的方法:平均颜色->average ; 众数颜色->most，预设为average
        :return: None
        """
        zoom_ratio = element_img_pixel // org_img_pixel  # 计算缩放倍率
        dir_path = f"./montage_img"
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
        # Saving the image 储存图片
        filename = f'{dir_path}/Montage_{file_name}_ZoomRatio-{zoom_ratio}_' \
                   f'ElementImgSize-{element_img_pixel}_Method-{cal_color_method}.png'
        # imencode，好处是能写入中文名称
        cv2.imencode('.png', img, [int(cv2.IMWRITE_TIFF_RESUNIT), 2,  # 分辨率单位
                                   int(cv2.IMWRITE_TIFF_COMPRESSION), 5,  # 压缩方式
                                   int(cv2.IMWRITE_TIFF_XDPI), 600,  # 设定水平dpi
                                   int(cv2.IMWRITE_TIFF_YDPI), 600,  # 设定垂直dpi
                                   ])[1].tofile(filename)

    @staticmethod
    def pil_import_img_trans_cv2(file_path):
        """
        透过PIL汇入图片并转换成cv2格式传出
        :param file_path: 要读取的图片路径
        :return: 回传cv2形式的图像资料
        """
        img = Image.open(file_path)  # 使用PIL读取档案，避开cv2无法读取中文路径的问题
        pil2cv2 = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
        return pil2cv2

    @staticmethod
    def concat_vh(list_2d):
        """
        将二维清单依照顺序水平垂直拼接起来，先拼接水平再垂直
        :param list_2d: 要拼接的图片清单
        :return: 拼接的图像结果
        """
        # return final image
        # 下方的拼接结果即是同张图以3x3九宫格排列拼接
        # img_tile = concat_vh([[img1_s, img1_s, img1_s],
        #                       [img1_s, img1_s, img1_s],
        #                       [img1_s, img1_s, img1_s]])
        return cv2.vconcat([cv2.hconcat(list_h)
                            for list_h in list_2d])


from django.conf.urls.defaults import *

from haystack.forms import FacetedSearchForm
from haystack.query import SearchQuerySet
from haystack.views import FacetedSearchView

sqs =SearchQuerySet().facet(
                      'funder_name'
                    ).facet(
                      'funder_party'
                    ).facet(
                      'funder_committee_type'
                    ).order_by(
                      '-pub_date'
                    )

urlpatterns = patterns('haystack.views',
        url(r'^$', FacetedSearchView(form_class=FacetedSearchForm,
                                        template='search/search.html',
                                        searchqueryset=sqs,
                                        results_per_page=15),
                                        name='haystack_search'),
        )


from django.conf.urls import patterns, url

from .views import *

urlpatterns = patterns(
    '',
    url('^$', generator_view, name='generator'),
    url('^domains$', domain_list_view, name='domain_list'),
    url('^domain/(?P<domain>.*)$', domain_view, name='domain'),
)


import json
import time
import logging

from typing import Dict, List, Union

from openzwave.node import ZWaveNode
from openzwave.value import ZWaveValue
from openzwave.network import ZWaveNetwork

from zwave_mqtt_bridge.hass_mqtt import HassMqtt
from zwave_mqtt_bridge.zw_node import ZwNode

DEBUG = True

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s',
                    datefmt='%m-%d %H:%M',
                    filename='zwave-gate.log',
                    filemode='w')
_log = logging.getLogger("bridge")

class Bridge:

    def __init__(self, mqtt: HassMqtt, ignored_labels: List):
        self._ignored_labels = ignored_labels
        self._mqtt = mqtt
        self._nodes = {}  # type: Dict[str, ZwNode]
        self._last_config = 0
        self._log = logging.getLogger("zwbridge")
        self._repair_time = 0
        self._last_repair_attempt = 0
        self._healing = False
        self.zw_network = None
        #self._repair_time = int(time.time() / (24*60*60))

    def _find_node(self, node_id: int) -> Union[ZwNode, None]:
        for i in self._nodes.values():
            if i.id() == node_id:
                return i
        return None

    def set_config(self, node_id: int, config_id: int, data: str):
        self._find_node(node_id).set_config(int(config_id), json.loads(data))

    def nodes(self):
        return self._nodes

    def check_for_repair(self, network: ZWaveNetwork):
        if time.time() - self._last_repair_attempt > 5*60:
            self._last_repair_attempt = time.time()
            if network.is_ready:
                now = int(time.time() / (24*60*60))
                if now != self._repair_time:
                    try:
                        _log.info("Starting healing..")
                        self._healing = True
                        network.heal(True)
                    finally:
                        self._healing = False
                        self._repair_time = now
                        _log.info("Healing done")
            else:
                _log.info("Healing scheduled - waiting network to be ready")

    def register_all(self):
        self._last_config = time.time()
        for node in self._nodes.values():
            node.register(self._mqtt)

    def value_update(self, network: ZWaveNetwork, node: ZWaveNode, value: ZWaveValue):
        self.check_for_repair(network)
        if self._healing:
            _log.info("Healing, skipping value update")
            return
        n = self._nodes.get(node.name)  # type: ZwNode
        if not n:
            self._log.info("New node %s", node)
            n = ZwNode(node, self._mqtt, self._ignored_labels)
            name = node.name
            if node.name == "":
                name = str(node.node_id)
            self._nodes[name] = n
        if value.genre == "User":
            if self._last_config:
                if value.label.lower() in self._ignored_labels:
                    return
                if n.is_spamming():
                    return
                if not n.update_state(value):
                    self._log.info("Value update (Command class: %r), %s => %s=%r (NOT HANDLED)", hex(value.command_class),
                                   n.name(), value.label, value.data)
        elif value.genre == "System":
            self._log.debug("System (Command class: %r), %s => %s=%r (NOT HANDLED)", hex(value.command_class),
                           n.name(), value.label, value.data)
        elif value.genre == "Config":
            self._log.debug("Config (Command class: %r), %s => %s=%r (NOT HANDLED)", hex(value.command_class),
                           n.name(), value.label, value.data)
        elif value.genre == "Basic":
            self._log.debug("Basic (Command class: %r), %s => %s=%r (NOT HANDLED)", hex(value.command_class),
                           n.name(), value.label, value.data)
        else:
            _log.info("Unhandled message of genre %r (%r, %r)", value.genre, value.label, value.data)

    def heal(self, node_id):
        self._find_node(node_id)._zwn.heal()

    def network_update(self, node_id):
        return self.zw_network.controller.request_network_update(node_id)

    def neighbor_update(self, node_id):
        return self.zw_network.controller.request_node_neighbor_update(node_id)

    def network_heal(self):
        return self.zw_network.heal()

    def add_node(self):
        return self.zw_network.controller.add_node()

    def write_config(self):
        return self.zw_network.write_config()

    def update_config(self):
        return self.zw_network.controller.update_ozw_config()

    def rename_node(self, node_id, name):
        pass

    def refresh_info(self, node_id):
        return self.zw_network.controller.send_node_information(node_id)


import numpy as np
from skimage import measure

def regiongrow_lesions(mask, img, spacing, config):
    mask = mask.astype(int)
    img_bin = (img > 129).astype(int)
    img_labels = measure.label(img_bin)
    ovl_labels, num_lesions = measure.label(mask, return_num=True)
    maxvol = np.ceil(config['max_vol'] / np.product(spacing)).astype(int)

    for label in range(1, num_lesions + 1):

        location = np.where(ovl_labels == label)

        label_img = []
        for n in range(len(location[0])):
            label_img.append(img_labels[location[0][n], location[1][n], location[2][n]])
        label_img = np.array(label_img)
        label_img = label_img[np.array(np.nonzero(label_img))]
        if len(label_img[0]) > 0:
            label_img = np.argmax(np.bincount(np.asarray(label_img[0])))
        else:
            continue

        location_lesion = np.array(np.where(img_labels == label_img))

        label_mask = []
        for n in range(len(location_lesion[0])):
            label_mask.append(mask[location_lesion[0][n], location_lesion[1][n], location_lesion[2][n]])
        label_mask = np.array(label_mask)
        label_mask = label_mask[np.array(np.nonzero(label_mask))]
        #        print(label_mask)
        label_mask = np.argmax(np.bincount(np.asarray(label_mask[0])))

        # discard if more than 25% of lesion is background
        if float(len(location[0])) / float(len(location_lesion[0])) < 0.25:
            continue

        # discard if lesion grows larger than maximum volume
        if len(location_lesion[0]) > maxvol:
            continue

        for n in range(len(location_lesion[0])):
            mask[location_lesion[0, n], location_lesion[1, n], location_lesion[2, n]] = label_mask
    return mask.astype(int)

def remove_small_lesions(mask, spacing):
    mask_bin = mask > 0
    ovl_labels, num_lesions = measure.label(mask_bin, return_num=True)
    th = (np.ceil(2 / np.product(spacing)).astype(int))
    if th < 2:
        th = 2

    for label in range(1, num_lesions + 1):
        location = np.where(ovl_labels == label)
        if len(location[0]) <= th:
            for n in range(len(location[0])):
                mask[location[0][n], location[1][n], location[2][n]] = 0
    return mask


from autobahn.asyncio.websocket import WebSocketServerProtocol, \
    WebSocketServerFactory

from MonPaquet import json_parser
#from MonPaquet import gpio_controler

class MyServerProtocol(WebSocketServerProtocol):

    def setGPIOList(self, GPIOList):
        self.myGPIOs = GPIOList
        #plus utilisé

    def onConnect(self, request):
        print("Client connecting: {0}".format(request.peer))
        self.myGPIOs = dict()
        json_parser.readconfig(self.myGPIOs, "gpio_def.xml")
        # gpio_controler.setupOutput(self.myGPIOs)

    def onOpen(self):
        print("WebSocket connection open.")

    def onMessage(self, payload, isBinary):
        if isBinary:
            print("Binary message received: {0} bytes".format(len(payload)))
        else:
            print("Text message received: {0}".format(payload.decode('utf8')))

        # echo back message verbatim
        # self.sendMessage(payload, isBinary)
        #jsonStr = json.loads(payload.decode('utf8'))

        jsonStr2 = payload.decode('utf8')

        json_parser.read_json(jsonStr2, self.myGPIOs)

        for keys in self.myGPIOs.keys():
            print(keys + " vaut : " + self.myGPIOs[keys].value)

        # gpio_controler.apply(self.myGPIOs)

    def onClose(self, wasClean, code, reason):
        print("WebSocket connection closed: {0}".format(reason))
        # gpio_controler.close()

if __name__ == '__main__':
    import asyncio

    factory = WebSocketServerFactory(u"ws://127.0.0.1:9000")
    factory.protocol = MyServerProtocol

    loop = asyncio.get_event_loop()
    coro = loop.create_server(factory, '0.0.0.0', 9000)
    server = loop.run_until_complete(coro)

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        server.close()
        loop.close()


"""empty message

Revision ID: 80bfb3203169
Revises: 29fd6d1fc6da
Create Date: 2017-11-23 19:08:59.269351

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '80bfb3203169'
down_revision = '29fd6d1fc6da'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint('evaluations_company_id_fkey', 'evaluations', type_='foreignkey')
    op.create_foreign_key(None, 'evaluations', 'users', ['company_id'], ['id'])
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint(None, 'evaluations', type_='foreignkey')
    op.create_foreign_key('evaluations_company_id_fkey', 'evaluations', 'companies', ['company_id'], ['id'])
    # ### end Alembic commands ###


# coding: utf-8
# Author: Roman Miroshnychenko aka Roman V.M.
# E-mail: roman1972@gmail.com

from django.apps.config import AppConfig

class AddonSubmitterAppConfig(AppConfig):
    name = 'addon_submitter'
    verbose_name = 'Kodi Addon Submitter'


#source : https://github.com/georgestanley/CMC/blob/master/NCE/NCEAverage.py

import torch
from torch import nn
from utils.alias_multinomial import AliasMethod
import math

class ContLoss(nn.Module):

    def __init__(self, tau=0.07):
        super(ContLoss, self).__init__()
        self.tau = tau

    def forward(self,a,b):
        a_norm = torch.norm(a, dim=1).reshape(-1, 1)
        a_cap = torch.div(a, a_norm)
        b_norm = torch.norm(b, dim=1).reshape(-1, 1)
        b_cap = torch.div(b, b_norm)
        a_cap_b_cap = torch.cat([a_cap, b_cap], dim=0)
        a_cap_b_cap_transpose = torch.t(a_cap_b_cap)
        b_cap_a_cap = torch.cat([b_cap, a_cap], dim=0)
        sim = torch.mm(a_cap_b_cap, a_cap_b_cap_transpose)
        sim_by_tau = torch.div(sim, self.tau)
        exp_sim_by_tau = torch.exp(sim_by_tau)
        sum_of_rows = torch.sum(exp_sim_by_tau, dim=1)
        exp_sim_by_tau_diag = torch.diag(exp_sim_by_tau)
        numerators = torch.exp(torch.div(torch.nn.CosineSimilarity()(a_cap_b_cap, b_cap_a_cap), self.tau))
        denominators = sum_of_rows - exp_sim_by_tau_diag
        num_by_den = torch.div(numerators, denominators)
        neglog_num_by_den = -torch.log(num_by_den)
        return torch.mean(neglog_num_by_den)

'''
class NCEAverage(nn.Module):

    def __init__(self, inputSize, outputSize, K, T=0.07, momentum=0.5, use_softmax=False):
        super(NCEAverage, self).__init__()
        self.nLem = outputSize
        self.unigrams = torch.ones(self.nLem)
        self.multinomial = AliasMethod(self.unigrams)
        self.multinomial.cuda()
        self.K = K
        self.use_softmax = use_softmax

        self.register_buffer('params', torch.tensor([K, T, -1, -1, momentum]))
        stdv = 1. / math.sqrt(inputSize / 3)
        self.register_buffer('memory_l', torch.rand(outputSize, inputSize).mul_(2 * stdv).add_(-stdv))
        self.register_buffer('memory_ab', torch.rand(outputSize, inputSize).mul_(2 * stdv).add_(-stdv))

    def forward(self, l, ab, y, idx=None):
        K = int(self.params[0].item())
        T = self.params[1].item()
        Z_l = self.params[2].item()
        Z_ab = self.params[3].item()

        momentum = self.params[4].item()
        batchSize = l.size(0)
        outputSize = self.memory_l.size(0)
        inputSize = self.memory_l.size(1)

        # score computation
        if idx is None:
            idx = self.multinomial.draw(batchSize * (self.K + 1)).view(batchSize, -1)
            idx.select(1, 0).copy_(y.data)
        # sample
        weight_l = torch.index_select(self.memory_l, 0, idx.view(-1)).detach()
        weight_l = weight_l.view(batchSize, K + 1, inputSize)
        out_ab = torch.bmm(weight_l, ab.view(batchSize, inputSize, 1))
        # sample
        weight_ab = torch.index_select(self.memory_ab, 0, idx.view(-1)).detach()
        weight_ab = weight_ab.view(batchSize, K + 1, inputSize)
        out_l = torch.bmm(weight_ab, l.view(batchSize, inputSize, 1))

        if self.use_softmax:
            out_ab = torch.div(out_ab, T)
            out_l = torch.div(out_l, T)
            out_l = out_l.contiguous()
            out_ab = out_ab.contiguous()
        else:
            out_ab = torch.exp(torch.div(out_ab, T))
            out_l = torch.exp(torch.div(out_l, T))
            # set Z_0 if haven't been set yet,
            # Z_0 is used as a constant approximation of Z, to scale the probs
            if Z_l < 0:
                self.params[2] = out_l.mean() * outputSize
                Z_l = self.params[2].clone().detach().item()
                print("normalization constant Z_l is set to {:.1f}".format(Z_l))
            if Z_ab < 0:
                self.params[3] = out_ab.mean() * outputSize
                Z_ab = self.params[3].clone().detach().item()
                print("normalization constant Z_ab is set to {:.1f}".format(Z_ab))
            # compute out_l, out_ab
            out_l = torch.div(out_l, Z_l).contiguous()
            out_ab = torch.div(out_ab, Z_ab).contiguous()

        # # update memory
        with torch.no_grad():
            l_pos = torch.index_select(self.memory_l, 0, y.view(-1))
            l_pos.mul_(momentum)
            l_pos.add_(torch.mul(l, 1 - momentum))
            l_norm = l_pos.pow(2).sum(1, keepdim=True).pow(0.5)
            updated_l = l_pos.div(l_norm)
            self.memory_l.index_copy_(0, y, updated_l)

            ab_pos = torch.index_select(self.memory_ab, 0, y.view(-1))
            ab_pos.mul_(momentum)
            ab_pos.add_(torch.mul(ab, 1 - momentum))
            ab_norm = ab_pos.pow(2).sum(1, keepdim=True).pow(0.5)
            updated_ab = ab_pos.div(ab_norm)
            self.memory_ab.index_copy_(0, y, updated_ab)

        return out_l, out_ab
'''

'''
class NCESoftmaxLoss(nn.Module):
    """Softmax cross-entropy loss (a.k.a., info-NCE loss in CPC paper)"""
    def __init__(self):
        super(NCESoftmaxLoss, self).__init__()
        self.criterion = nn.CrossEntropyLoss()

    def forward(self, x):
        bsz = x.shape[0]
        x = x.squeeze()
        label = torch.zeros([bsz]).cuda().long()
        loss = self.criterion(x, label)
        return loss
'''


from ConfigParser import RawConfigParser, NoSectionError, NoOptionError
from argparse import ArgumentParser, RawDescriptionHelpFormatter
import os
from pushover import Pushover

def read_config(config_path):
    config_path = os.path.expanduser(config_path)
    config = RawConfigParser()
    params = {"users": {}}
    files = config.read(config_path)
    if not files:
        return params
    params["token"] = config.get("main", "token")
    for name in config.sections():
        if name != "main":
            user = {}
            user["user_key"] = config.get(name, "user_key")
            try:
                user["device"] = config.get(name, "device")
            except NoOptionError:
                user["device"] = None
            params["users"][name] = user
    return params

def main():
    parser = ArgumentParser(
        description="Send a message to pushover.",
        formatter_class=RawDescriptionHelpFormatter,
        epilog="""
For more details and bug reports, see: https://github.com/Thibauth/python-pushover""",
    )
    parser.add_argument("--token", help="API token")
    parser.add_argument(
        "--user",
        "-u",
        help="User key or section name in the configuration",
        required=True,
    )
    parser.add_argument(
        "-c",
        "--config",
        help="configuration file\
                        (default: ~/.pushoverrc)",
        default="~/.pushoverrc",
    )
    parser.add_argument("message", help="message to send")
    parser.add_argument("--url", help="additional url")
    parser.add_argument("--url-title", help="url title")
    parser.add_argument("--title", "-t", help="message title")
    parser.add_argument(
        "--priority", "-p", help="notification priority (-1, 0, 1 or 2)", type=int
    )
    parser.add_argument(
        "--retry",
        "-r",
        help="resend interval in seconds (required for priority 2)",
        type=int,
    )
    parser.add_argument(
        "--expire",
        "-e",
        help="expiration time in seconds (required for priority 2)",
        type=int,
    )
    parser.add_argument(
        "--version",
        "-v",
        action="version",
        help="output version information and exit",
        version="""
%(prog)s 1.0
Copyright (C) 2013-2018 Thibaut Horel <thibaut.horel@gmail.com>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.""",
    )

    args = parser.parse_args()
    params = read_config(args.config)
    if args.priority == 2 and (args.retry is None or args.expire is None):
        parser.error("priority of 2 requires expire and retry")
    if args.user in params["users"]:
        user_key = params["users"][args.user]["user_key"]
        device = params["users"][args.user]["device"]
    else:
        user_key = args.user
        device = None
    token = args.token or params["token"]

    Pushover(token).message(
        user_key,
        args.message,
        device=device,
        title=args.title,
        priority=args.priority,
        url=args.url,
        url_title=args.url_title,
        timestamp=True,
        retry=args.retry,
        expire=args.expire,
    )

if __name__ == "__main__":
    main()


from __future__ import division
import json
import matplotlib.pyplot as plt

# global
global_10 = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_10.json').read())
global_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_dv.json').read())
global_walk = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_walk.json').read())
global_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_walk_dv.json').read())
global_10_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_10_dv.json').read())
global_10_walk = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_10_walk.json').read())
global_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/global_10_walk_dv.json').read())

# [thres, preci, recal, fone]
# plt.plot(global_walk[0][2], global_walk[0][1], linewidth=2, linestyle="-", label="graph2vec")
# plt.plot(global_walk_dv[0][2], global_walk_dv[0][1], linewidth=2, linestyle="-", label="graph2vec + doc2vec-SUP")
# plt.plot(global_10_walk[0][2], global_10_walk[0][1], linewidth=2, linestyle="-", label="10-FEAT + graph2vec")
# plt.plot(global_10_walk_dv[0][2], global_10_walk_dv[0][1], linewidth=2, linestyle="-", label="10-FEAT + graph2vec + doc2vec-SUP")

plt.plot(global_10[0][2], global_10[0][1], linewidth=2, linestyle="-", label="10-FEAT")
plt.plot(global_dv[0][2], global_dv[0][1], linewidth=2, linestyle="-", label="doc2vec-SUP")
plt.plot(global_10_dv[0][2], global_10_dv[0][1], linewidth=2, linestyle="-", label="10-FEAT + doc2vec-SUP")

plt.legend(loc='upper right')
plt.xlabel('recall', fontsize=20)
plt.ylabel('precision', fontsize=20)
plt.title('Recall-Precision Curve', fontsize=20)

# partition
# part1_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/part1_10_walk_dv.json').read())
# part2_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/part2_10_walk_dv.json').read())
# part3_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/part3_10_walk_dv.json').read())
# part4_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/part4_10_walk_dv.json').read())
# part5_10_walk_dv = json.loads(open(r'/Users/ZhangHaotian/desktop/LO/part5_10_walk_dv.json').read())

# create x axis to be 0.1:0.01:0.99
# x_axis = []
# initial = 0.1
# while initial <= 0.99:
#     x_axis.append(initial)
#     initial += 0.01
#     initial = round(initial, 2)


import os
import shutil
import re
from glob import glob
import argparse
import cv2

# mode 1: copy all file in folder + delete select file extension
#python ./scripts/copyImage.py --input_dir data/SRRAW_text --output_dir data/SRRAW_textEdit --mode 1 --image_file_extension '*jpg' --delete_specific_file_extension TRUE --delete_file_extension '*ARW'
# mode 2: copy by quantity
#python ./scripts/copyImage.py --input_dir data/mini-imagenet-20220523T033428Z-001/mini-imagenet --output_dir data/mini-imagenet-20220523T033428Z-001/mini-imagenetEdit --image_file_extension '*jpg' --mode 2 --quantity 35

image_size = 96

def main(args) -> None:
    if args.mode == '1':
        mode1(args)
    elif args.mode == '2':
        mode2(args)
    else:
        print("Invalid Mode")

def mode1(args) -> None:
    delete_specific_file_extension = args.delete_specific_file_extension
    delete_file_extension = args.delete_file_extension
    input_dir = args.input_dir
    image_file_extension = args.image_file_extension
    output_dir = args.output_dir

    list_folder_input = [x[0] for x in os.walk(input_dir)]
    list_folder_input.sort(key=natural_keys)
    # print(list_folder_input)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
        start = 1
        print('Start: ', start)
    else:
        list_output = os.listdir(output_dir)
        print('Length Output: ', len(list_output))
        if len(list_output) > 0:
            list_output.sort(key=natural_keys)
            end_output = list_output[-1]
            end_output = end_output.split('.')[0]
            start = int(end_output) + 1
            print('Start: ', start)
        else:
            start = 1
            print('Start: ', start)

    index = start
    for folder_input in list_folder_input:
        # print('Folder: ', folder_input)
        # Delete Specific file extention
        if delete_specific_file_extension:
            delete_file_names = glob(os.path.join(folder_input, delete_file_extension))
            for a in delete_file_names:
                os.remove(a)
        # Get all image paths
        image_file_names = glob(os.path.join(folder_input, image_file_extension))
        # print('List: ', image_file_names)
        # print('Length Input {dir}: '.format(dir=folder_input), len(image_file_names))
        for image in image_file_names:
            img = cv2.imread(image)
            [h, w, _] = img.shape
            if h >= image_size and w >= image_size:
                worker(folder_input, image, output_dir, index)
                index += 1

def mode2(args) -> None:
    quantity = args.quantity
    input_dir = args.input_dir
    image_file_extension = args.image_file_extension
    output_dir = args.output_dir

    list_folder_input = [x[0] for x in os.walk(input_dir)]
    list_folder_input.sort(key=natural_keys)
    # print(list_folder_input)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)
        start = 1
        print('Start: ', start)
    else:
        list_output = os.listdir(output_dir)
        print('Length Output: ', len(list_output))
        if len(list_output) > 0:
            list_output.sort(key=natural_keys)
            end_output = list_output[-1]
            end_output = end_output.split('.')[0]
            start = int(end_output) + 1
            print('Start: ', start)
        else:
            start = 1
            print('Start: ', start)

    index = start
    for folder_input in list_folder_input:
        # print('Folder: ', folder_input)
        # Get all image paths
        image_file_names = glob(os.path.join(folder_input, image_file_extension))
        # print('List: ', image_file_names)
        # print('Length Input {dir}: '.format(dir=folder_input), len(image_file_names))
        for image in image_file_names[0:quantity-1]:
            img = cv2.imread(image)
            [h, w, _] = img.shape
            print(h,w)
            if h >= image_size and w >= image_size:
                worker(folder_input, image, output_dir, index)
                index += 1

def worker(images_dir, image_file_name, output_dir, index) -> None:
    new_image_name = f"{index}.{image_file_name.split('.')[-1]}"
    shutil.copyfile(f"{image_file_name}", f"{output_dir}/{new_image_name}")

def atoi(text):
    return int(text) if text.isdigit() else text

def natural_keys(text):
    '''
    alist.sort(key=natural_keys) sorts in human order
    http://nedbatchelder.com/blog/200712/human_sorting.html
    (See Toothy's implementation in the comments)
    '''
    return [atoi(c) for c in re.split(r'(\d+)', text)]

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Prepare database scripts.")
    parser.add_argument("--input_dir", type=str, help="Path to input image directory.")
    parser.add_argument("--output_dir", type=str, help="Path to generator image directory.")
    parser.add_argument("--mode", type=str, help="1 or 2.")
    parser.add_argument("--quantity", type=int, help="Quantity need to copy", default=0)
    parser.add_argument("--image_file_extension", type=str, help="File extension of input image.")
    parser.add_argument("--delete_specific_file_extension", type=bool, default=False,
                        help="If True, all file with folow file extension will be delete.")
    parser.add_argument("--delete_file_extension", type=str, default=None, help="File extension need to delete.")
    args = parser.parse_args()

    main(args)