import sys

from filtering_pipeline import KO_FILTER_TAG
from sketchgraphs.data.sequence import EdgeOp, NodeOp
from sam.catalog_primitive import Arc, Line, Circle, Point
from sam.catalog_constraint import *

from src.filters.filter_convert_exchangeformat import FilterConvertSequence
from src.utils.logger import logger
import unittest

class TestFilterCount(unittest.TestCase):
    @classmethod
    def setUp(self):
        self.filter = FilterConvertSequence(conf= {'nb_nodes': None, 'nb_edges': 1})

    def test_two_circles(self):
        mock_sequence = [
            NodeOp(label=7), # External
            NodeOp(label=2, parameters = {'isConstruction': False, 'clockwise': False, 'xCenter': 0.0,  # Circle
                                            'yCenter': 0.0, 'xDir': 1.0, 'yDir': 0.0, 'radius': 0.07468157776994294}),
            NodeOp(label=103), # SubnodeType.SN_Center
            EdgeOp(label=101, references=[2,1]), # ConstraintType.Subnode
            EdgeOp(label=0, references=[2,0]), # Coincident
            NodeOp(label=2, parameters={'isConstruction': False, 'clockwise': False, 'xCenter': 0.0, 'yCenter': 0.0,
                            'xDir': 1.0, 'yDir': 0.0, 'radius': 0.06665470513285787}), # Circle
            NodeOp(label=103), # SubnodeType.SN_Center
            EdgeOp(label=101, references=[4,3]), # ConstraintType.Subnode
            EdgeOp(label=0, references=[4,2]), # Coincident
            NodeOp(label=8), # Stop
        ]

        answer = self.filter.process({'sequence': mock_sequence})
        logger.debug(f'answer= {answer}')
        self.assertIsNone(answer.get(KO_FILTER_TAG))

        sketch = answer.get('sequence')
        self.assertEqual(len(sketch), 3)
        self.assertTrue(isinstance(sketch[0], Circle))
        self.assertTrue(isinstance(sketch[1], Circle))
        self.assertTrue(isinstance(sketch[2], Coincident))


#%%
import requests
from bs4 import BeautifulSoup
from selenium import webdriver

#%%

def get_lol_site(username):
    return ''.join(['https://euw.op.gg/summoner/userName=',username])

#%%

wd_lol = webdriver.Chrome(executable_path="./chromedriver_win32/chromedriver.exe")
#%%
wd_lol.switch_to_window('/summoner/league/')

#%%
wd_lol.get(get_lol_site('arakniro'))

rank = wd_lol.find_element_by_class_name("LadderRank")

#%%
num_classement, tier_classement = rank.text.split(' ')[2:4]
#%%
num_classement = int(''.join([i for i in num_classement if i.isdigit()]))
#%%
tier_classement = float(''.join([i for i in tier_classement if i not in ['(','%']]))

#%%

classement_solo = wd_lol.find_element_by_class_name('TierRank')
classement_solo.text

#%%
classement_flex = wd_lol.find_element_by_class_name('sub-tier__rank-tier')
classement_flex.text

#%%

menu = wd_lol.find_element_by_class_name('Menu')
#%%
links = wd_lol.find_elements_by_css_selector('dd')
len(links)
#%%

side_content = wd_lol.find_element_by_class_name('SideContent')

#%%
win_ratio = wd_lol.find_element_by_class_name('WinRatioTitle')
#%%
nb_parties,nb_victoires,nb_defaites = win_ratio.text.split(' ').join([i for i in win_ratio.text if i.isdigit()])

#%%
''.join([i for i in win_ratio.text if i.isdigit()])


#importing the needed modules
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats

#Importing the csv file
#since This  file is a txt file we need to convert it to csv .

#pf = pd.read_csv(r'C:\Users\carnivore\OneDrive\Documents\machine learning\stuscore.txt')
#pf.to_csv(r'C:\Users\carnivore\OneDrive\Documents\machine learning\stuscore.csv',index=None)

#storing the variable hours and Scores in a list .
df = pd.read_csv(r'C:\Users\carnivore\OneDrive\Documents\machine learning\stuscore.csv')
list_hours = []
list_scores = []
for i in df['Hours']:
    list_hours.append(i)
for j in df['Scores']:
    list_scores.append(j)

#Training the model with the test variables (Hours and Scores) and finding the relationship between them using Linear Regression
slope , intercept , r , p , std_err = stats.linregress(list_hours,list_scores)

def myfunc(list_hours):
    return slope * list_hours + intercept

mymodel = list(map(myfunc,list_hours))

plt.scatter(list_hours,list_scores)
plt.plot(list_hours,mymodel)
plt.show()

#checking the R-Square value
print("R-square value :- %f"%(r))

#asking for the user input for predicting the value
x = float(input("Enter the number of hours for which you want the predict the scores"))
score_main = myfunc(x)
print(score_main)

#score_main = myfunc(9.25)
#print("Score when the time of study is 9.25 hours is %f"%(score_main))


"""Implementation of LSTM with peephole connections."""
import torch
from torch import nn
from torch.autograd import Variable
from torch.nn import functional, init

class PLSTMCell(nn.Module):

    """LSTM cell with peephole connections"""

    def __init__(self, input_size, hidden_size, use_bias=True):
        """
        Most parts are copied from torch.nn.LSTMCell.
        """

        super(PLSTMCell, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.use_bias = use_bias
        self.weight_ih = nn.Parameter(
            torch.FloatTensor(input_size, 4 * hidden_size))
        self.weight_hh = nn.Parameter(
            torch.FloatTensor(hidden_size, 4 * hidden_size))

        # TODO: can make weight_ch a vector instead of matrix, then use hadamard in forward step
        self.weight_ch_i = nn.Parameter(torch.FloatTensor(hidden_size))
        self.weight_ch_f = nn.Parameter(torch.FloatTensor(hidden_size))
        self.weight_ch_o = nn.Parameter(torch.FloatTensor(hidden_size))

        if use_bias:
            self.bias = nn.Parameter(torch.FloatTensor(4 * hidden_size))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        """
        Initialize parameters following the way proposed in the paper.
        """

        init.orthogonal(self.weight_ih.data)
        weight_hh_data = torch.eye(self.hidden_size)
        weight_hh_data = weight_hh_data.repeat(1, 4)
        self.weight_hh.data.set_(weight_hh_data)

        # weight_ch_data = torch.eye(self.hidden_size)
        # weight_ch_data = weight_ch_data.repeat(1, 4)
        # self.weight_ch.data.set_(weight_ch_data)
        # init.constant(self.weight_ch_i, val=0)
        # init.constant(self.weight_ch_f, val=0)
        # init.constant(self.weight_ch_o, val=0)
        init.normal(self.weight_ch_i)
        init.normal(self.weight_ch_f)
        init.normal(self.weight_ch_o)

        # The bias is just set to zero vectors.
        if self.use_bias:
            init.constant(self.bias.data, val=0)

    def forward(self, input_, hx):
        """
        Args:
            input_: A (batch, input_size) tensor containing input
                features.
            hx: A tuple (h_0, c_0), which contains the initial hidden
                and cell state, where the size of both states is
                (batch, hidden_size).

        Returns:
            h_1, c_1: Tensors containing the next hidden and cell state.
        """

        h_0, c_0 = hx
        batch_size = h_0.size(0)
        bias_batch = (self.bias.unsqueeze(0)
                      .expand(batch_size, *self.bias.size()))
        wh_b = torch.addmm(bias_batch, h_0, self.weight_hh)
        wi = torch.mm(input_, self.weight_ih)
        # wc = torch.mm(c_0, self.weight_ch)

        f, i, o, g = torch.split(wh_b + wi,
                                 split_size=self.hidden_size, dim=1)
        weight_ch_i_batch = self.weight_ch_i.unsqueeze(0).expand(batch_size, *self.weight_ch_i.size())
        weight_ch_f_batch = self.weight_ch_f.unsqueeze(0).expand(batch_size, *self.weight_ch_f.size())
        weight_ch_o_batch = self.weight_ch_o.unsqueeze(0).expand(batch_size, *self.weight_ch_o.size())
        i = i + c_0 * weight_ch_i_batch
        f = f + c_0 * weight_ch_f_batch

        c_1 = torch.sigmoid(f)*c_0 + torch.sigmoid(i)*torch.tanh(g)

        o = o + c_1 * weight_ch_o_batch

        h_1 = torch.sigmoid(o) * torch.tanh(c_1)
        return h_1, c_1

    def __repr__(self):
        s = '{name}({input_size}, {hidden_size})'
        return s.format(name=self.__class__.__name__, **self.__dict__)

class LSTMCell(nn.Module):

    """LSTM cell with peephole connections"""

    def __init__(self, input_size, hidden_size, use_bias=True):
        """
        Most parts are copied from torch.nn.LSTMCell.
        """

        super(LSTMCell, self).__init__()
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.use_bias = use_bias
        self.weight_ih = nn.Parameter(
            torch.FloatTensor(input_size, 4 * hidden_size))
        self.weight_hh = nn.Parameter(
            torch.FloatTensor(hidden_size, 4 * hidden_size))
        if use_bias:
            self.bias = nn.Parameter(torch.FloatTensor(4 * hidden_size))
        else:
            self.register_parameter('bias', None)
        self.reset_parameters()

    def reset_parameters(self):
        """
        Initialize parameters following the way proposed in the paper.
        """

        init.orthogonal(self.weight_ih.data)
        weight_hh_data = torch.eye(self.hidden_size)
        weight_hh_data = weight_hh_data.repeat(1, 4)
        self.weight_hh.data.set_(weight_hh_data)
        # The bias is just set to zero vectors.
        if self.use_bias:
            init.constant(self.bias.data, val=0)

    def forward(self, input_, hx):
        """
        Args:
            input_: A (batch, input_size) tensor containing input
                features.
            hx: A tuple (h_0, c_0), which contains the initial hidden
                and cell state, where the size of both states is
                (batch, hidden_size).

        Returns:
            h_1, c_1: Tensors containing the next hidden and cell state.
        """

        h_0, c_0 = hx
        batch_size = h_0.size(0)
        bias_batch = (self.bias.unsqueeze(0)
                      .expand(batch_size, *self.bias.size()))
        wh_b = torch.addmm(bias_batch, h_0, self.weight_hh)
        wi = torch.mm(input_, self.weight_ih)
        f, i, o, g = torch.split(wh_b + wi,
                                 split_size=self.hidden_size, dim=1)
        c_1 = torch.sigmoid(f)*c_0 + torch.sigmoid(i)*torch.tanh(g)
        h_1 = torch.sigmoid(o) * torch.tanh(c_1)
        return h_1, c_1

    def __repr__(self):
        s = '{name}({input_size}, {hidden_size})'
        return s.format(name=self.__class__.__name__, **self.__dict__)

class PLSTM(nn.Module):

    """A module that runs multiple steps of LSTM."""

    def __init__(self, input_size, hidden_size, num_layers=1,
                 use_bias=True, batch_first=False, dropout=0, **kwargs):
        super(PLSTM, self).__init__()
        cell_class = PLSTMCell
        self.cell_class = cell_class
        self.input_size = input_size
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.use_bias = use_bias
        self.batch_first = batch_first
        self.dropout = dropout

        for layer in range(num_layers):
            layer_input_size = input_size if layer == 0 else hidden_size
            cell = cell_class(input_size=layer_input_size,
                              hidden_size=hidden_size,
                              **kwargs)
            setattr(self, 'cell_{}'.format(layer), cell)
        self.dropout_layer = nn.Dropout(dropout)
        self.reset_parameters()

    def get_cell(self, layer):
        return getattr(self, 'cell_{}'.format(layer))

    def reset_parameters(self):
        for layer in range(self.num_layers):
            cell = self.get_cell(layer)
            cell.reset_parameters()

    @staticmethod
    def _forward_rnn(cell, input_, length, hx):
        max_time = input_.size(0)
        output = []
        for time in range(max_time):
            h_next, c_next = cell(input_=input_[time], hx=hx)
            mask = (time < length).float().unsqueeze(1).expand_as(h_next)
            h_next = h_next*mask + hx[0]*(1 - mask)
            c_next = c_next*mask + hx[1]*(1 - mask)
            hx_next = (h_next, c_next)
            output.append(h_next)
            hx = hx_next
        output = torch.stack(output, 0)
        return output, hx

    def forward(self, input_, length=None, hx=None):
        if self.batch_first:
            input_ = input_.transpose(0, 1)
        max_time, batch_size, _ = input_.size()
        if length is None:
            length = Variable(torch.LongTensor([max_time] * batch_size))
            if input_.is_cuda:
                device = input_.get_device()
                length = length.cuda(device)
        if hx is None:
            hx = Variable(input_.data.new(batch_size, self.hidden_size).zero_())
            hx = (hx, hx) # TODO: shouldn't be two separate things??
        h_n = []
        c_n = []
        layer_output = None
        for layer in range(self.num_layers):
            cell = self.get_cell(layer)
            layer_output, (layer_h_n, layer_c_n) = PLSTM._forward_rnn(
                cell=cell, input_=input_, length=length, hx=hx)
            input_ = self.dropout_layer(layer_output)
            h_n.append(layer_h_n)
            c_n.append(layer_c_n)
        output = layer_output
        h_n = torch.stack(h_n, 0)
        c_n = torch.stack(c_n, 0)
        return output, (h_n, c_n)


import numpy as np
import math
import os
import sys
import matplotlib.pyplot as plt

lib_path = os.path.abspath('../../python/')
sys.path.append(lib_path)

from bbo.bbo_plotting import plotUpdate, plotExplorationCurve, plotLearningCurve
from bbo.bbo_plotting import saveUpdate, saveExplorationCurve, saveLearningCurve

from dmp_bbo.dmp_bbo_plotting import saveUpdateRollouts, setColor
from dmp_bbo.rollout import Rollout

def runOptimizationTask(task, task_solver, initial_distribution, updater, n_updates, n_samples_per_update,fig=None,directory=None):

    distribution = initial_distribution

    all_costs = []
    learning_curve = []
    exploration_curve = []

    if fig:
        ax_space   = fig.add_subplot(141)
        ax_rollout = fig.add_subplot(142)

    # Optimization loop
    for i_update in range(n_updates):

        # 0. Get cost of current distribution mean
        cost_vars_eval = task_solver.performRollout(distribution.mean)
        cost_eval = task.evaluateRollout(cost_vars_eval,distribution.mean)
        rollout_eval = Rollout(distribution.mean,cost_vars_eval,cost_eval)

        # 1. Sample from distribution
        samples = distribution.generateSamples(n_samples_per_update)

        # 2. Evaluate the samples
        costs = np.full(n_samples_per_update,0.0)
        rollouts = []
        for i_sample in range(n_samples_per_update):

            # 2A. Perform the rollouts
            cost_vars = task_solver.performRollout(samples[i_sample,:])

            # 2B. Evaluate the rollouts
            cur_costs = task.evaluateRollout(cost_vars,samples[i_sample,:])
            costs[i_sample] = cur_costs[0]

            rollouts.append(Rollout(samples[i_sample,:],cost_vars,cur_costs))

        # 3. Update parameters
        distribution_new, weights = updater.updateDistribution(distribution, samples, costs)

        # Bookkeeping and plotting
        # All the costs so far
        all_costs.extend(costs)
        # Update exploration curve
        cur_samples = i_update*n_samples_per_update
        cur_exploration = np.sqrt(distribution.maxEigenValue())
        exploration_curve.append([cur_samples,cur_exploration])
        # Update learning curve
        learning_curve.append([cur_samples])
        learning_curve[-1].extend(cost_eval)

        # Plot summary of this update
        if fig:
            highlight = (i_update==0)
            plotUpdate(distribution,cost_eval,samples,costs,weights,distribution_new,ax_space,highlight)
            # Try plotting rollout with task solver
            h = task_solver.plotRollout(rollout_eval.cost_vars,ax_rollout)
            if not h:
                # Task solver didn't know how to plot the rollout, try plotting
                # it with task instead
                h = task.plotRollout(rollout_eval.cost_vars,ax_rollout)
            if h:
                # If there is a handle, change its color depending on i_update
                setColor(h,i_update,n_updates)

        if directory:
            saveUpdateRollouts(directory, i_update, distribution, rollout_eval, rollouts, weights, distribution_new)

        # Distribution is new distribution
        distribution = distribution_new

    # Plot learning curve
    cost_labels = task.costLabels()
    if fig:
        plotExplorationCurve(exploration_curve,fig.add_subplot(143))
        plotLearningCurve(learning_curve,fig.add_subplot(144),all_costs,cost_labels)

    # Save learning curve to file, if necessary
    if directory:
        saveLearningCurve(directory,learning_curve)
        saveExplorationCurve(directory,exploration_curve)
        print('Saved results to "'+directory+'".')

    return learning_curve


# --coding: utf8--

import os

from django.core.management.base import BaseCommand

from templated_docs import fill_template

class DocumentGenerationCommand(BaseCommand):
    """
    A management command which generates a document using the context specified
    in the ``get_document_context()`` method.
    """

    def add_arguments(self, parser):
        parser.add_argument('template_name', type=str)
        parser.add_argument('--format', type=str, default='odt',
                            help='Convert a document to specified format')
        parser.add_argument('--output_file', type=str, default=None,
                            help='Put the result into this file.')

    def handle(self, template_name, **options):
        output_format = options['format']
        temp_name = fill_template(
            template_name,
            self.get_document_context(template_name, **options),
            output_format)

        out_file = options['output_file'] or temp_name
        if out_file != temp_name:
            os.rename(temp_name, out_file)
        print(out_file)

    def get_document_context(self, template_name, **options):
        """
        Fill the context for passing in the document template.
        """
        raise NotImplementedError(
            'subclasses of GenerateDocumentCommand must provide a '
            'get_document_context() method')


import urllib.request
import re
number = 8022
while True:
    url = 'http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing='+str(number)
    page = urllib\
        .request\
        .urlopen(url)\
        .read()\
        .decode("utf-8")
    number = re.search('is \d+', page).group(0)[3:]
    print(number)


import time
from copy import deepcopy
from dataclasses import dataclass

from utils import open_file

dir_path = __file__.split("/")
day = int(dir_path[-1][3:-3])
year = int(dir_path[-2])

TESTSTRING = """on x=10..12,y=10..12,z=10..12
on x=11..13,y=11..13,z=11..13
off x=9..11,y=9..11,z=9..11
on x=10..10,y=10..10,z=10..10"""
STRING = """on x=-20..26,y=-36..17,z=-47..7
on x=-20..33,y=-21..23,z=-26..28
on x=-22..28,y=-29..23,z=-38..16
on x=-46..7,y=-6..46,z=-50..-1
on x=-49..1,y=-3..46,z=-24..28
on x=2..47,y=-22..22,z=-23..27
on x=-27..23,y=-28..26,z=-21..29
on x=-39..5,y=-6..47,z=-3..44
on x=-30..21,y=-8..43,z=-13..34
on x=-22..26,y=-27..20,z=-29..19
off x=-48..-32,y=26..41,z=-47..-37
on x=-12..35,y=6..50,z=-50..-2
off x=-48..-32,y=-32..-16,z=-15..-5
on x=-18..26,y=-33..15,z=-7..46
off x=-40..-22,y=-38..-28,z=23..41
on x=-16..35,y=-41..10,z=-47..6
off x=-32..-23,y=11..30,z=-14..3
on x=-49..-5,y=-3..45,z=-29..18
off x=18..30,y=-20..-8,z=-3..13
on x=-41..9,y=-7..43,z=-33..15
on x=-54112..-39298,y=-85059..-49293,z=-27449..7877
on x=967..23432,y=45373..81175,z=27513..53682"""

ON = "on"
OFF = "off"

@dataclass
class Cuboid:
    switch: str
    x_min: int
    x_max: int
    y_min: int
    y_max: int
    z_min: int
    z_max: int

    def __and__(self, o):
        x_min = max(self.x_min, o.x_min)
        x_max = min(self.x_max, o.x_max)
        if x_max < x_min:
            return
        y_min = max(self.y_min, o.y_min)
        y_max = min(self.y_max, o.y_max)
        if y_max < y_min:
            return
        z_min = max(self.z_min, o.z_min)
        z_max = min(self.z_max, o.z_max)
        if z_max < z_min:
            return

        return Cuboid("", x_min, x_max, y_min, y_max, z_min, z_max)

    def unwrap(self):
        return {
            (x, y, z)
            for x in range(self.x_min, self.x_max + 1)
            for y in range(self.y_min, self.y_max + 1)
            for z in range(self.z_min, self.z_max + 1)
        }

    def within_bounds(self, x_l, x_u, y_l, y_u, z_l, z_u):
        return (
            self.x_min >= x_l
            and self.x_max <= x_u
            and self.y_min >= y_l
            and self.y_max <= y_u
            and self.z_min >= z_l
            and self.z_max <= z_u
        )

    def __sub__(self, o):
        ixn = self & o
        if not ixn:
            return [self]

        cuboids = []

        if self.x_min < ixn.x_min:
            cuboids.append(self.clone(x_max=ixn.x_min - 1))
        if self.x_max > ixn.x_max:
            cuboids.append(self.clone(x_min=ixn.x_max + 1))

        if self.y_min < ixn.y_min:
            cuboids.append(
                self.clone(x_min=ixn.x_min, x_max=ixn.x_max, y_max=ixn.y_min - 1)
            )
        if self.y_max > ixn.y_max:
            cuboids.append(
                self.clone(x_min=ixn.x_min, x_max=ixn.x_max, y_min=ixn.y_max + 1)
            )

        if self.z_min < ixn.z_min:
            cuboids.append(
                self.clone(
                    x_min=ixn.x_min,
                    x_max=ixn.x_max,
                    y_min=ixn.y_min,
                    y_max=ixn.y_max,
                    z_max=ixn.z_min - 1,
                )
            )
        if self.z_max > ixn.z_max:
            cuboids.append(
                self.clone(
                    x_min=ixn.x_min,
                    x_max=ixn.x_max,
                    y_min=ixn.y_min,
                    y_max=ixn.y_max,
                    z_min=ixn.z_max + 1,
                )
            )

        return cuboids

    def clone(self, **kwargs):
        c = deepcopy(self)
        for k, v in kwargs.items():
            c.__setattr__(k, v)
        return c

    @property
    def size(self):
        return self.x * self.y * self.z

    @property
    def x(self):
        return self.x_max - self.x_min + 1

    @property
    def y(self):
        return self.y_max - self.y_min + 1

    @property
    def z(self):
        return self.z_max - self.z_min + 1

def parse_input():
    f = open_file(day, year)
    # f = STRING.split('\n')
    # f = TESTSTRING.split('\n')
    inputs = []
    for s in f:
        switch, dims = s.strip().split(" ")
        x, y, z = [d[2:] for d in dims.split(",")]
        x_min, x_max = [int(i) for i in x.split("..")]
        y_min, y_max = [int(i) for i in y.split("..")]
        z_min, z_max = [int(i) for i in z.split("..")]
        inputs.append(Cuboid(switch, x_min, x_max, y_min, y_max, z_min, z_max))

    return inputs

def run1(data, bounds=None):
    on_cubes = []
    for cuboid in data:
        if bounds and not cuboid.within_bounds(*bounds):
            continue
        new_on_cubes = []
        for c in on_cubes:
            new_on_cubes.extend(c - cuboid)

        if cuboid.switch == ON:
            new_on_cubes.append(cuboid)

        on_cubes = new_on_cubes

    return sum(c.size for c in on_cubes)

def run2(data):
    pass

def test_subtract():
    a = Cuboid("on", 10, 15, 10, 15, 10, 15)
    b = Cuboid("off", 11, 12, 11, 12, 11, 12)

    c = a - b
    for i in c:
        print(i)
    assert len(c) == 6

if __name__ == "__main__":
    # test_subtract()
    a = time.time()
    inputs = parse_input()
    f = run1(inputs, (-50, 50, -50, 50, -50, 50))
    g = run1(inputs)
    print(f"Part 1: {f}")
    print(f"Part 2: {g}")
    print(f"Runtime: {round((time.time() - a)*1000,3)}ms")


import numpy as np
import pandas as pd
import plotly.graph_objs as go
from operator import itemgetter
from packages import df_no_transformation, clf_pipe, lime1, nbrs, x_test_transformed

informations_to_display = {
    'NAME_INCOME_TYPE' : 'Income type',
    'AMT_INCOME_TOTAL' : 'Amount of income',
    'AMT_CREDIT' : 'Amount of credit',
    'AMT_GOODS_PRICE' : 'Amount of goods price',
    'AMT_ANNUITY' : 'Amount of annuity',
    'INCOME_TO_CREDIT_RATIO' : 'Ratio Income / Credit',
    'CREDIT_TO_ANNUITY_RATIO' : 'Ratio Credit / Annuity',
    'INCOME_TO_FAMILYSIZE_RATIO' : 'Ratio Income / Family size',
    'NAME_FAMILY_STATUS' : 'Family status',
    'EXT_SOURCE_2' : 'EXT_SOURCE_2'
}

def isFloat(var):
    try:
        float(var)
        return True
    except ValueError:
        return False

def find_loan(id_loan):
    if id_loan in df_no_transformation['SK_ID_CURR']:
        return True
    return False

def get_info_loan(id_loan):
    result = dict()
    for col_name in list(informations_to_display):
        if isFloat(df_no_transformation[col_name][id_loan]):
            result[informations_to_display[col_name]] = round(float(df_no_transformation[col_name][id_loan]),6)
        else:
            result[informations_to_display[col_name]] = df_no_transformation[col_name][id_loan]
    return result

def get_solvency(id_loan):
    return clf_pipe[1].predict_proba(np.array(x_test_transformed.loc[id_loan]).reshape(1, -1)).flatten()

def get_feature_importance(id_loan):
    exp = lime1.explain_instance(x_test_transformed.loc[id_loan], clf_pipe[1].predict_proba, num_samples=100)
    indices, values = [], []
    for ind, val in sorted(exp.as_list(), key=itemgetter(1)):
        indices.append(ind)
        values.append(val)
    data = pd.DataFrame(values, columns=["values"], index=indices)
    del indices, values
    data["positive"] = data["values"]>0
    return data

def get_similarity(id_loan):
    indices_similary_loans = nbrs.kneighbors(np.array(x_test_transformed.loc[id_loan]).reshape(1, -1))[1].flatten()
    return df_no_transformation.iloc[indices_similary_loans].reset_index().drop(columns="index")

# [WIP]
def get_global_feature_importances(n_top_features=20):
    indices, values = [], []
    for ind, val in sorted(zip(
        clf_pipe[1].feature_importances_,
        x_test_transformed.columns),
        reverse=True)[0:  n_top_features] :
        indices.append(ind)
        values.append(val)
    data = pd.DataFrame(values, columns=["values"], index=indices)
    del indices, values
    return {
        'data': [go.Bar(
                    x=data.index,
                    y=data["values"],
                    orientation='h',
        )],

        'layout': go.Layout(
                            margin={'l': 300, 'b': 50, 't': 30, 'r': 30},
                            height=700,
                            width=1200
                            )
    }


# import csv

# with open('datacsv.csv', 'r') as x:
#     baca = csv.reader(x)
#     print(list(baca))

# x = ['no', 'nama']
# y = [12, 'andi']
# print(dict(zip(x, y)))

# with open('datacsv.csv', 'r') as x:
#     baca = csv.DictReader(x) #sensitive lower upper case
#     for i in baca:
#         print(dict(i))

import csv
data = [
    {'nama': 'andi', 'usia':22, 'kota':'jakarta'},
    {'nama': 'budi', 'usia':22, 'kota':'jakarta'},
    {'nama': 'caca', 'usia':22, 'kota':'jakarta'},
]

with open('datacsv.csv', 'w', newline='') as x:
    kolom = ['nama', 'usia', 'kota']
    a = csv.DictWriter(x, fieldnames=kolom, delimiter='i')
    a.writeheader()
    a.writerows(data)


import numpy as np

import torch
import torch.nn.functional as F
import torch.optim as optim
import torch.utils.data
import sys
from utils_copy_Luca import plot_2dmatrix, accumulate_values_by_region, compute_performance_metrics
from cy_utils import compute_map_with_new_labels, compute_accumulated_values_by_region, compute_disagg_weights, \
    set_value_for_each_region
from pix_transform_utils.utils import upsample

from pix_transform_CHE.pix_transform_net import PixTransformNet

DEFAULT_PARAMS = {'greyscale': False,
                  'channels': -1,
                  'bicubic_input': False,
                  'spatial_features_input': True,
                  'weights_regularizer': [0.0001, 0.001, 0.001],  # spatial color head
                  'loss': 'l1',

                  'optim': 'adam',
                  'lr': 0.0001,

                  'batch_size': 32,
                  'patch_size': 8,
                  'iteration': 1024 * 32,

                  'logstep': 512,
                  }

if 'ipykernel' in sys.modules:
    from tqdm import tqdm_notebook as tqdm
else:
    from tqdm import tqdm as tqdm

def PixTransform(source_img, guide_img, valid_mask=None, params=DEFAULT_PARAMS, validation_data=None, orig_guide_res=None):
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

    if len(guide_img.shape) < 3:
        guide_img = np.expand_dims(guide_img, 0)

    if params["channels"] > 0:
        guide_img = guide_img[0:params["channels"], :, :]

    if params['greyscale']:
        guide_img = np.mean(guide_img, axis=0, keepdims=True)

    if valid_mask is None:
        valid_mask = np.ones_like(guide_img)

    if validation_data is not None:
        validation_census, target_img, validation_regions, validation_ids, valid_validation_ids = validation_data
        num_validation_ids = np.unique(validation_regions).__len__()
        target_img[~valid_mask] = target_img[valid_mask].mean()
    source_img[~valid_mask] = source_img[valid_mask].mean()

    n_channels, hr_height, hr_width = guide_img.shape

    source_img = source_img.squeeze()
    lr_height, lr_width = source_img.shape

    assert (hr_height == lr_height)
    assert (hr_width == lr_width)
    # assert (hr_height % lr_height == 0)

    PS = params["patch_size"]
    Mh = hr_height // PS
    Mw = hr_width // PS

    # normalize guide and source\
    masked_guide_img = np.ma.array(guide_img, mask=~np.tile(valid_mask[None], (n_channels,1,1)))
    guide_img_mean = np.ma.getdata( masked_guide_img.mean((1,2)) )
    guide_img_std = np.ma.getdata( masked_guide_img.std((1,2)) )
    guide_img = ((guide_img.transpose((1,2,0)) - guide_img_mean ) / guide_img_std).transpose((2,0,1))

    maskedsource_img = np.ma.array(source_img, mask=~valid_mask)
    source_img_mean = np.ma.getdata( maskedsource_img.mean() )
    source_img_std = np.ma.getdata( maskedsource_img.std() )
    source_img = (source_img - source_img_mean) / source_img_std
    if target_img is not None:
        target_img = (target_img - source_img_mean) / source_img_std

    if params['spatial_features_input']:
        x = np.linspace(-0.5, 0.5, hr_height)
        y = np.linspace(-0.5, 0.5, hr_width)
        x_grid, y_grid = np.meshgrid(x, y, indexing='ij')

        x_grid = np.expand_dims(x_grid, axis=0)
        y_grid = np.expand_dims(y_grid, axis=0)

        guide_img = np.concatenate([guide_img, x_grid, y_grid], axis=0)
        n_channels += 2

    #### prepare_patches #########################################################################
    # guide_patches is M^2 x C x D x D
    # source_pixels is M^2 x 1

    # Move all important variable to pytorch
    guide_img = torch.from_numpy(guide_img).float()
    source_img = torch.from_numpy(source_img).float()
    valid_mask = torch.from_numpy(valid_mask).type(torch.BoolTensor)
    if target_img is not None:
        target_img = torch.from_numpy(target_img).float()#.to(device)
        validation_regions = torch.from_numpy(validation_regions.astype(np.int32))#.to(device)

    source_img_mean = torch.tensor(source_img_mean)
    source_img_std = torch.tensor(source_img_std)

    # Iterate throuh the image an cut out examples
    guide_patches = guide_img.unfold(1,PS,PS).unfold(2,PS,PS).contiguous().view(n_channels, -1, PS, PS ).permute(1,0,2,3).to(device)
    source_patches = source_img.unfold(0,PS,PS).unfold(1,PS,PS).contiguous().view(1, -1, PS, PS ).permute(1,0,2,3).to(device)
    mask_patches = valid_mask.unfold(0,PS,PS).unfold(1,PS,PS).type(torch.BoolTensor).contiguous().view(1, -1, PS, PS ).permute(1,0,2,3).to(device)

    train_data = torch.utils.data.TensorDataset(guide_patches, source_patches,mask_patches)
    train_loader = torch.utils.data.DataLoader(train_data, batch_size=params['batch_size'], shuffle=True)
    ###############################################################################################

    #### setup network ############################################################################
    mynet = PixTransformNet(channels_in=guide_img.shape[0],
                            weights_regularizer=params['weights_regularizer']).train().to(device)
    optimizer = optim.Adam(mynet.params_with_regularizer, lr=params['lr'])
    if params['loss'] == 'mse':
        myloss = torch.nn.MSELoss()
    elif params['loss'] == 'l1':
        myloss = torch.nn.L1Loss()
    else:
        print("unknown loss!")
        return
    ###############################################################################################

    epochs = params["epochs"] # params["batch_size"] * params["iteration"] // (guide_patches.shape[0])
    with tqdm(range(0, epochs), leave=True) as tnr:
        # tnr.set_description("epoch {}".format(0))
        if target_img is not None:
            tnr.set_postfix(R2=-99., MAEc=100000.)
        else:
            tnr.set_postfix(consistency=-1.)
        for epoch in tnr:
            for (x, y, mask) in (train_loader):
                if mask.sum()<1:
                    optimizer.zero_grad()
                    continue

                optimizer.zero_grad()

                y_pred = mynet(x)
                loss = myloss(y_pred[mask], y[mask])

                loss.backward()
                optimizer.step()

            if epoch % params['logstep'] == 0:
                with torch.no_grad():
                    mynet.eval()

                    # batchwise passing for whole image
                    predicted_target_img = mynet.forward_batchwise(guide_img.unsqueeze(0)).squeeze()

                    # replace masked values with the mean value, this way the artefacts when upsampling are mitigated
                    predicted_target_img[~valid_mask] = 0#predicted_target_img[valid_mask].mean()

                    if target_img is not None:

                        # revert normalization
                        abs_predicted_target_img =  (source_img_std * predicted_target_img) + source_img_mean
                        abs_source_img = (source_img_std * source_img) + source_img_mean
                        abs_target_img = (source_img_std * target_img) + source_img_mean

                        if params["predict_log_values"]:
                            abs_predicted_target_img = torch.exp(abs_predicted_target_img)
                            abs_source_img = torch.exp(abs_source_img)
                            abs_target_img = torch.exp(abs_target_img)

                        mse_predicted_source_img = F.mse_loss(abs_predicted_target_img[valid_mask], abs_source_img[valid_mask]).cpu().numpy()

                        # convert resolution back to feature resolution
                        if params["feature_downsampling"]!=1:
                            full_res_predicted_target_image = upsample(abs_predicted_target_img, params["feature_downsampling"], orig_guide_res=orig_guide_res)
                            full_upsamp_source_image = upsample(abs_source_img, params["feature_downsampling"], orig_guide_res=orig_guide_res)
                        else:
                            full_res_predicted_target_image = upsample(abs_predicted_target_img, params["feature_downsampling"], orig_guide_res=orig_guide_res)
                            full_upsamp_source_image = upsample(abs_source_img, params["feature_downsampling"], orig_guide_res=orig_guide_res)

                        # Aggregate by fine administrative boundary
                        agg_preds_arr = compute_accumulated_values_by_region(
                            validation_regions.numpy().astype(np.uint32),
                            full_res_predicted_target_image.cpu().numpy().astype(np.float32),
                            valid_validation_ids,
                            num_validation_ids
                        )
                        agg_preds = {id: agg_preds_arr[id] for id in validation_ids}
                        r2, mae, mse = compute_performance_metrics(agg_preds, validation_census)

                    if target_img is not None:
                        tnr.set_postfix(R2=r2, zMAEc=mae, zMSEs=mse_predicted_source_img) #MSEs=mse_predicted_source_img, MSEt=mse_predicted_target_img,

                    else:
                        tnr.set_postfix(MSEs=mse_predicted_source_img)
                    mynet.train()

    # compute final prediction, un-normalize, and back to numpy
    mynet.eval()
    predicted_target_img = mynet(guide_img.unsqueeze(0)).squeeze()
    predicted_target_img = source_img_mean + source_img_std * predicted_target_img
    predicted_target_img = predicted_target_img.cpu().detach().squeeze().numpy()

    return predicted_target_img


def minAndRemove(array):
    min = array[0]
    minIndex = 0

    for i in range(len(array)):
        if array[i] < min:
            min = array[i]
            minIndex = i

    return array.pop(minIndex)

def selectionSort(array):
    sorted = []

    while len(array) != 0:
        sorted.append(minAndRemove(array))

    return sorted

print(selectionSort([3,4,51,4,5,2,77]))


import time
from atlasbuggy import ThreadedStream
from atlasbuggy.subscriptions import *

from lms200 import OdometryMessage

class OdometryStream(ThreadedStream):
    def __init__(self, enabled=True, log_level=None):
        super(OdometryStream, self).__init__(enabled, log_level)

        self.kalman_tag = "kalman"
        self.kalman_feed = None
        self.require_subscription(self.kalman_tag, Update)

        self.angular_tag = "angular"
        self.angular_service_tag = "angular_v"
        self.angle_feed = None
        self.require_subscription(self.angular_tag, Update, service_tag=self.angular_service_tag)

        self.adjust_service(message_class=OdometryMessage)

    def take(self, subscriptions):
        self.kalman_feed = self.get_feed(self.kalman_tag)
        self.angle_feed = self.get_feed(self.angular_tag)

    def run(self):
        vx = None
        vy = None
        angular_v = None
        while self.is_running():
            if not self.kalman_feed.empty():
                position, velocity = self.kalman_feed.get()
                vx = velocity[0]
                vy = velocity[1]
                self.logger.debug("kalman data received: (vx=%0.4f, vy%0.4f)" % (vx, vy))
            if not self.angle_feed.empty():
                angular_v = self.angle_feed.get()
                self.logger.debug("angular v data received: %0.6f" % angular_v)

            if vx is not None and vy is not None and angular_v is not None:
                self.logger.debug("posting vx=%0.4f, vy=%0.4f, ang_v=%0.4f" % (vx, vy, angular_v))
                self.post(OdometryMessage(vx, vy, angular_v))
                vx = None
                vy = None
                angular_v = None

            time.sleep(0.001)


import os
import numpy as np
import parmed
from pyrosetta import Pose
from math import pi as _pi
import utils.protein_distance as pro_dist
import math
import time

class Charmm(object):
    def __init__(self, parameters_file_path, topology_file_path, save_path):
        self.params_dict = self.read_charmm_parameters_file(parameters_file_path)
        self.top_dict = self.read_charmm_topology_file(topology_file_path)

        # if isinstance(pdb, Pose):
        #     self.pdb = parmed.load_rosetta(pdb)
        # else:
        #     self.pdb = parmed.load_file(pdb)

        '''
        file_atoms = parmed.load_file(save_path).coordinates
        file_atoms = np.around(file_atoms, decimals=3).tolist()
        pose_atoms = parmed.load_rosetta(pose).coordinates
        pose_atoms = np.around(pose_atoms, decimals=3).tolist()

        self.convert_index = []
        for file_atoms_index, x in enumerate(file_atoms):
            if x in pose_atoms:
                self.convert_index.append(pose_atoms.index(x))
        '''
        self.pdb = parmed.load_file(save_path)

        self.res_name_list, self.res_index_list = self.get_residues_name_list(self.pdb)
        self.analysis_pdb_struct()

    def analysis_pdb_struct(self):
        self.bond_mat, charmm_name_atom_struct = self.get_bond_elem(self.pdb, self.top_dict, self.params_dict)
        atom_index_dict = {}
        for x in self.bond_mat:
            atom1, atom2 = list(x[0])
            if atom1 not in atom_index_dict:
                atom_index_dict[atom1] = [atom2]
            else:
                atom_index_dict[atom1].append(atom2)
            if atom2 not in atom_index_dict:
                atom_index_dict[atom2] = [atom1]
            else:
                atom_index_dict[atom2].append(atom1)
        self.angle_mat, self.angle_ub_mat = self.get_angle_elem(atom_index_dict, charmm_name_atom_struct, self.res_name_list,
                                                                self.top_dict, self.params_dict)
        self.dihedral_mat, self.improper_mat = self.get_dihedral_elem(atom_index_dict, self.res_index_list,
                                                                      charmm_name_atom_struct, self.res_name_list,
                                                                      self.top_dict, self.params_dict)

    def calculate_unit(self, calc_pdb):
        #atoms_coords = parmed.load_rosetta(calc_pdb).coordinates[self.convert_index]
        atoms_coords = parmed.load_file(calc_pdb).coordinates
        DEG_TO_RAD = np.pi / 180.0

        # bond unit
        energy_bond = 0
        for x in self.bond_mat:
            atoms = list(x[0])
            atom1_index, atom2_index = atoms
            atoms1_coord = atoms_coords[atom1_index]
            atoms2_coord = atoms_coords[atom2_index]
            d = np.sqrt(pro_dist.distance2(atoms1_coord, atoms2_coord))
            #atom1, atom2 = atoms_list[atom1_index], atoms_list[atom2_index]
            #bond = parmed.Bond(atom1, atom2)
            bond_type = x[1]
            #d = bond.measure()
            dx = d - bond_type[1]
            energy_item = bond_type[0] * dx * dx
            energy_bond = energy_bond + energy_item

        # angle unit
        energy_angle = 0
        for x in self.angle_mat:
            atoms = list(x[0])
            atom1_index, atom2_index, atom3_index = atoms
            atoms1_coord = atoms_coords[atom1_index]
            atoms2_coord = atoms_coords[atom2_index]
            atoms3_coord = atoms_coords[atom3_index]

            angle_type = x[1]
            #angle = parmed.Angle(atom1, atom2, atom3)
            #theta = angle.measure()
            theta = pro_dist.angle(atoms1_coord, atoms2_coord, atoms3_coord)
            d_theta = (theta - angle_type[1]) * DEG_TO_RAD
            energy_item = d_theta * d_theta * angle_type[0]
            energy_angle = energy_angle + energy_item

        # ub unit
        energy_ub = 0
        for x in self.angle_ub_mat:
            atoms = list(x[0])
            atom1_index, _, atom3_index = atoms
            atoms1_coord = atoms_coords[atom1_index]
            atoms3_coord = atoms_coords[atom3_index]
            ub_type = x[1]

            s = np.sqrt(pro_dist.distance2(atoms1_coord, atoms3_coord))

            #atom1, atom3 = atoms_list[atom1_index], atoms_list[atom3_index]

            #bond = parmed.Bond(atom1, atom3)

            #s = bond.measure()
            ds = (s - ub_type[1])
            energy_item = ds * ds * ub_type[0]
            energy_ub = energy_ub + energy_item

        # dihedral unit
        energy_dihedral = 0
        for x in self.dihedral_mat:
            atoms = list(x[0])
            atom1_index, atom2_index, atom3_index, atom4_index = atoms
            atoms1_coord = atoms_coords[atom1_index]
            atoms2_coord = atoms_coords[atom2_index]
            atoms3_coord = atoms_coords[atom3_index]
            atoms4_coord = atoms_coords[atom4_index]
            chi = pro_dist.dihedral(atoms1_coord, atoms2_coord, atoms3_coord, atoms4_coord) * DEG_TO_RAD

            #atom1, atom2, atom3, atom4 = atoms_list[atom1_index], atoms_list[atom2_index], atoms_list[atom3_index], \
            #                             atoms_list[atom4_index]
            #dihedral = parmed.Dihedral(atom1, atom2, atom3, atom4)
            #chi = dihedral.measure() * DEG_TO_RAD
            dihedral_type = x[1]
            if len(dihedral_type) == 1:
                energy_item = dihedral_type[0][0] * (
                            1 + np.cos(dihedral_type[0][1] * chi - dihedral_type[0][2] * DEG_TO_RAD))
            else:
                energy_item = 0
                for y in dihedral_type:
                    energy_item += y[0] * (1 + np.cos(y[1] * chi - y[2] * DEG_TO_RAD))
            energy_dihedral = energy_item + energy_dihedral

        # improper unit
        energy_improper = 0
        for x in self.improper_mat:
            atoms = list(x[0])
            atom1_index, atom2_index, atom3_index, atom4_index = atoms
            #atom1, atom2, atom3, atom4 = atoms_list[atom1_index], atoms_list[atom2_index], atoms_list[atom3_index], \
            #                             atoms_list[atom4_index]
            atoms1_coord = atoms_coords[atom1_index]
            atoms2_coord = atoms_coords[atom2_index]
            atoms3_coord = atoms_coords[atom3_index]
            atoms4_coord = atoms_coords[atom4_index]
            phi = pro_dist.dihedral(atoms1_coord, atoms2_coord, atoms3_coord, atoms4_coord)
            improper_type = x[1]

            #improper = parmed.Improper(atom1, atom2, atom3, atom4)
            #phi = improper.measure()
            dphi = (improper_type[1] - phi) * DEG_TO_RAD

            energy_item = improper_type[0] * dphi * dphi

            energy_improper = energy_item + energy_improper

        return energy_bond + energy_angle + energy_ub + energy_dihedral + energy_improper

    def get_charmm_name(self, atom_index, top_dict, charmm_name_atom_struct, res_name_list):
        atom_name_key = '{}_{}'.format(res_name_list[atom_index], charmm_name_atom_struct[atom_index])
        if atom_name_key in top_dict['atom']:
            atom_name = top_dict['atom'][atom_name_key]
        else:
            atom_name = charmm_name_atom_struct[atom_index]
        return atom_name

    def modify_atom_name(self, atom_name, res_name, first_flag):
        if res_name == 'ILE' and 'HD1' in atom_name:
            atom_name = atom_name.replace('HD1', 'HD')
        if res_name == 'ILE' and 'CD1' in atom_name:
            atom_name = atom_name.replace('CD1', 'CD')
        if res_name == 'SER' and 'HG' in atom_name:
            atom_name = atom_name.replace('HG', 'HG1')
        if atom_name == 'OXT':
            atom_name = 'OC'

        if atom_name[0] in ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0']:
            atom_name = atom_name[1:] + atom_name[0]
        else:
            atom_name = atom_name

        if first_flag:
            if atom_name == 'N':
                atom_name = 'NH3'
            if atom_name == 'H1':
                atom_name = 'HC'
            if atom_name == 'H2':
                atom_name = 'HC'
            if atom_name == 'H3':
                atom_name = 'HC'
        if atom_name == 'H':
            atom_name = 'HN'
        return atom_name

    def read_charmm_topology_file(self, file_path):
        res_name = ''
        charmm_bond = {}
        charmm_atom_dict = {}
        charmm_improper = {}

        for x in open(file_path, 'r'):
            if x == '\n':
                res_name = ''
            if '!' in x:
                line = x[:x.index('!')]
            else:
                line = x.replace('\n', '')
            if ('RESI' in x or 'PRES' in x) and res_name == '':
                res_name = line.split()[1]
                if res_name == 'HSP':
                    res_name = 'HIS'
                charmm_bond[res_name] = []
                charmm_improper[res_name] = []
            if res_name != '':

                if 'BOND' in line or 'DOUBLE' in line:
                    atom_pair = line.split()[1:]

                    for atom_index in range(int(len(atom_pair) / 2)):
                        charmm_bond[res_name].append({atom_pair[atom_index * 2], atom_pair[atom_index * 2 + 1]})
                if 'ATOM' in line:
                    atom_data = line.split()
                    pdb_atom_name = atom_data[1]
                    charmm_atom_type = atom_data[2]
                    dict_key = '{}_{}'.format(res_name, pdb_atom_name)
                    charmm_atom_dict[dict_key] = charmm_atom_type
                if 'IMPR' in line:
                    atom_pair = line.split()[1:]
                    for atom_index in range(int(len(atom_pair) / 4)):
                        charmm_improper[res_name].append([atom_pair[atom_index * 4], atom_pair[atom_index * 4 + 1],
                                                          atom_pair[atom_index * 4 + 2], atom_pair[atom_index * 4 + 3]])
        charmm_top = {
            'bond': charmm_bond,
            'atom': charmm_atom_dict,
            'impr': charmm_improper
        }

        return charmm_top

    def read_charmm_parameters_file(self, file_path):
        bond_block = False
        angle_block = False
        dihedrals_block = False
        improper_block = False
        cmap_block = False
        nonbonded_block = False

        LJ_params = {}
        LJ_params_14 = {}
        camp_params = {}
        improper_params = {}
        dihedral_params = {}
        angle_params = {}
        angle_UB_params = {}
        bond_params = {}

        for x in open(file_path, 'r'):

            if '!' in x:
                line = x[:x.index('!')]
            else:
                line = x.replace('\n', '')
            if x[0] == '*':
                continue
            if line == '':
                continue

            if 'BONDS' in line:
                bond_block = True
                continue
            if 'ANGLES' in line:
                angle_block = True
                continue
            if 'DIHEDRALS' in line:
                dihedrals_block = True
                continue
            if 'IMPROPER' in line:
                improper_block = True
                continue
            if 'CMAP' in line:
                cmap_block = True
                continue
            if 'NONBONDED' in line:
                nonbonded_block = True
                continue
            if 'NBFIX' in line:
                break

            if nonbonded_block:
                # V(Lennard - Jones) = Eps, i, j[(Rmin, i, j / ri, j) ** 12 - 2(Rmin, i, j / ri, j) ** 6]
                nonbonded_data = line.split()
                if nonbonded_data == []:
                    continue

                if len(nonbonded_data) == 4:
                    nonbonded_key = nonbonded_data[0]
                    ignored, epsilon, rmin = nonbonded_data[1:]
                    LJ_params[nonbonded_key] = np.array([ignored, epsilon, rmin], dtype=float)
                if len(nonbonded_data) == 7:
                    nonbonded_key = nonbonded_data[0]
                    ignored, epsilon, rmin, ignored_14, epsilon_14, rmin_14 = nonbonded_data[1:]
                    LJ_params_14[nonbonded_key] = np.array([ignored, epsilon, rmin, ignored_14, epsilon_14, rmin_14],
                                                           dtype=float)
            elif cmap_block:
                continue
            elif improper_block:
                # V(improper) = kpsi(psi - psi0)**2
                improper_data = line.split()
                if improper_data == []:
                    continue

                improper_key = '{} {} {} {}'.format(*improper_data[:4])
                kpsi, psi0 = improper_data[4], improper_data[6]
                improper_params[improper_key] = np.array([kpsi, psi0], dtype=float)
            elif dihedrals_block:
                # V(dihedral) = kchi(1 + cos(n(chi) - delta))
                dihedral_data = line.split()
                if dihedral_data == []:
                    continue

                dihedral_key = '{} {} {} {}'.format(*dihedral_data[:4])
                kchi, n, delta = dihedral_data[4:]
                if dihedral_key not in dihedral_params:
                    dihedral_params[dihedral_key] = []
                dihedral_params[dihedral_key].append(np.array([kchi, n, delta], dtype=float).tolist())
            elif angle_block:
                # V(angle) = Ktheta(Theta - Theta0) ** 2
                # V(Urey_Bradley) = Kub(S - S0) ** 2
                angle_data = line.split()
                if angle_data == []:
                    continue

                angle_key = '{} {} {}'.format(*angle_data[:3])
                ktheta, theta0 = angle_data[3: 5]
                angle_params[angle_key] = np.array([ktheta, theta0], dtype=float)
                if len(angle_data) == 7:
                    kub, s0 = angle_data[5:]
                    angle_UB_params[angle_key] = np.array([kub, s0], dtype=float)
            elif bond_block:
                # V(bond) = Kb(b - b0)**2
                bond_data = line.split()
                if bond_data == []:
                    continue

                bond_key = '{} {}'.format(*bond_data[:2])
                kb, b0 = bond_data[2:]
                bond_params[bond_key] = np.array([kb, b0], dtype=float)

        charmm_params = {
            'BOND': bond_params,
            'ANGLE': angle_params,
            'UB': angle_UB_params,
            'DIHEDRAL': dihedral_params,
            'IMPROPER': improper_params,
            'CMAP': camp_params,
            'LJ': LJ_params,
            'LJ_14': LJ_params_14
        }

        return charmm_params

    def get_residues_name_list(self, pdb):
        name_list = []
        index_list = []
        for index, res in enumerate(pdb.residues):
            name_list = name_list + [res.name] * len(res.atoms)
            index_list = index_list + [index] * len(res.atoms)
        return name_list, index_list

    def get_bond_elem(self, pdb, top_dict, param_dict):
        res_num = len(pdb.residues)
        first_res_flag = True
        last_res_flag = False
        count = 0

        charmm_name_atom_struct = []
        res_struct = []

        for res_index, res in enumerate(pdb.residues):
            if res_index == res_num - 1:
                last_res_flag = True

            res_atom_list = []
            res_modify_index = {}
            for atom_index, atom in enumerate(res):
                atom_name = self.modify_atom_name(atom.name, res.name, first_res_flag)
                if last_res_flag:
                    if atom_index == 2:
                        atom_name = 'CC'
                    if atom_index == 3:
                        atom_name = 'OC'

                key = '{}_{}'.format(res.name, atom_name)
                res_atom_list.append(atom_name)
                if key in top_dict['atom']:
                    atom.name = top_dict['atom'][key]
                else:
                    #print(res.name, atom_name)
                    atom.name = atom_name
            first_res_flag = False

            charmm_name_atom_struct = charmm_name_atom_struct + res_atom_list
            res_struct.append([res.name, len(res_atom_list)])

        # bond_mat = np.zeros((len(charmm_name_atom_struct), len(charmm_name_atom_struct)), dtype=bool)
        bond_mat = []

        atom_count = 0
        first_res_flag = True
        last_res_flag = False
        for res_index, res in enumerate(res_struct):
            res_name = res[0]
            res_bond_top = top_dict['bond'][res_name].copy()
            res_atom_list = charmm_name_atom_struct[atom_count: res[1] + atom_count]
            if res_index == res_num - 1:
                last_res_flag = True
                res_bond_top.remove({'C', '+N'})
                res_bond_top.append({'OC', 'CC'})
                res_bond_top.append({'CA', 'CC'})
            if first_res_flag:
                res_bond_top.append({'NH3', 'HC'})
            first_res_flag = False

            for bond in res_bond_top:
                atom1, atom2 = list(bond)
                if atom1 == '+N':
                    atom1_index = [atom_count + res[1]]
                    atom2_index = [i + atom_count for i, x in enumerate(res_atom_list) if x == atom2]
                    atom1 = 'N'
                    # print('atom1', atom1, atom2, atom_list[atom1_index[0]-1], atom_list[atom2_index[0]])
                    atom1_charmm_name_key = '{}_{}'.format(res_struct[res_index + 1][0], atom1)
                    atom2_charmm_name_key = '{}_{}'.format(res_name, atom2)

                elif atom2 == '+N':
                    atom2_index = [atom_count + res[1]]
                    atom1_index = [i + atom_count for i, x in enumerate(res_atom_list) if x == atom1]
                    atom2 = 'N'
                    # print('atom2', atom1, atom2, atom_list[atom1_index[0]], atom_list[atom2_index[0]-1])
                    atom1_charmm_name_key = '{}_{}'.format(res_name, atom1)
                    atom2_charmm_name_key = '{}_{}'.format(res_struct[res_index + 1][0], atom2)

                else:
                    atom1_index = [i + atom_count for i, x in enumerate(res_atom_list) if x == atom1]
                    atom2_index = [i + atom_count for i, x in enumerate(res_atom_list) if x == atom2]
                    atom1_charmm_name_key = '{}_{}'.format(res_name, atom1)
                    atom2_charmm_name_key = '{}_{}'.format(res_name, atom2)

                atom1_charmm_name = top_dict['atom'][atom1_charmm_name_key] if atom1_charmm_name_key in top_dict[
                    'atom'] else atom1
                atom2_charmm_name = top_dict['atom'][atom2_charmm_name_key] if atom2_charmm_name_key in top_dict[
                    'atom'] else atom2
                bond_name = '{} {}'.format(atom1_charmm_name, atom2_charmm_name)
                if bond_name in param_dict['BOND']:
                    bond_type = param_dict['BOND'][bond_name]
                else:
                    bond_name = '{} {}'.format(atom2_charmm_name, atom1_charmm_name)
                    if bond_name in param_dict['BOND']:
                        bond_type = param_dict['BOND'][bond_name]
                    else:
                        continue
                for atom1_seq_index in atom1_index:
                    for atom2_seq_index in atom2_index:
                        bond_mat.append([{atom1_seq_index, atom2_seq_index}, bond_type])
            atom_count = atom_count + res[1]

        first_bond_type = param_dict['BOND']['NH3 CT1']
        bond_mat.append(([{0, 1}, first_bond_type]))
        return bond_mat, charmm_name_atom_struct

    def get_angle_elem(self, atom_index_dict, charmm_name_atom_struct, res_name_list, top_dict, params_dict):
        angle_mat = []
        angle_ub_mat = []
        for atom_mid in atom_index_dict:
            atom_mid_charmm_name_key = '{}_{}'.format(res_name_list[atom_mid], charmm_name_atom_struct[atom_mid])
            if atom_mid_charmm_name_key in top_dict['atom']:
                atom_mid_charmm_name = top_dict['atom'][atom_mid_charmm_name_key]
            else:
                atom_mid_charmm_name = charmm_name_atom_struct[atom_mid]

            links_atoms = atom_index_dict[atom_mid]
            for atom_1 in links_atoms:
                atom_1_charmm_name_key = '{}_{}'.format(res_name_list[atom_1], charmm_name_atom_struct[atom_1])
                if atom_1_charmm_name_key in top_dict['atom']:
                    atom_1_charmm_name = top_dict['atom'][atom_1_charmm_name_key]
                else:
                    atom_1_charmm_name = charmm_name_atom_struct[atom_1]

                for atom_2 in links_atoms:
                    if atom_1 == atom_2:
                        continue
                    atom_2_charmm_name_key = '{}_{}'.format(res_name_list[atom_2], charmm_name_atom_struct[atom_2])
                    if atom_2_charmm_name_key in top_dict['atom']:
                        atom_2_charmm_name = top_dict['atom'][atom_2_charmm_name_key]
                    else:
                        atom_2_charmm_name = charmm_name_atom_struct[atom_2]
                    angle_key = '{} {} {}'.format(atom_1_charmm_name, atom_mid_charmm_name, atom_2_charmm_name)
                    if angle_key in params_dict['ANGLE']:
                        angle_type = params_dict['ANGLE'][angle_key]
                        if [[atom_2, atom_mid, atom_1], angle_type] not in angle_mat:
                            angle_mat.append([[atom_1, atom_mid, atom_2], angle_type])
                    if angle_key in params_dict['UB']:
                        angle_ub_type = params_dict['UB'][angle_key]
                        if [[atom_2, atom_mid, atom_1], angle_ub_type] not in angle_ub_mat:
                            angle_ub_mat.append([[atom_1, atom_mid, atom_2], angle_ub_type])

        angle_mat = self.get_unique_list(angle_mat)
        angle_ub_mat = self.get_unique_list(angle_ub_mat)

        return angle_mat, angle_ub_mat

    def get_dihedral_elem(self, atom_index_dict, res_index_list, charmm_name_atom_struct, res_name_list, top_dict,
                          params_dict):
        dihedral_mat = []
        dihedral_pos_mat = []
        improper_mat = []
        improper_mat_X = []
        for atom1_index in atom_index_dict:
            atom1_name = self.get_charmm_name(atom1_index, top_dict, charmm_name_atom_struct, res_name_list)
            atom1_link = atom_index_dict[atom1_index]
            for atom2_index in atom1_link:
                if atom1_index == atom2_index:
                    continue
                atom2_name = self.get_charmm_name(atom2_index, top_dict, charmm_name_atom_struct, res_name_list)
                atom2_link = atom_index_dict[atom2_index]
                for atom3_index in atom2_link:
                    if atom3_index in [atom1_index, atom2_index]:
                        continue
                    atom3_name = self.get_charmm_name(atom3_index, top_dict, charmm_name_atom_struct, res_name_list)
                    atom3_link = atom_index_dict[atom3_index]
                    for atom4_index in atom3_link:
                        if atom4_index in [atom1_index, atom2_index, atom3_index]:
                            continue
                        atom4_name = self.get_charmm_name(atom4_index, top_dict, charmm_name_atom_struct, res_name_list)
                        dihedral_key = '{} {} {} {}'.format(atom1_name, atom2_name, atom3_name, atom4_name)
                        if dihedral_key in params_dict['DIHEDRAL']:
                            dihedral_type = params_dict['DIHEDRAL'][dihedral_key]
                            if [atom4_index, atom3_index, atom2_index, atom1_index] not in dihedral_pos_mat:
                                dihedral_mat.append(
                                    [[atom1_index, atom2_index, atom3_index, atom4_index], dihedral_type])
                                dihedral_pos_mat.append([atom1_index, atom2_index, atom3_index, atom4_index])

        for atom1_index in atom_index_dict:
            atom1_name = self.get_charmm_name(atom1_index, top_dict, charmm_name_atom_struct, res_name_list)
            atom1_link = atom_index_dict[atom1_index]
            for atom2_index in atom1_link:
                if atom1_index == atom2_index:
                    continue
                atom2_name = self.get_charmm_name(atom2_index, top_dict, charmm_name_atom_struct, res_name_list)
                atom2_link = atom_index_dict[atom2_index]
                for atom3_index in atom2_link:
                    if atom3_index in [atom1_index, atom2_index]:
                        continue
                    atom3_name = self.get_charmm_name(atom3_index, top_dict, charmm_name_atom_struct, res_name_list)
                    atom3_link = atom_index_dict[atom3_index]
                    for atom4_index in atom3_link:
                        if atom4_index in [atom1_index, atom2_index, atom3_index]:
                            continue
                        atom4_name = self.get_charmm_name(atom4_index, top_dict, charmm_name_atom_struct, res_name_list)
                        dihedral_key_X_mid = 'X {} {} X'.format(atom2_name, atom3_name)
                        dihedral_key_X_side = '{} X X {}'.format(atom1_name, atom4_name)
                        if dihedral_key_X_mid in params_dict['DIHEDRAL']:
                            dihedral_type = params_dict['DIHEDRAL'][dihedral_key_X_mid]
                            if [atom1_index, atom2_index, atom3_index, atom4_index] not in dihedral_pos_mat and [
                                atom4_index, atom3_index, atom2_index, atom1_index] not in dihedral_pos_mat:
                                dihedral_mat.append(
                                    [[atom1_index, atom2_index, atom3_index, atom4_index], dihedral_type])
                                dihedral_pos_mat.append([atom1_index, atom2_index, atom3_index, atom4_index])
                                # print(res_name_list[atom1_index], dihedral_key_X_mid)
                        elif dihedral_key_X_side in params_dict['DIHEDRAL']:
                            dihedral_type = params_dict['DIHEDRAL'][dihedral_key_X_side]
                            if [atom1_index, atom2_index, atom3_index, atom4_index] not in dihedral_pos_mat and [
                                atom4_index, atom3_index, atom2_index, atom1_index] not in dihedral_pos_mat:
                                dihedral_mat.append(
                                    [[atom1_index, atom2_index, atom3_index, atom4_index], dihedral_type])
                                dihedral_pos_mat.append([atom1_index, atom2_index, atom3_index, atom4_index])

        improper_pos_noX_mat = {}
        improper_pos_X_mat = {}  # key is 'first_atom_index last_atom_index', value is {atom_mid_1, atom_mid_2}
        for atom3_index in atom_index_dict:
            # atom3 is first atom
            atom3_name = self.get_charmm_name(atom3_index, top_dict, charmm_name_atom_struct, res_name_list)
            atom3_link = atom_index_dict[atom3_index]

            for atom1_index in atom3_link:
                atom1_name = self.get_charmm_name(atom1_index, top_dict, charmm_name_atom_struct, res_name_list)
                for atom2_index in atom3_link:
                    if atom2_index in [atom1_index]:
                        continue
                    atom2_name = self.get_charmm_name(atom2_index, top_dict, charmm_name_atom_struct, res_name_list)
                    for atom4_index in atom3_link:
                        if atom4_index in [atom1_index, atom2_index]:
                            continue
                        atom4_name = self.get_charmm_name(atom4_index, top_dict, charmm_name_atom_struct, res_name_list)

                        # if atom1_name == atom2_name and atom2_name == atom4_name and atom1_name == 'NC2':
                        #    print(atom1_index, atom2_index, atom4_index)

                        improper_key = '{} {} {} {}'.format(atom3_name, atom1_name, atom2_name, atom4_name)
                        improper_key_reverse = '{} {} {} {}'.format(atom1_name, atom2_name, atom4_name, atom3_name)
                        improper_X_key_4 = '{} X X {}'.format(atom3_name, atom4_name)
                        improper_X_key_reverse_4 = '{} X X {}'.format(atom4_name, atom3_name)
                        improper_X_key_2 = '{} X X {}'.format(atom3_name, atom2_name)
                        improper_X_key_reverse_2 = '{} X X {}'.format(atom2_name, atom3_name)
                        improper_X_key_1 = '{} X X {}'.format(atom3_name, atom1_name)
                        improper_X_key_reverse_1 = '{} X X {}'.format(atom1_name, atom3_name)

                        X_mat_key_4 = '{} {}'.format(atom3_index, atom4_index)
                        X_mat_key_1 = '{} {}'.format(atom3_index, atom1_index)
                        X_mat_key_2 = '{} {}'.format(atom3_index, atom2_index)

                        if improper_key in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_key]
                            if atom3_index not in improper_pos_noX_mat:
                                improper_pos_noX_mat[atom3_index] = [{atom1_index, atom2_index, atom4_index}]
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])

                            elif {atom1_index, atom2_index, atom4_index} not in improper_pos_noX_mat[atom3_index]:
                                improper_pos_noX_mat[atom3_index].append({atom1_index, atom2_index, atom4_index})
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])

                        elif improper_key_reverse in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_key_reverse]
                            if atom3_index not in improper_pos_noX_mat:
                                improper_pos_noX_mat[atom3_index] = [{atom1_index, atom2_index, atom4_index}]
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])

                            elif {atom1_index, atom2_index, atom4_index} not in improper_pos_noX_mat[atom3_index]:
                                improper_pos_noX_mat[atom3_index].append({atom1_index, atom2_index, atom4_index})
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])

                        elif improper_X_key_4 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_4]
                            if X_mat_key_4 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_4] = [{atom1_index, atom2_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                            elif {atom1_index, atom2_index} not in improper_pos_X_mat[X_mat_key_4]:
                                improper_pos_X_mat[X_mat_key_4].append({atom1_index, atom2_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                        elif improper_X_key_reverse_4 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_reverse_4]
                            if X_mat_key_4 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_4] = [{atom1_index, atom2_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom4_index, atom1_index, atom2_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom4_index, atom2_index, atom1_index, atom3_index], improper_type])
                            elif {atom1_index, atom2_index} not in improper_pos_X_mat[X_mat_key_4]:
                                improper_pos_X_mat[X_mat_key_4].append({atom1_index, atom2_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom2_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom1_index, atom4_index], improper_type])
                                improper_mat_X.append(
                                    [[atom4_index, atom1_index, atom2_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom4_index, atom2_index, atom1_index, atom3_index], improper_type])

                        elif improper_X_key_2 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_2]
                            if X_mat_key_2 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_2] = [{atom1_index, atom4_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                            elif {atom1_index, atom4_index} not in improper_pos_X_mat[X_mat_key_2]:
                                improper_pos_X_mat[X_mat_key_2].append({atom1_index, atom4_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                        elif improper_X_key_reverse_2 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_reverse_2]
                            if X_mat_key_2 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_2] = [{atom1_index, atom4_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom2_index, atom1_index, atom4_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom2_index, atom4_index, atom1_index, atom3_index], improper_type])
                            elif {atom1_index, atom4_index} not in improper_pos_X_mat[X_mat_key_2]:
                                improper_pos_X_mat[X_mat_key_2].append({atom1_index, atom4_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom1_index, atom4_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom1_index, atom2_index], improper_type])
                                improper_mat_X.append(
                                    [[atom2_index, atom1_index, atom4_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom2_index, atom4_index, atom1_index, atom3_index], improper_type])

                        elif improper_X_key_1 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_1]
                            if X_mat_key_1 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_1] = [{atom4_index, atom2_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])

                            elif {atom4_index, atom2_index} not in improper_pos_X_mat[X_mat_key_1]:
                                improper_pos_X_mat[X_mat_key_1].append({atom4_index, atom2_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])

                        elif improper_X_key_reverse_1 in params_dict['IMPROPER']:
                            improper_type = params_dict['IMPROPER'][improper_X_key_reverse_1]
                            if X_mat_key_1 not in improper_pos_X_mat:
                                improper_pos_X_mat[X_mat_key_1] = [{atom4_index, atom2_index}]
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom1_index, atom4_index, atom2_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom1_index, atom2_index, atom4_index, atom3_index], improper_type])
                            elif {atom4_index, atom2_index} not in improper_pos_X_mat[X_mat_key_1]:
                                improper_pos_X_mat[X_mat_key_1].append({atom4_index, atom2_index})
                                improper_mat_X.append(
                                    [[atom3_index, atom4_index, atom2_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom3_index, atom2_index, atom4_index, atom1_index], improper_type])
                                improper_mat_X.append(
                                    [[atom1_index, atom4_index, atom2_index, atom3_index], improper_type])
                                improper_mat_X.append(
                                    [[atom1_index, atom2_index, atom4_index, atom3_index], improper_type])
        dihedral_mat = self.get_unique_list(dihedral_mat)

        # improper_mat = get_unique_list(improper_mat)
        def find_improper_mat(atoms):
            find_flag = False
            for x in improper_mat_:
                if [x[0][0], {x[0][1], x[0][2], x[0][3]}] == [atoms[0], {atoms[1], atoms[2], atoms[3]}]:
                    find_flag = True
                    break
            return find_flag

        improper_mat_ = []
        for x in improper_mat:
            atoms = x[0]
            atom1_name, atom2_name = charmm_name_atom_struct[atoms[0]], charmm_name_atom_struct[atoms[1]]
            atom3_name, atom4_name = charmm_name_atom_struct[atoms[2]], charmm_name_atom_struct[atoms[3]]
            atoms_name = [atom1_name, atom2_name, atom3_name, atom4_name]
            atom1_res_index, atom2_res_index = res_index_list[atoms[0]], res_index_list[atoms[1]]
            atom3_res_index, atom4_res_index = res_index_list[atoms[2]], res_index_list[atoms[3]]

            res_index_min = int(np.argmin([atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index]))
            res_min = min(atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index)
            min_flag = [atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index].count(res_min) == 1
            res_index_max = int(np.argmax([atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index]))
            res_max = max(atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index)
            max_flag = [atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index].count(res_max) == 1
            if max_flag:
                res_name = self.pdb.residues[res_max - 1].name
            elif min_flag:
                res_name = self.pdb.residues[res_min + 1].name
            else:
                res_name = self.pdb.residues[res_min].name

            if res_min == res_max:
                # res_name = res_name_list[res_min]
                if res_min == len(self.pdb.residues) - 1:
                    src_improper_list = top_dict['impr'][res_name].copy()
                    src_improper_list.append(['CC', 'OC', 'CA', 'OC'])
                else:
                    src_improper_list = top_dict['impr'][res_name]
                src_improper_list_ = [[z[0], {z[1], z[2], z[3]}] for z in src_improper_list]
                if [atoms_name[0], atoms_name[1], atoms_name[2], atoms_name[3]] in src_improper_list_:
                    if not find_improper_mat(atoms):
                        improper_mat_.append(x)
                #    print(res_name, res_min, atoms_name, src_improper_list)
                # else:
                #    print(res_name, res_min, atoms_name, src_improper_list)
            else:

                if min_flag and atoms_name[res_index_min] == 'C':
                    # res_name = res_name_list[res_min + 1]
                    atoms_name[res_index_min] = '-C'
                if max_flag and atoms_name[res_index_max] == 'N':
                    # res_name = res_name_list[res_max - 1]
                    atoms_name[res_index_max] = '+N'
                src_improper_list = top_dict['impr'][res_name]
                src_improper_list_ = [[z[0], {z[1], z[2], z[3]}] for z in src_improper_list]
                if [atoms_name[0], atoms_name[1], atoms_name[2], atoms_name[3]] in src_improper_list_:
                    if not find_improper_mat(atoms):
                        improper_mat_.append(x)
                #   print(res_name, res_max, atoms_name, src_improper_list)
                # else:
                #    print(res_name, res_min, atoms_name, src_improper_list)

        for x in improper_mat_X:
            atoms = x[0]
            improper_type = x[1]
            atoms_reverse = atoms.copy()
            atoms_reverse[1], atoms_reverse[2] = atoms_reverse[2], atoms_reverse[1]
            atom1_name, atom2_name = charmm_name_atom_struct[atoms[0]], charmm_name_atom_struct[atoms[1]]
            atom3_name, atom4_name = charmm_name_atom_struct[atoms[2]], charmm_name_atom_struct[atoms[3]]
            atoms_name = [atom1_name, atom2_name, atom3_name, atom4_name]
            atom1_res_index, atom2_res_index = res_index_list[atoms[0]], res_index_list[atoms[1]]
            atom3_res_index, atom4_res_index = res_index_list[atoms[2]], res_index_list[atoms[3]]

            res_index_min = int(np.argmin([atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index]))
            res_min = min(atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index)
            min_flag = [atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index].count(res_min) == 1
            res_index_max = int(np.argmax([atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index]))
            res_max = max(atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index)
            max_flag = [atom1_res_index, atom2_res_index, atom3_res_index, atom4_res_index].count(res_max) == 1
            if max_flag:
                res_name = self.pdb.residues[res_max - 1].name
            elif min_flag:
                res_name = self.pdb.residues[res_min + 1].name
            else:
                res_name = self.pdb.residues[res_min].name

            if res_min == res_max:
                # res_name = res_name_list[res_min]
                if res_min == len(self.pdb.residues) - 1:
                    src_improper_list = top_dict['impr'][res_name].copy()
                    src_improper_list.append(['CC', 'OC', 'CA', 'OC'])
                else:
                    src_improper_list = top_dict['impr'][res_name]
                src_improper_list_ = [[z[0], {z[1], z[2]}, z[3]] for z in src_improper_list]
                if [atoms_name[0], {atoms_name[1], atoms_name[2]}, atoms_name[3]] in src_improper_list_:
                    if not find_improper_mat(atoms):
                        improper_mat_.append(x)
                #    print(res_name, res_min, atoms_name, src_improper_list)
                # else:
                #    print(res_name, res_min, atoms_name, src_improper_list)
            else:

                if min_flag and atoms_name[res_index_min] == 'C':
                    # res_name = res_name_list[res_min + 1]
                    atoms_name[res_index_min] = '-C'
                if max_flag and atoms_name[res_index_max] == 'N':
                    # res_name = res_name_list[res_max - 1]
                    atoms_name[res_index_max] = '+N'
                src_improper_list = top_dict['impr'][res_name]
                src_improper_list_ = [[z[0], {z[1], z[2]}, z[3]] for z in src_improper_list]
                if [atoms_name[0], {atoms_name[1], atoms_name[2]}, atoms_name[3]] in src_improper_list_:
                    # if not find_improper_mat(atoms):
                    improper_mat_.append(x)
                # else:
                #    print(res_name, res_min, atoms_name, src_improper_list)

        # improper_mat =  get_unique_list(improper_mat_)
        improper_mat = improper_mat_

        return dihedral_mat, improper_mat

    def get_unique_list(self, src_list):
        unique_list = []
        for x in src_list:
            if x not in unique_list:
                unique_list.append(x)
        return unique_list


import ast
from flask import g

def menu_unique_constraint_valid(objdbca, objtable, option):
    retBool = True
    msg = ''

    entry_parameter = option.get('entry_parameter').get('parameter')
    menu_name = entry_parameter.get("menu_name")
    unique_constraint_item = entry_parameter.get("unique_constraint_item")
    cmd_type = option.get('cmd_type')

    # 廃止/復活の場合はチェックをスキップする
    if cmd_type == "Discard" or cmd_type == "Restore":
        return retBool, msg, option

    # 値をリスト形式にフォーマット
    try:
        unique_constraint_item = ast.literal_eval(unique_constraint_item)
    except Exception:
        retBool = False
        msg = g.appmsg.get_api_message("MSG-20246", [])
        return retBool, msg, option

    # 指定されたメニューからメニュー項目名(REST)を取得
    where_str = "WHERE DISUSE_FLAG = '0' AND MENU_CREATE_ID = %s"
    return_values = objdbca.table_select('T_MENU_COLUMN', where_str, [menu_name])
    item_name_rest = []
    if 0 < len(return_values):
        for data in return_values:
            try:
                item_name_rest.append(data.get('COLUMN_NAME_REST'))
            except Exception:
                retBool = False
                msg = g.appmsg.get_api_message("MSG-20247", [])
    else:
        retBool = False
        msg = g.appmsg.get_api_message("MSG-20248", [])
    # リスト型かどうか判定
    if retBool:
        if isinstance(unique_constraint_item, list):
            for item_list in unique_constraint_item:
                if isinstance(item_list, list):
                    for item in item_list:
                        if item not in item_name_rest:
                            retBool = False
                            msg = g.appmsg.get_api_message("MSG-20249", [item])
                            return retBool, msg, option
                else:
                    retBool = False
                    msg = g.appmsg.get_api_message("MSG-20250", [])
        else:
            retBool = False
            msg = g.appmsg.get_api_message("MSG-20251", [])

    # unique_constraint_itemの重複をチェック
    seen = []
    check_unique_list = [x for x in unique_constraint_item if x not in seen and not seen.append(x)]
    if len(unique_constraint_item) != len(check_unique_list):
        seen = []
        duplication_target = [x for x in unique_constraint_item if not seen.append(x) and seen.count(x) == 2]
        retBool = False
        msg = g.appmsg.get_api_message("MSG-20262", [str(duplication_target)])

    return retBool, msg, option