"""
Linear layer with fused activation with PyTorch autodiff support.
"""

from typing import Optional, Tuple

import torch
from torch import Tensor
from torch import nn
from triton import cdiv

from .act_kernels import act_func_backward_kernel
from .linear_kernels import linear_forward_kernel
from .types import Context, Device

class LinearAutoGrad(torch.autograd.Function):
    """
    Autodiff for linear layer.
    """
    @staticmethod
    def forward(
        ctx: Context,
        input: Tensor,
        weight: Tensor,
        bias: Optional[Tensor] = None,
        act_func: Optional[str] = None,
        ) -> Tensor:
        """
        Linearly transforms the input using weights, optionally adding bias
        and fusing an activation function.

        Args:
            input: Input to transform.
                Must be of shape [..., in_feat_dim].
            weight: Weights input is transformed by.
                Must be of shape [in_feat_dim, out_feat_dim].
            bias: Optional additive bias vector, with None for no bias.
                If provided, must be of shape [out_feat_dim].
            act_func: Name of activation function to apply, with None for identity.
                Options are 'sigmoid', 'tanh', 'relu', 'gelu', and 'silu'.

        Returns:
            Input linearly transformed, potentially with added biased and
            fused activation.
        """
        assert weight.ndim == 2, \
            f'Weights must be 2D, received shape {weight.shape}'
        assert bias is None or bias.ndim == 1, \
            f'Bias must be 1D, received shape {bias.shape}'

        assert input.shape[-1] == weight.shape[0], \
            f'Incompatible input ({input.shape}) and weights ({weight.shape}) shape'
        assert bias is None or weight.shape[1] == bias.shape[0], \
            f'Incompatible weights ({weight.shape}) and bias ({bias.shape}) shape'

        flattened_input = input.flatten(0, -2)
        batch_dim, in_feat_dim = flattened_input.shape
        _, out_feat_dim = weight.shape

        requires_grad = (input.requires_grad or
                         weight.requires_grad or
                         (bias is not None and bias.requires_grad))
        save_pre_act = requires_grad and act_func is not None

        output = torch.empty((batch_dim, out_feat_dim),
                             device=input.device,
                             dtype=input.dtype)
        pre_act = torch.empty_like(output) if save_pre_act else output

        # Launches a 1D grid, where each program outputs blocks of
        # BLOCK_SIZE_BATCH rows and BLOCK_SIZE_OUT_FEAT columns.
        grid = lambda META: (cdiv(batch_dim, META['BLOCK_SIZE_BATCH']) *
                             cdiv(out_feat_dim, META['BLOCK_SIZE_OUT_FEAT']),)
        linear_forward_kernel[grid](flattened_input, weight,
                                    input if bias is None else bias,
                                    pre_act, output,
                                    batch_dim, in_feat_dim, out_feat_dim,
                                    *flattened_input.stride(), *weight.stride(),
                                    *pre_act.stride(), *output.stride(),
                                    add_bias=bias is not None, act_func=act_func,
                                    save_pre_act=save_pre_act)

        ctx.act_func = act_func
        ctx.bias_requires_grad = False if bias is None else bias.requires_grad
        if requires_grad:
            ctx.save_for_backward(input, pre_act if save_pre_act else None, weight)

        return output.view(*input.shape[:-1], out_feat_dim)

    @staticmethod
    def backward(
        ctx: Context,
        output_grad: Tensor,
        ) -> Tuple[Optional[Tensor], ...]:
        """
        Calculates the input gradient of the linear layer.

        Args:
            ctx: Context containing stored variables.
            output_grad: Output gradients.
                Must be the same shape as the output.

        Returns:
            Input gradient of the linear layer.
        """
        input, pre_act, weight = ctx.saved_tensors

        output_grad = output_grad.flatten(0, -2)
        flattened_input = input.flatten(0, -2)

        batch_dim, _ = flattened_input.shape
        _, out_feat_dim = weight.shape

        if ctx.act_func is None:
            pre_act_grad = output_grad

        else:
            size = batch_dim * out_feat_dim
            pre_act_grad = torch.empty(size, dtype=pre_act.dtype,
                                       device=pre_act.device)

            # Launches 1D grid where each program operates over
            # BLOCK_SIZE elements.
            grid = lambda META: (cdiv(size, META['BLOCK_SIZE']),)
            act_func_backward_kernel[grid](output_grad, pre_act, pre_act_grad,
                                           size, ctx.act_func)

            pre_act_grad = pre_act_grad.view_as(pre_act)

        # Using PyTorch's matmul, but linear_forward_kernel
        # could have also been used.
        input_grad = pre_act_grad @ weight.T if input.requires_grad else None
        weight_grad = (flattened_input.T @ pre_act_grad
                       if weight.requires_grad else None)
        bias_grad = pre_act_grad.sum(dim=0) if ctx.bias_requires_grad else None

        # Pads output with None because a gradient is necessary for
        # all input arguments.
        return (input_grad.view_as(input) if input_grad is not None else None,
                weight_grad, bias_grad, None)

class Linear(nn.Linear):
    """
    Linearly transforms the input using weights, optionally adding bias
    and fusing an activation function.
    See also base class.

    Note: Unlike PyTorch's linear layer, the weight matrix in this module is
    of shape [in_features, out_features] instead of [out_features, in_features].
    This may cause unexpected issues when manipulating the weights (e.g., porting
    parameters, initializing them, and so forth).

    Args:
        in_features: Number of input features.
        out_features: Number of output features.
        bias: Flag for additive bias.
        act_func: Name of activation function to apply, with None for identity.
            Options are 'sigmoid', 'tanh', 'relu', 'gelu', and 'silu'.
        device: Device to use. Only CUDA is supported.
        dtype: Dtype of layer. Only float32 is supported.

    Raises:
        RuntimeError: 1. A device other than CUDA was passed.
                      2. A dtype other than float32 was passed.
    """
    def __init__(
        self,
        in_features: int,
        out_features: int,
        bias: bool = True,
        act_func: Optional[str] = None,
        device: Device = 'cuda',
        dtype: torch.dtype = torch.float32,
        ) -> None:
        if 'cuda' not in str(device):
            raise RuntimeError('Linear layer only supports CUDA devices.')

        if dtype is not torch.float32:
            raise RuntimeError('Linear layer only supports float32 dtype.')

        super().__init__(in_features, out_features, bias, device, dtype)
        self.weight = nn.Parameter(self.weight.T.contiguous())
        self.act_func = act_func

    def forward(self, input: Tensor) -> Tensor:
        return LinearAutoGrad.apply(input, self.weight, self.bias,
                                    self.act_func)


#-----------------------------------------------------------------
# Working with psycopg2
#-----------------------------------------------------------------

import psycopg2
import sys
from prettytable import PrettyTable

def heading(str):
    print('-'*60)
    print("** %s:" % (str,))
    print('-'*60, '\n')

SHOW_CMD = True
def print_cmd(cmd):
    if SHOW_CMD:
        print(cmd.decode('utf-8'))

def print_table(rows, columnsArray):
    table = PrettyTable(columnsArray)
    for row in rows:
        table.add_row(row)
    print(table)
    print()

#-----------------------------------------------------------------
# US1: As a Personal User, I want to create a post so that I can share my
# experiences with friends and family.
#-----------------------------------------------------------------
def show_US1_menu():
    heading("US1 - As a Personal User, I want \
    to create a post so that I can share my experiences with friends and family")

    post_type = "picture" # hardcode post type for demonstration
    caption = "yay!" # hardcode new caption for demonstration
    posted_by = 2 # hardcode posted by user id for demonstration
    US1(post_type=post_type, caption=caption, uid=posted_by)

def US1(post_type, caption, uid):
    tmpl = '''
        INSERT INTO Posts(type, caption, uid)
        VALUES (%s, %s, %s);
        SELECT type, caption, uid
        FROM Posts;
    '''
    cmd = cur.mogrify(tmpl, (post_type, caption, uid))
    print_cmd(cmd)
    cur.execute(cmd)
    rows = cur.fetchall()

    print_table(rows, ['type','caption','uid'])

    print("\nUS1: As a Personal User, I want to create a post so that I can share my experiences with friends and family.")
    print("\nDescription: This query is supposed to insert a new post created by a user into the Posts table. We insert the post type, caption and who it was posted by. The post_id is automatically generated since it is a serial type. In this way, the query enables users to share their experiences through posts.\n")

    # ^ Don't print rows unless we do a select

if __name__ == '__main__':
    try:
        # default database and user
        db, user = 'instagram', 'isdb'
        # you may have to adjust the user
        # python a4-socnet-sraja.py a4_socnet postgres
        if len(sys.argv) >= 2:
            db = sys.argv[1]
        if len(sys.argv) >= 3:
            user = sys.argv[2]
        # by assigning to conn and cur here they become
        # global variables.  Hence they are not passed
        # into the various SQL interface functions
        conn = psycopg2.connect(database=db, user=user)
        conn.autocommit = True
        cur = conn.cursor()

        show_US1_menu() #show user story 1 menu

    except psycopg2.Error as e:
        print("Unable to open connection: %s" % (e,))


import random
import time

t0 = 1
t1 = 100
d = 1

a = [str(x) for x in range(t0,t1 + 1, d)]# a 为列表
b = []
nn = [0]
def choice(event):
	time.sleep(1)
	rm = random.choice(a)
	a.remove(rm)#无放回抽签
	b.append(rm)

	if ((len(b)-nn[0])%10 == 0):
		b.append('\n')
		nn[0] += 1
	bstr = ', '.join(b)
#随机输出数字
	ctn = wx.TextCtrl(win,-1,str(rm),pos = (95,225),size = (240, 160),style = (wx.TE_CENTER |wx.TE_READONLY))
	font = wx.Font(100,wx.DECORATIVE, wx.ITALIC, wx.NORMAL)
	ctn.SetFont(font)

#已输出的
	dtn = wx.TextCtrl(win,-1,bstr,pos = (95,85),size = (240, 120),style = (wx.TE_LEFT |wx.TE_READONLY))
	font = wx.Font(15,wx.DECORATIVE, wx.ITALIC, wx.NORMAL)
	dtn.SetFont(font)

#mac
#conda install python.app
#pythonw *.py

#linux
#conda install wxpython
#python *.py

import wx
app = wx.App()
win = wx.Frame(None, title = '抽奖器',size = (450,500))

win.Show()

btn = wx.Button(win, label = '抽奖', pos = (165,45), size = (80, 25))
btn.Bind(wx.EVT_BUTTON, choice)

codes = 'ctn = wx.TextCtrl(win,-1,"",pos = (95,225),size = (240, 160),style = (wx.TE_CENTER |wx.TE_READONLY));font = wx.Font(100,wx.DECORATIVE, wx.ITALIC, wx.NORMAL);ctn.SetFont(font)'
exec(codes)

codes = 'dtn = wx.TextCtrl(win,-1,"",pos = (95,85),size = (240, 120),style = (wx.TE_LEFT |wx.TE_READONLY));font = wx.Font(15,wx.DECORATIVE, wx.ITALIC, wx.NORMAL);dtn.SetFont(font)'
exec(codes)

app.MainLoop()


import logging
import threading
import timeit
import os
import psutil
import inspect
from pyramid.threadlocal import get_current_request

lock = threading.Lock()

"""Provides an ``includeme`` function that lets developers configure the
  package to be part of their Pyramid application with::

      config.include('pyramid_straw')

"""

logger = logging.getLogger(__name__)

def re_query(query, params):
    """Replace query params with actual parameters."""
    if type(params) is dict:
        for k, v in params.items():
            query = query.replace('%({})s'.format(k), str(v))
    if type(params) is tuple:
        for v in params:
            query = query.replace('?', str(v), 1)
    return query

def sizeof_fmt(num, suffix='B'):
    """Format the bytes."""

    for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
        if abs(num) < 1024.0:
            return '{:.1f}{}{}'.format(num, unit, suffix)
        num /= 1024.0
    return '{:.1f}{}{}'.format(num, 'Yi', suffix)

def humanize(lines, only_paths):
    """Yield dict about location of calling function"""
    for x in lines:
        (f, filename, at, function, l, i) = x
        # Will skip any lines that are not included
        if not any([x in filename for x in only_paths]):
            continue

        yield {
            'filename': filename.strip(),
            'at': at,
            'function': function.strip()
        }

def get_used_memory():
    """Gets the non-shared memory about process."""
    process = psutil.Process(os.getpid())
    return process.memory_info().rss

def to_toolbar(data):
    """Inject analyzed data in request."""
    request = get_current_request()

    if not request and not getattr(request, 'straw_data', None):
        return

    with lock:
        request.straw_data.append(data)

def includeme(config):
    settings = config.registry.settings

    # Custom sender for reports, needs to be callable
    dsn_factory = settings.get('pyramid_straw.report_hook', None)

    # Enable or disable saving data to request based on debugtoolbar presence
    debug_toolbar = len(settings.get('debugtoolbar.panels', []))

    # Control the inspection of query
    disable_inspect = settings.get('pyramid_straw.disable_inspect', False)

    # For example src/my_project_name/models
    only_paths = settings.get('pyramid_straw.only_paths', [])

    try:
        from sqlalchemy import event
        from sqlalchemy.engine.base import Engine

        def _before(conn, cursor, query, params, context, execmany):
            setattr(context, 'straw_start_timer', timeit.default_timer())
            setattr(context, 'straw_start_memory', get_used_memory())

        def _after(conn, cursor, query, params, context, execmany):
            stop_timer = timeit.default_timer()
            duration = (stop_timer - context.straw_start_timer) * 1000
            memory = get_used_memory() - context.straw_start_memory
            data = {
                'type': 'cursor',
                'duration': duration,
                'memory': sizeof_fmt(memory),
                'query': re_query(query, params),
                'parameters': params,
                'lines': []
            }
            if not disable_inspect:
                lines = inspect.getouterframes(inspect.currentframe())
                # NOTE: introspection takes a bit so it will add about .3-.5ms
                # to query timing
                data['lines'] = humanize(lines, only_paths)
            if dsn_factory and callable(dsn_factory):
                dsn_factory(data)
            if debug_toolbar:
                to_toolbar(data)
        if dsn_factory or debug_toolbar:
            event.listen(Engine, 'before_cursor_execute', _before)
            event.listen(Engine, 'after_cursor_execute', _after)

            # Flow events
            event.listen(Engine, 'begin', lambda x: to_toolbar({'type': 'begin'}))  # noqa
            event.listen(Engine, 'commit', lambda x: to_toolbar({'type': 'commit'}))  # noqa
            event.listen(Engine, 'rollback', lambda x: to_toolbar({'type': 'rollback'}))  # noqa

    except ImportError:
        logger.warning('SQLAlchemy is not installed.')


# ------------------------------------------------------------------------------
# Part of implementation is adopted from CenterNet,
# made publicly available under the MIT License at https://github.com/xingyizhou/CenterNet.git
# ------------------------------------------------------------------------------
import warnings
from typing import Dict, List, Tuple, Union

import cv2
import numpy as np

# suppress warnings
warnings.filterwarnings("ignore")

class DetProcess:
    def __init__(self, K: int = 3000, num_classes: int = 2, scale: float = 1.0):
        self.K = K
        self.num_classes = num_classes
        self.scale = scale
        self.max_per_image = 3000

    def __call__(
        self, det_out: Dict[str, np.ndarray], meta: Dict[str, Union[int, np.ndarray]]
    ):
        hm = self.sigmoid(det_out["hm"])
        dets, keep, logi, cr = ctdet_4ps_decode(
            hm[:, 0:1, :, :],
            det_out["wh"],
            det_out["ax"],
            det_out["cr"],
            reg=det_out["reg"],
            K=self.K,
        )

        raw_dets = dets
        dets = dets.reshape(1, -1, dets.shape[2])
        dets = ctdet_4ps_post_process_upper_left(
            dets.copy(),
            [meta["c"]],
            [meta["s"]],
            meta["out_height"],
            meta["out_width"],
            2,
        )
        for j in range(1, self.num_classes + 1):
            dets[0][j] = np.array(dets[0][j], dtype=np.float32).reshape(-1, 9)
            dets[0][j][:, :8] /= self.scale
        dets = dets[0]
        detections = [dets]

        logi += cr
        results = self.merge_outputs(detections)
        slct_logi_feat, slct_dets_feat = self.filter(results, logi, raw_dets[:, :, :8])
        slct_output_dets = results[1][: slct_logi_feat.shape[1], :8]
        return slct_logi_feat, slct_dets_feat, slct_output_dets

    @staticmethod
    def sigmoid(data: np.ndarray) -> np.ndarray:
        return 1 / (1 + np.exp(-data))

    def merge_outputs(self, detections: Dict[int, np.ndarray]) -> Dict[int, np.ndarray]:
        # thresh_conf, thresh_min, thresh_max = 0.1, 0.5, 0.7
        results = {}
        for j in range(1, self.num_classes + 1):
            results[j] = np.concatenate(
                [detection[j] for detection in detections], axis=0
            ).astype(np.float32)

        scores = np.hstack([results[j][:, 8] for j in range(1, self.num_classes + 1)])
        if len(scores) > self.max_per_image:
            kth = len(scores) - self.max_per_image
            thresh = np.partition(scores, kth)[kth]
            for j in range(1, self.num_classes + 1):
                keep_inds = results[j][:, 8] >= thresh
                results[j] = results[j][keep_inds]
        return results

    @staticmethod
    def filter(
        results: Dict[int, np.ndarray], logi: np.ndarray, ps: np.ndarray
    ) -> Tuple[np.ndarray, np.ndarray]:
        # this function select boxes
        batch_size, feat_dim = logi.shape[0], logi.shape[2]
        num_valid = sum(results[1][:, 8] >= 0.15)

        slct_logi = np.zeros((batch_size, num_valid, feat_dim), dtype=np.float32)
        slct_dets = np.zeros((batch_size, num_valid, 8), dtype=np.int32)
        for i in range(batch_size):
            for j in range(num_valid):
                slct_logi[i, j, :] = logi[i, j, :]
                slct_dets[i, j, :] = ps[i, j, :]

        return slct_logi, slct_dets

def ctdet_4ps_decode(
    heat: np.ndarray,
    wh: np.ndarray,
    ax: np.ndarray,
    cr: np.ndarray,
    reg: np.ndarray = None,
    cat_spec_wh: bool = False,
    K: int = 100,
):
    batch, cat, _, width = heat.shape
    heat, keep = _nms(heat)
    scores, inds, clses, ys, xs = _topk(heat, K=K)

    if reg is not None:
        reg = _tranpose_and_gather_feat(reg, inds)
        reg = reg.reshape(batch, K, 2)
        xs = xs.reshape(batch, K, 1) + reg[:, :, 0:1]
        ys = ys.reshape(batch, K, 1) + reg[:, :, 1:2]
    else:
        xs = xs.reshape(batch, K, 1) + 0.5
        ys = ys.reshape(batch, K, 1) + 0.5

    wh = _tranpose_and_gather_feat(wh, inds)
    ax = _tranpose_and_gather_feat(ax, inds)

    if cat_spec_wh:
        wh = wh.reshape(batch, K, cat, 8)
        clses_ind = clses.reshape(batch, K, 1, 1).expand(batch, K, 1, 8)
        wh = wh.gather(2, clses_ind).reshape(batch, K, 8)
    else:
        wh = wh.reshape(batch, K, 8)

    clses = clses.reshape(batch, K, 1)
    scores = scores.reshape(batch, K, 1)

    bboxes_vec = [
        xs - wh[..., 0:1],
        ys - wh[..., 1:2],
        xs - wh[..., 2:3],
        ys - wh[..., 3:4],
        xs - wh[..., 4:5],
        ys - wh[..., 5:6],
        xs - wh[..., 6:7],
        ys - wh[..., 7:8],
    ]
    bboxes = np.concatenate(bboxes_vec, axis=2)

    cc_match = np.concatenate(
        [
            (xs - wh[..., 0:1]) + width * np.round(ys - wh[..., 1:2]),
            (xs - wh[..., 2:3]) + width * np.round(ys - wh[..., 3:4]),
            (xs - wh[..., 4:5]) + width * np.round(ys - wh[..., 5:6]),
            (xs - wh[..., 6:7]) + width * np.round(ys - wh[..., 7:8]),
        ],
        axis=2,
    )
    cc_match = np.round(cc_match).astype(np.int64)
    cr_feat = _get_4ps_feat(cc_match, cr)
    cr_feat = cr_feat.sum(axis=3)

    detections = np.concatenate([bboxes, scores, clses], axis=2)
    return detections, keep, ax, cr_feat

def _nms(heat: np.ndarray, kernel: int = 3) -> Tuple[np.ndarray, np.ndarray]:
    pad = (kernel - 1) // 2
    hmax = max_pool(heat, kernel_size=kernel, stride=1, padding=pad)
    keep = hmax == heat
    return heat * keep, keep

def max_pool(
    img: np.ndarray, kernel_size: int, stride: int, padding: int
) -> np.ndarray:
    h, w = img.shape[2:]
    img = np.pad(
        img,
        ((0, 0), (0, 0), (padding, padding), (padding, padding)),
        "constant",
        constant_values=0,
    )

    res_h = ((h + 2 - kernel_size) // stride) + 1
    res_w = ((w + 2 - kernel_size) // stride) + 1
    res = np.zeros((img.shape[0], img.shape[1], res_h, res_w))
    for i in range(res_h):
        for j in range(res_w):
            temp = img[
                :,
                :,
                i * stride : i * stride + kernel_size,
                j * stride : j * stride + kernel_size,
            ]
            res[:, :, i, j] = temp.max()
    return res

def _topk(
    scores: np.ndarray, K: int = 40
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    batch, cat, height, width = scores.shape

    topk_scores, topk_inds = find_topk(scores.reshape(batch, cat, -1), K)

    topk_inds = topk_inds % (height * width)
    topk_ys = topk_inds / width
    topk_xs = np.float32(np.int32(topk_inds % width))

    topk_score, topk_ind = find_topk(topk_scores.reshape(batch, -1), K)
    topk_clses = np.int32(topk_ind / K)
    topk_inds = _gather_feat(topk_inds.reshape(batch, -1, 1), topk_ind).reshape(
        batch, K
    )
    topk_ys = _gather_feat(topk_ys.reshape(batch, -1, 1), topk_ind).reshape(batch, K)
    topk_xs = _gather_feat(topk_xs.reshape(batch, -1, 1), topk_ind).reshape(batch, K)

    return topk_score, topk_inds, topk_clses, topk_ys, topk_xs

def find_topk(
    a: np.ndarray, k: int, axis: int = -1, largest: bool = True, sorted: bool = True
) -> Tuple[np.ndarray, np.ndarray]:
    if axis is None:
        axis_size = a.size
    else:
        axis_size = a.shape[axis]
    assert 1 <= k <= axis_size

    a = np.asanyarray(a)
    if largest:
        index_array = np.argpartition(a, axis_size - k, axis=axis)
        topk_indices = np.take(index_array, -np.arange(k) - 1, axis=axis)
    else:
        index_array = np.argpartition(a, k - 1, axis=axis)
        topk_indices = np.take(index_array, np.arange(k), axis=axis)

    topk_values = np.take_along_axis(a, topk_indices, axis=axis)
    if sorted:
        sorted_indices_in_topk = np.argsort(topk_values, axis=axis)
        if largest:
            sorted_indices_in_topk = np.flip(sorted_indices_in_topk, axis=axis)

        sorted_topk_values = np.take_along_axis(
            topk_values, sorted_indices_in_topk, axis=axis
        )
        sorted_topk_indices = np.take_along_axis(
            topk_indices, sorted_indices_in_topk, axis=axis
        )
        return sorted_topk_values, sorted_topk_indices
    return topk_values, topk_indices

def _gather_feat(feat: np.ndarray, ind: np.ndarray) -> np.ndarray:
    dim = feat.shape[2]
    ind = np.broadcast_to(ind[:, :, None], (ind.shape[0], ind.shape[1], dim))
    feat = _gather(feat, 1, ind)
    return feat

def _gather(data: np.ndarray, dim: int, index: np.ndarray) -> np.ndarray:
    """
    Gathers values along an axis specified by dim.
    For a 3-D tensor the output is specified by:
        out[i][j][k] = input[index[i][j][k]][j][k]  # if dim == 0
        out[i][j][k] = input[i][index[i][j][k]][k]  # if dim == 1
        out[i][j][k] = input[i][j][index[i][j][k]]  # if dim == 2

    :param dim: The axis along which to index
    :param index: A tensor of indices of elements to gather
    :return: tensor of gathered values
    """
    idx_xsection_shape = index.shape[:dim] + index.shape[dim + 1 :]
    data_xsection_shape = data.shape[:dim] + data.shape[dim + 1 :]
    if idx_xsection_shape != data_xsection_shape:
        raise ValueError(
            "Except for dimension "
            + str(dim)
            + ", all dimensions of index and data should be the same size"
        )

    if index.dtype != np.int64:
        raise TypeError("The values of index must be integers")

    data_swaped = np.swapaxes(data, 0, dim)
    index_swaped = np.swapaxes(index, 0, dim)
    gathered = np.take_along_axis(data_swaped, index_swaped, axis=0)
    return np.swapaxes(gathered, 0, dim)

def _tranpose_and_gather_feat(feat: np.ndarray, ind: np.ndarray) -> np.ndarray:
    feat = np.ascontiguousarray(np.transpose(feat, [0, 2, 3, 1]))
    feat = feat.reshape(feat.shape[0], -1, feat.shape[3])
    feat = _gather_feat(feat, ind)
    return feat

def _get_4ps_feat(cc_match: np.ndarray, output: np.ndarray) -> np.ndarray:
    if isinstance(output, dict):
        feat = output["cr"]
    else:
        feat = output

    feat = np.ascontiguousarray(feat.transpose(0, 2, 3, 1))
    feat = feat.reshape(feat.shape[0], -1, feat.shape[3])
    feat = feat[..., None]
    feat = np.concatenate([feat] * 4, axis=-1)

    dim = feat.shape[2]
    cc_match = cc_match[..., None, :]
    cc_match = np.concatenate([cc_match] * dim, axis=2)
    if not (isinstance(output, dict)):
        cc_match = np.where(
            cc_match < feat.shape[1],
            cc_match,
            (feat.shape[0] - 1) * np.ones(cc_match.shape).astype(np.int64),
        )

        cc_match = np.where(
            cc_match >= 0, cc_match, np.zeros(cc_match.shape).astype(np.int64)
        )
    feat = np.take_along_axis(feat, cc_match, axis=1)
    return feat

def ctdet_4ps_post_process_upper_left(
    dets: np.ndarray,
    c: List[np.ndarray],
    s: List[float],
    h: int,
    w: int,
    num_classes: int,
) -> np.ndarray:
    # dets: batch x max_dets x dim
    # return 1-based class det dict
    ret = []
    for i in range(dets.shape[0]):
        top_preds = {}
        dets[i, :, 0:2] = transform_preds_upper_left(
            dets[i, :, 0:2], c[i], s[i], (w, h)
        )
        dets[i, :, 2:4] = transform_preds_upper_left(
            dets[i, :, 2:4], c[i], s[i], (w, h)
        )
        dets[i, :, 4:6] = transform_preds_upper_left(
            dets[i, :, 4:6], c[i], s[i], (w, h)
        )
        dets[i, :, 6:8] = transform_preds_upper_left(
            dets[i, :, 6:8], c[i], s[i], (w, h)
        )
        classes = dets[i, :, -1]
        for j in range(num_classes):
            inds = classes == j
            tmp_top_pred = [
                dets[i, inds, :8].astype(np.float32),
                dets[i, inds, 8:9].astype(np.float32),
            ]
            top_preds[j + 1] = np.concatenate(tmp_top_pred, axis=1).tolist()
        ret.append(top_preds)
    return ret

def transform_preds_upper_left(
    coords: np.ndarray,
    center: np.ndarray,
    scale: float,
    output_size: Tuple[int, int],
) -> np.ndarray:
    target_coords = np.zeros(coords.shape)

    trans = get_affine_transform_upper_left(center, scale, output_size, inv=1)
    for p in range(coords.shape[0]):
        target_coords[p, 0:2] = affine_transform(coords[p, 0:2], trans)
    return target_coords

def get_affine_transform_upper_left(
    center: np.ndarray,
    scale: float,
    output_size: List[Tuple[int, int]],
    inv: int = 0,
) -> np.ndarray:
    if not isinstance(scale, np.ndarray) and not isinstance(scale, list):
        scale = np.array([scale, scale], dtype=np.float32)

    src = np.zeros((3, 2), dtype=np.float32)
    dst = np.zeros((3, 2), dtype=np.float32)
    src[0, :] = center
    dst[0, :] = [0, 0]
    if center[0] < center[1]:
        src[1, :] = [scale[0], center[1]]
        dst[1, :] = [output_size[0], 0]
    else:
        src[1, :] = [center[0], scale[0]]
        dst[1, :] = [0, output_size[0]]
    src[2:, :] = get_3rd_point(src[0, :], src[1, :])
    dst[2:, :] = get_3rd_point(dst[0, :], dst[1, :])

    if inv:
        trans = cv2.getAffineTransform(np.float32(dst), np.float32(src))
    else:
        trans = cv2.getAffineTransform(np.float32(src), np.float32(dst))
    return trans

def get_3rd_point(a: np.ndarray, b: np.ndarray) -> np.ndarray:
    direct = a - b
    return b + np.array([-direct[1], direct[0]], dtype=np.float32)

def affine_transform(pt: np.ndarray, t: np.ndarray) -> np.ndarray:
    new_pt = np.array([pt[0], pt[1], 1.0], dtype=np.float32).T
    new_pt = np.dot(t, new_pt)
    return new_pt[:2]


# -*- coding: utf-8 -*-

import numpy as np
from sklearn.datasets import load_iris
import matplotlib.pyplot as plot

"""
Ejercicio 1
"""

matriz = load_iris()

a=matriz.data
b=matriz.target_names

x1=a[0:49,-2:]
x2=a[50:99,-2:]
x3=a[100:149,-2:]

plot.scatter(x1[:,0],x1[:,1],c=[[1,0,0]],label=b[0])
plot.scatter(x2[:,0],x2[:,1],c=[[0,1,0]],label=b[1])
plot.scatter(x3[:,0],x3[:,1],c=[[0,0,1]],label=b[2])
plot.title('Iris database')
plot.legend(loc=2)
plot.show()

input('Pulse enter para pasar al ejercicio 2')
"""
Ejercicio 2
"""

x12=np.random.permutation(x1)
x22=np.random.permutation(x2)
x32=np.random.permutation(x3)
training=np.array(np.concatenate((x12[:40,:],x22[:40,:],x32[:40,:]),axis=0,out=None))
test=np.array(np.concatenate((x12[-10:,:],x22[-10:,:],x32[-10:,:]),axis=0,out=None))

plot.scatter(training[:,0],training[:,1],c=[[1,1,0]],label="training")
plot.scatter(test[:,0],test[:,1],c=[[0,1,1]],label="test")
plot.legend(loc=2)
plot.show()
print("Tamaño training: ",training.shape[0], ". Tamaño test: ",test.shape[0])

input('Pulse enter para pasar al ejercicio 3')
"""
Ejercicio 3
"""

n=np.linspace(0,2*np.pi,100)
seno=np.sin(n)
coseno=np.cos(n)
suma=np.sin(n)+np.cos(n)

plot.plot(n,seno,"k--",label="Sin(x)")
plot.plot(n,coseno,"b--",label="Cos(x)")
plot.plot(n,suma,"r--",label="Sin(x)+Cos(x)")
plot.legend(loc=2)
plot.show()
#n,a b-- ,r r--


import argparse
import logging

from elixir import feedstock

__version__ = '0.0.1'

def _config_logging(logging_level='INFO', logging_file=None):

    allowed_levels = {
        'DEBUG': logging.DEBUG,
        'INFO': logging.INFO,
        'WARNING': logging.WARNING,
        'ERROR': logging.ERROR,
        'CRITICAL': logging.CRITICAL
    }

    logging_config = {
        'level': allowed_levels.get(logging_level, 'INFO'),
        'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
    }

    if logging_file:
        logging_config['filename'] = logging_file

    logging.basicConfig(**logging_config)

def main(pid, source_dir='.', logging_level='Info', logging_file=None, deposit_dir=None):

    _config_logging(logging_level, logging_file)

    logging.info('Starting to pack a document')

    if feedstock.is_valid_pid(pid):
        xml = feedstock.loadXML(pid)
        raw_data = feedstock.load_rawdata(pid)
        article = feedstock.Article(pid, xml, raw_data, source_dir, deposit_dir)

        article.wrap_document()

def argp():
    parser = argparse.ArgumentParser(
        description="Create a article package from the legacy data")

    parser.add_argument(
        '--pid',
        '-p',
        default=None,
        help='Document ID, must be the PID number'
    )

    parser.add_argument(
        '--source_dir',
        '-s',
        default='.',
        help='Source directory where the pdf, images and html\'s cold be fetched'
    )

    parser.add_argument(
        '--logging_file',
        '-o',
        default=None,
        help='File to record all logging data, if None the log will be send to the standard out'
    )

    parser.add_argument(
        '--logging_level',
        '-l',
        default='INFO',
        choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
        help='File to record all logging data, if None the log will be send to the standard out'
    )

    parser.add_argument(
        '--deposit_dir',
        '-d',
        default=None,
        help='Directory to receive the packages'
    )

    args = parser.parse_args()

    main(
        args.pid,
        source_dir=args.source_dir,
        logging_level=args.logging_level,
        logging_file=args.logging_file,
        deposit_dir=args.deposit_dir
    )

if __name__ == "__main__":

    argp()


"""
This file is part of nand2tetris, as taught in The Hebrew University, and
was written by Aviv Yaish. It is an extension to the specifications given
[here](https://www.nand2tetris.org) (Shimon Schocken and Noam Nisan, 2017),
as allowed by the Creative Common Attribution-NonCommercial-ShareAlike 3.0
Unported [License](https://creativecommons.org/licenses/by-nc-sa/3.0/).
"""
import typing
import os

class CodeWriter:
    """Translates VM commands into Hack assembly code."""

    def __init__(self, output_stream: typing.TextIO) -> None:
        """Initializes the CodeWriter.

        Args:
            output_stream (typing.TextIO): output stream.
        """
        # Your code goes here!
        # Note that you can write to output_stream like so:
        # output_stream.write("Hello world! \n")
        self.output_stream = output_stream
        self.segments_type_dict = {"local": "LCL", "argument": "ARG",
                                   "this": "THIS", "that": "THAT",
                                   "pointer": 3, "temp": 5}
        self._vm_file_name = ''
        self.comp_i = 0
        self.call_i = 0

        self.function_counter = 0
    def set_file_name(self, filename: str) -> None:
        """Informs the code writer that the translation of a new VM file is
        started.

        Args:
            filename (str): The name of the VM file.
        """
        # Your code goes here!
        # This function is useful when translating code that handles the
        # static segment. For example, in order to prevent collisions between two
        # .vm files which push/pop to the static segment, one can use the current
        # file's name in the assembly variable's name and thus differentiate between
        # static variables belonging to different files.
        # To avoid problems with Linux/Windows/MacOS differences with regards
        # to filenames and paths, you are advised to parse the filename in
        # the function "translate_file" in Main.py using python's os library,
        # For example, using code similar to:
        # input_filename, input_extension = os.path.splitext(os.path.basename(input_file.name))
        self._vm_file_name = filename

    def write_arithmetic(self, command: str) -> None:
        """Writes assembly code that is the translation of the given
        arithmetic command. For the commands eq, lt, gt, you should correctly
        compare between all numbers our computer supports, and we define the
        value "true" to be -1, and "false" to be 0.

        Args:
            command (str): an arithmetic command.
        """
        # Your code goes here!
        if command == "add":
            self.output_stream.write(self._binary_operation("+"))

        elif command == "sub":
            self.output_stream.write(self._binary_operation("-"))

        elif command == "neg":
            self.output_stream.write(self._unary_operation("-", "before"))

        elif command == "eq":
            self.output_stream.write(self.compare("JEQ"))

        elif command == "gt":
            self.output_stream.write(self.compare("JGT"))

        elif command == "lt":
            self.output_stream.write(self.compare("JLT"))

        elif command == "and":
            self.output_stream.write(self._binary_operation("&"))

        elif command == "or":
            self.output_stream.write(self._binary_operation("|"))

        elif command == "not":
            self.output_stream.write(self._unary_operation("!", "before"))
        elif command == "shiftleft":
            self.output_stream.write(self._unary_operation("<<", "after"))
        elif command == "shiftright":
            self.output_stream.write(self._unary_operation(">>", "after"))
        # todo >> << shift left and right

    def write_push_pop(self, command: str, segment: str, index: int) -> None:
        """Writes assembly code that is the translation of the given
        command, where command is either C_PUSH or C_POP.

        Args:
            command (str): "C_PUSH" or "C_POP".
            segment (str): the memory segment to operate on.
            index (int): the index in the memory segment.
        """
        # Your code goes here!
        # Note: each reference to "static i" appearing in the file Xxx.vm should
        # be translated to the assembly symbol "Xxx.i". In the subsequent
        # assembly process, the Hack assembler will allocate these symbolic
        # variables to the RAM, starting at address 16.
        code = ""
        if command == "C_PUSH":
            if segment == "constant":
                code += f"@{index}\nD=A\n"
            elif segment == "local" or segment == "argument" or segment == "this" or segment == "that":
                code += f"@{index}\nD=A\n@{self.segments_type_dict[segment]}\nA=M+D\nD=M\n"
            elif segment == "pointer" or segment == "temp":
                code += f"@{index}\nD=A\n@{self.segments_type_dict[segment]}\nA=A+D\nD=M\n"
            elif segment == "static":
                code += f"@{self._vm_file_name}.{index}\nD=M\n"
            code += "@SP\nA=M\nM=D\n@SP\nM=M+1\n"

        elif command == "C_POP":
            if segment == "constant":
                code += f"@{index}\nD=A\n"
            elif segment == "local" or segment == "argument" or segment == "this" or segment == "that":
                code += f"@{self.segments_type_dict[segment]}\nD=M\n@{index}\nD=D+A\n"
            elif segment == "pointer" or segment == "temp":
                code += f"@{index}\nD=A\n@{self.segments_type_dict[segment]}\nA=D+A\nD=A\n"
            elif segment == "static":
                code += f"@{self._vm_file_name}.{index}\nD=A\n"

            code += f"@R13\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@R13\nA=M\nM=D\n"
        self.output_stream.write(code)

    def write_label(self, label: str) -> None:
        """Writes assembly code that affects the label command.
        Let "Xxx.foo" be a function within the file Xxx.vm. The handling of
        each "label bar" command within "Xxx.foo" generates and injects the symbol
        "Xxx.foo$bar" into the assembly code stream.
        When translating "goto bar" and "if-goto bar" commands within "foo",
        the label "Xxx.foo$bar" must be used instead of "bar".

        Args:
            label (str): the label to write.
        """
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        code = f"({label})\n"
        self.output_stream.write(code)

    def write_goto(self, label: str) -> None:
        """Writes assembly code that affects the goto command.

        Args:
            label (str): the label to go to.
        """
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        code = f"@{label}\n0;JMP\n"
        self.output_stream.write(code)

    def write_if(self, label: str) -> None:
        """Writes assembly code that affects the if-goto command.

        Args:
            label (str): the label to go to.
        """
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        code = f"@SP\nM=M-1\nA=M\nD=M\n@{label}\nD;JNE\n"
        self.output_stream.write(code)

    def write_function(self, function_name: str, n_vars: int) -> None:
        """Writes assembly code that affects the function command.
        The handling of each "function Xxx.foo" command within the file Xxx.vm
        generates and injects a symbol "Xxx.foo" into the assembly code stream,
        that labels the entry-point to the function's code.
        In the subsequent assembly process, the assembler translates this
        symbol into the physical address where the function code starts.

        Args:
            function_name (str): the name of the function.
            n_vars (int): the number of local variables of the function.
        """
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        # The pseudo-code of "function function_name n_vars" is:
        # (function_name)       // injects a function entry label into the code
        # repeat n_vars times:  // n_vars = number of local variables
        #   push constant 0     // initializes the local variables to 0
        self.write_label(function_name)
        for i in range(n_vars):
            self.write_push_pop("C_PUSH", "constant", 0)

    def write_call(self, function_name: str, n_args: int) -> None:
        """Writes assembly code that affects the call command.
        Let "Xxx.foo" be a function within the file Xxx.vm.
        The handling of each "call" command within Xxx.foo's code generates and
        injects a symbol "Xxx.foo$ret.i" into the assembly code stream, where
        "i" is a running integer (one such symbol is generated for each "call"
        command within "Xxx.foo").
        This symbol is used to mark the return address within the caller's
        code. In the subsequent assembly process, the assembler translates this
        symbol into the physical memory address of the command immediately
        following the "call" command.

        Args:
            function_name (str): the name of the function to call.
            n_args (int): the number of arguments of the function.
        """
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        # The pseudo-code of "call function_name n_args" is:
        # push return_address   // generates a label and pushes it to the stack
        # push LCL              // saves LCL of the caller
        # push ARG              // saves ARG of the caller
        # push THIS             // saves THIS of the caller
        # push THAT             // saves THAT of the caller
        # ARG = SP-5-n_args     // repositions ARG
        # LCL = SP              // repositions LCL
        # goto function_name    // transfers control to the callee
        # (return_address)      // injects the return address label into the code
        self.call_i += 1
        return_address = f"return_{function_name}{self.call_i}"
        self.output_stream.write(f"@{return_address}\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n")
        for segment in ["LCL", "ARG", "THIS", "THAT"]:
            self.output_stream.write(f"@{segment}\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n")
        # ARG = SP-5-n_args
        self.output_stream.write(f"@{n_args}\nD=A\n@SP\nD=M-D\n@5\nD=D-A\n@ARG\nM=D\n")
        # LCL = SP
        self.output_stream.write(f"@SP\nD=M\n@LCL\nM=D\n")
        self.write_goto(function_name)
        self.write_label(f"return_{function_name}{self.call_i}")

    def write_return(self) -> None:
        """Writes assembly code that affects the return command."""
        # This is irrelevant for project 7,
        # you will implement this in project 8!
        # The pseudo-code of "return" is:
        # frame = LCL                   // frame is a temporary variable
        # return_address = *(frame-5)   // puts the return address in a temp var
        # *ARG = pop()                  // repositions the return value for the caller
        # SP = ARG + 1                  // repositions SP for the caller
        # THAT = *(frame-1)             // restores THAT for the caller
        # THIS = *(frame-2)             // restores THIS for the caller
        # ARG = *(frame-3)              // restores ARG for the caller
        # LCL = *(frame-4)              // restores LCL for the caller
        # goto return_address           // go to the return address
        self.output_stream.write("@LCL\nD=M\n@13\nM=D\n")
        self.output_stream.write("@13\nD=M\n@5\nD=D-A\nA=D\nD=M\n@14\nM=D\n")
        self.output_stream.write(
            "@SP\nA=M-1\nD=M\n@ARG\nA=M\nM=D\n@SP\nM=M-1\n")
        self.output_stream.write("@ARG\nD=M+1\n@SP\nM=D\n")
        for i, segment in enumerate(["THAT", "THIS", "ARG", "LCL"]):
            self.output_stream.write(
                f"@13\nD=M\n@{i + 1}\nA=D-A\nD=M\n@{segment}\nM=D\n")
        self.output_stream.write("@14\nA=M\n0;JMP\n")

    def compare(self, command):
        self.comp_i += 1
        stack_to_d = f"@SP\n" \
               f"M=M-1\n" \
               f"A=M\n" \
               f"D=M\n" \

        code = stack_to_d + f"@R13\nM=D\n"
        code += f"@YPOS{self.comp_i}\nD;JGT\n"
        code += stack_to_d +  f"@YNEGXPOS{self.comp_i}\nD;JGT\n"
        code += f"(CHECK{self.comp_i})\n@R13\nD=D-M\n" \
                f"@TRUE{self.comp_i}\nD;{command}\n" \
                f"@FALSE{self.comp_i}\n0;JMP\n"
        code+= f"(YPOS{self.comp_i})\n" \
               f"@SP\n" \
               f"M=M-1\n" \
               f"A=M\n" \
               f"D=M\n" \
               f"@YPOSXNEG{self.comp_i}\nD;JLT\n" \
               f"@CHECK{self.comp_i}\n0;JMP\n" \
               f"(YNEGXPOS{self.comp_i})\n" \
               f"@R13\nM=-1\n" \
               f"@CHECK{self.comp_i}\n0;JMP\n" \
               f"(YPOSXNEG{self.comp_i})\nD=-1\n" \
               f"@CHECK{self.comp_i}\n0;JMP\n" \
               f"(FALSE{self.comp_i})\n" \
               f"@SP\n" \
               f"A=M\n" \
               f"M=0\n" \
               f"@END{self.comp_i}\n0;JMP\n" \
               f"(TRUE{self.comp_i})\n" \
               f"@SP\n" \
               f"A=M\n" \
               f"M=-1\n" \
               f"@END{self.comp_i}\n0;JMP\n" \
               f"(END{self.comp_i})\n@SP\nM=M+1\n"
        return code
    def _unary_operation(self, command, before_after) -> str:
        code = f"@SP\n" \
               f"M=M-1\n" \
               f"A=M\n"
        if before_after == "before":
            code += f"M={command}M\n"
        elif before_after == "after":
            code += f"M=M{command}\n"
        return code + f"@SP\n" \
                      f"M=M+1\n"

    # binary

    def _binary_operation(self, command) -> str:
        return f"@SP\n" \
               f"M=M-1\n" \
               f"A=M\n" \
               f"D=M\n" \
               f"@SP\n" \
               f"M=M-1\n" \
               f"A=M\n" \
               f"M=M{command}D\n" \
               f"@SP\n" \
               f"M=M+1\n"

    def writeBootstrap(self):
        self.output_stream.write("@256\nD=A\n@SP\nM=D\n")
        self.write_call("Sys.init",0)


import string
import nltk
from bs4 import BeautifulSoup
from nltk.tokenize import word_tokenize
from nltk.stem import WordNetLemmatizer
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

nltk.download('wordnet')
nltk.download('omw-1.4')
nltk.download('stopwords')
nltk.download('averaged_perceptron_tagger')

lem = WordNetLemmatizer()
stop_words = stopwords.words('english')
punc = list(string.punctuation)

file_path = 'news.xml'

with open(file_path, 'r', encoding='utf-8') as file:

    soup = BeautifulSoup(file, 'xml')

    heads = soup.find_all('value', {'name': 'head'})
    contents = soup.find_all('value', {'name': 'text'})

    # Getting the news text:

    news_text = [sorted(word_tokenize(contents[i].text.lower()), reverse=True)for i in range(len(contents))]

    # lemmatization:

    lem_news = [[lem.lemmatize(word) for word in list_words] for list_words in news_text]

    # POS for individual word in each news story:

    pos_news = [[nltk.pos_tag([word])[0] for word in list_words] for list_words in lem_news]

    # Filtering for nouns only:

    nouns_news = [[tup[0] for tup in tup_list if tup[1] == 'NN'] for tup_list in pos_news]

    # Cleaning the nouns list into sentence: Last Stage

    sentence_list = [[' '.join(word)] for word in nouns_news]

    dataset = [sent for element in sentence_list for sent in element]

    # Count the TF-IDF metric for each word in all stories, i.e. apply it to the whole collection of news documents.

    vectorizer = TfidfVectorizer()

    tfidf_matrix = vectorizer.fit_transform(dataset)

    terms = vectorizer.get_feature_names()

    # The following code was copied from another solution: I have no idea what's going on here, but it gives the result.
    # Again, because there is no topic in the Academy explaining how the get the most frequent words properly based on the score of each term

    for i in range(len(heads)):
        print(f'{heads[i].text}:')
        df = pd.DataFrame(tfidf_matrix[i].T.todense(), index=terms, columns=["tfidf"])
        df.index.name = 'word'
        df = df.sort_values(['tfidf', 'word'], ascending=[False, False]).head(5)
        print(' '.join(list(df.index)), '\n')


#!/usr/bin/env python
# coding: utf-8
# @Author  : Mr.K
# @Software: PyCharm Community Edition
# @Time    : 2020/1/3 12:19
# @Description:

#统计list中出现的元素个数
#参考：https://www.zybang.com/question/2fa278ce7f89fb437759d57ab4b20594.html
# numbers=["cc","cc","ct","ct","ac"]
# res = {}
# for i in numbers:
#     res[i] = res.get(i, 0) + 1
# print([k for k in res.keys()])
# print([v for v in res.values()])

#返回list中指定元素的所有索引
# a=["cc","cc","ct","ct","ac"]
# print([i for i,x in enumerate(a) if x=='cc'])

#统计list中出现的元素个数并获取其索引
#参考https://www.jianshu.com/p/c107f3d3c6ba
from collections import defaultdict
a=["cc","cc","ct","ct","ac"]
#a=[1,2,3,1,2,3]
d = defaultdict(list)
for i, v in enumerate(a):
    d[v].append(i)
print(d)


import csv
import pandas as pd
import matplotlib.pyplot as plt

CSV_LOAD_PATH = "data_C3-C4.csv"

ROW_INDEX = {
	'name': 0,
	'time': 1,
	'stim_intensity': 6,
	'rep_rate': 8
}

def plot_signal(datapoints):
	plt.plot(datapoints)
	# plt.title('Max: ', max(datapoints), 'min: ', min(datapoints))
	plt.show()

def percent_change(trial_a_signal, trial_b_signal):
	amp_a = max(trial_a_signal) - min(trial_a_signal)
	amp_b = max(trial_b_signal) - min(trial_b_signal)
	return (amp_b - amp_a) / amp_a

class Trial:

	def __init__(self, column):
		self.channel_name = column[ROW_INDEX['name']]
		self.time = column[ROW_INDEX['time']]
		self.stim_intensity = column[ROW_INDEX['stim_intensity']]
		self.rep_rate = column[ROW_INDEX['rep_rate']]

		self.signal = self.read_signal(column)

	def read_signal(self, column):
		# return a list of floats of signal
		list_of_strings = list(column[9:])
		return [float(i) for i in list_of_strings]

def main():
	# load csv
	df = pd.read_csv('data_C3-C4.csv')

	print(df.head(20))

	list_of_trials = []
	for col_index in range(1, 19):
		index = str(col_index)
		curr_trial = Trial(df[index])
		list_of_trials.append(curr_trial)

	trial_1_signal = list_of_trials[0].signal # -> do what you want here.

	trial_2_signal = list_of_trials[1].signal

	print(percent_change(trial_1_signal, trial_2_signal))

	# for trial in list_of_trials:
		# plot_signal(trial.signal)

	# print(type(trial_1.time))

if __name__ == "__main__":
	main()


"""
Perform teardown and integration logic after executing "constructive" Terraform
subcommands (e.g. `init`, `plan`, and `apply`).
"""

from typing import List

import json

from accounts.models import Group
from cbhooks.models import TerraformStateFile, TerraformPlanHook
from infrastructure.models import Environment, Server
from jobs.models import Job
from orders.models import BlueprintOrderItem
from resources.models import Resource
from utilities.logger import ThreadLogger
from common.methods import set_progress
from c2_wrapper import create_custom_field

logger = ThreadLogger(__name__)

# These are the names of Terraform resources associated with servers/VMs in the
# main Terraform Providers which have corresponding CloudBolt Resource
# Technology support. Used below in `_parse_state_file_for_server_ids()` for
# finding servers in a state file.
TERRAFORM_VM_TYPES = [
    "google_compute_instance",
    "azurerm_virtual_machine",
    "aws_instance",
    "vsphere_virtual_machine",
    "openstack_compute_instance_v2",
    "clc_server",
    "nutanix_virtual_machine",
]

def post_provision(
        hook: TerraformPlanHook,
        job: Job,
        resource: Resource,
        state_file_obj: TerraformStateFile,
        group: Group,
        **kwargs,
) -> str:
    """
    `post_provision` runs after `apply`, after a Terraform Plan action has
    provisioned one or more Resources.

    This function adds any servers provisioned by Terraform as "Server" objects
    in CloudBolt, allowing for management in this product.

    Note: This function _must_ return a `str. Any additional side-effects can
        occur during this function execution, but changing the return type will
        cause Terraform execution to break.

    Args:
        hook (TerraformPlanHook): The "Terraform Plan" Action that's called from
            a Blueprint.
        job (Job): Async "Job" object that's associated with running this `hook`.
        resource (Resource): "Resource" object that Terraform will populate /
            provision to.
        state_file_obj (TerraformStateFile): File model object for the generated
            Terraform state file.out
        group (Group): "Group" that the original Blueprint was ordered under.

    Returns:
        str: Output to be displayed on the Job "Details" page.
    """
    servers: List[Server] = get_or_create_server_records_from_state_file(
        state_file_obj=state_file_obj, resource=resource, group=group,
        job=job
    )

    # Write the Outputs of the TF plan to the Resource
    write_outputs(resource, state_file_obj)

    # Save the Blueprint deploy job to the server for job record
    blueprint_job = job.parent_job
    for server in servers:
        blueprint_job.server_set.add(server)

    return (
        f"Created resource '{resource}' with " f"{len(servers)} servers from terraform"
    )

def write_outputs(
        resource: Resource, state_file_obj: TerraformStateFile
) -> None:
    """
    Write all TF plan outputs to the Resource as parameters
    """
    state_file_content = state_file_obj.content_json
    try:
        tf_outputs = state_file_content["outputs"]
    except KeyError:
        set_progress("Outputs not set on TF plan. Continuing")
        return None
    for key in tf_outputs.keys():
        value = tf_outputs[key]["value"]
        tf_type = tf_outputs[key]["type"]
        try:
            description = tf_outputs[key]["description"]
        except:
            description = ""
        try:
            sensitive = tf_outputs[key]["sensitive"]
        except:
            sensitive = None
        logger.info(f'Custom Field: {key}, Value: {value}, Type: {type}, '
                    f'description: {description}, sensitive: {sensitive}')
        cb_type = get_cloudbolt_type(tf_type, sensitive)

        if not cb_type:
            try:
                # If cb_type can't be determined attempt to json.dumps
                value = json.dumps(value)
                cb_type = "STR"
            except:
                logger.warning(f"TF output type does not match supported "
                               f"output types. Type: {tf_type}, Key: {key}")
                continue

        if sensitive:
            # If Sensitive want to store as a string password value
            value = str(value)
        field_name = f'tf_output_{key}'
        cf = create_custom_field(field_name, key, cb_type, show_on_servers=True,
                                 description=description)
        resource.set_value_for_custom_field(field_name, value)
    return None

def get_cloudbolt_type(tf_type, sensitive):
    cb_type = ""
    if sensitive:
        cb_type = "PWD"
    elif tf_type == "string":
        cb_type = "STR"
    elif tf_type == "number":
        cb_type = "INT"
    elif tf_type == "bool":
        cb_type = "BOOL"
    return cb_type

def get_or_create_server_records_from_state_file(
        state_file_obj: TerraformStateFile, resource: Resource, group: Group,
        job: Job
) -> List[Server]:
    """
    Get or create Server object instances from server's unique UUIDs parsed out of
    the Terraform state file from this action.
    """

    server_ids = _parse_state_file_for_server_ids(state_file_obj, resource,
                                                  job)
    logger.info(
        f"Will create or update records for {len(server_ids)} servers in CloudBolt."
    )
    env = Environment.objects.get(name="Unassigned")
    servers = []

    for svr_id in server_ids:
        tech_dict = None
        if type(svr_id) == dict:
            logger.info(f'svr_id: {svr_id}')
            tech_dict = svr_id["tech_dict"]
            rh = svr_id["rh"]
            logger.info(f'tech_dict: {tech_dict}')
            env = svr_id["env"]
            svr_id = svr_id["id"]
        if svr_id:
            # Server manager does not have the create_or_update method,
            # so we do this manually.
            try:
                server = Server.objects.get(resource_handler_svr_id=svr_id)
                server.resource = resource
                server.group = group
                server.owner = resource.owner
                server.environment = env
                server.save()
                logger.info(f"Found existing server record: '{server}'")
            except Server.DoesNotExist:
                logger.info(
                    f"Creating new server with resource_handler_svr_id "
                    f"'{svr_id}', resource '{resource}', group '{group}', "
                    f"owner '{resource.owner}', and "
                    f"environment '{env}'"
                )
                server = Server(
                    hostname=svr_id,
                    resource_handler_svr_id=svr_id,
                    resource=resource,
                    group=group,
                    owner=resource.owner,
                    environment=env,
                )
                server.save()
                # We have to have already saved the new server record before this
                # will effectively be added as a custom field value, then
                # we have to save again to apply that relationship.
                server.created_by_terraform = True
                server.save()

            if tech_dict:
                server.resource_handler = rh
                server.save()
                try:
                    rh.cast().update_tech_specific_server_details(server,
                                                                  tech_dict)
                    server.refresh_info()
                except Exception as err:
                    logger.warning(f'Unable to directly sync server, verify '
                                   f'that the chosen region/vpc has been '
                                   f'imported to CloudBolt. Error: {err}')
            servers.append(server)

    return servers

def _parse_state_file_for_server_ids(state_file_obj: TerraformStateFile,
                                     resource, job) -> List[int]:
    """
    Read through the JSON dict of the state file getting resource ids if they
    are expected to be servers.

    Args:
        state_file_obj (cbhooks.TerraformStateFile)
    """
    server_ids = []

    logger.info(
        f"Parsing the JSON from the state file "
        f"'{state_file_obj.module_file.name}' to "
        f"search for servers that can be represented in CloudBolt."
    )

    state_dict = state_file_obj.content_json
    if not state_dict:
        return []

    # Terraform pushes out versions of their state file syntax, and we are only
    # able to support version 3 (will support 4 later, too)
    version = int(state_dict.get("version"))

    # Parse modules.
    # The JSON is slightly different depending on whether the plan contains modules
    modules = state_dict.get("modules")
    if modules:
        resources = []
        for module in modules:
            resources_dict = module.get("resources")
            for resource_name, attrs in resources_dict.items():
                resources.append(attrs)
    else:
        # Parse the resources directly.
        resources = state_dict.get("resources", [])

    for resource_dict in resources:
        # Expected format (version 3):
        # resource_dict = {
        #     'type': 'aws_instance',
        #     'primary': {
        #          'id': 'i-08204873902849032'
        #      }
        # }
        if resource_dict.get("type") in TERRAFORM_VM_TYPES:
            if resource_dict.get("mode") == "managed":
                logger.info(
                    f"Found a terraform resource of type '{resource_dict.get('type')}'."
                )
                if version == 3:
                    vm_id = resource_dict.get("primary", {}).get("id")
                    if vm_id:
                        server_ids.append(vm_id)
                else:
                    logger.warning(
                        f"Detected that the state file's version is "
                        f"{version} and CloudBolt currently only supports"
                        f"version 3."
                    )
                    if version == 4:
                        # We shouldn't be able to get to this step because Terraform
                        # variable - action input parsing should fail first,
                        # but we have left this code here for future support.
                        instances = resource_dict.get("instances")
                        for instance in instances:
                            vm_id = instance.get("attributes").get("id")
                            tech_dict = None
                            resource_type = resource_dict.get("type")
                            supported_types = [
                                "aws_instance", "vsphere_virtual_machine"
                            ]
                            if resource_type in supported_types:
                                tech_dict, rh, env = get_tech_dict(
                                    instance,
                                    vm_id,
                                    job,
                                    resource_type
                                )
                            if tech_dict:
                                vm_id = {
                                    "id": vm_id,
                                    "tech_dict": tech_dict,
                                    "rh": rh,
                                    "env": env
                                }
                            server_ids.append(vm_id)

    return server_ids

def get_tech_dict(instance, vm_id, job, resource_type):
    env = get_environment_from_job(job)
    if resource_type == "aws_instance":
        return get_aws_tech_dict(instance, env, vm_id)
    if resource_type == "vsphere_virtual_machine":
        return get_vmware_tech_dict(instance, env, vm_id)

def get_environment_from_job(job):
    params = json.loads(job.order_item._encrypted_arguments)["context"]
    bpoi_id = params["blueprint_order_item"]
    service_item_id = params["service_item_id"]
    bpoi = BlueprintOrderItem.objects.get(id=bpoi_id)
    bpia = bpoi.blueprintitemarguments_set.get(service_item__id=service_item_id)
    env = bpia.environment
    return env

def get_aws_tech_dict(instance, env, vm_id):
    attributes = instance.get("attributes")
    subnet_id = attributes.get("subnet_id")
    az = attributes.get("availability_zone")
    region = az[:-1]
    try:
        vpc_id, rh = get_aws_vpc_id(env, region, subnet_id)
    except Exception as err:
        logger.warning(f'get_aws_vpc_id failed. Error: {err}')
        vpc_id = None
    if not vpc_id or not rh:
        logger.warning('VPC could not be identified for TF server, skipping '
                       'direct import')
        return "", ""
    tech_dict = {
        "ec2_region": region,
        "availability_zone": az,
        "instance_id": vm_id,
        "vpc_id": vpc_id,
        "instance_type": attributes.get("instance_type"),
    }
    return tech_dict, rh, env

def get_vmware_tech_dict(instance, env, vm_id):
    attributes = instance.get("attributes")
    tech_dict = {
        "linked_clone": attributes.get("clone")[0].get("linked_clone"),
        # cluster =
        "moid": attributes.get("moid")
    }
    rh = env.resource_handler
    return tech_dict, rh, env

def get_aws_vpc_id(env, region, subnet_id):
    rh = env.resource_handler
    wrapper = rh.cast().get_api_wrapper()
    conn = wrapper.get_boto3_resource(
        rh.serviceaccount,
        rh.servicepasswd,
        region,
        service_name='ec2'
    )
    subnet = conn.Subnet(subnet_id)
    return subnet.vpc_id, rh


"""
Module containing all the method used in both the octtree and particle
    mesh simulations and all the global variables
"""

import multiprocessing
import copy
import pickle
import numpy as np
import astropy.units as u
import png
import pandas as pd
import matplotlib.pyplot as plt
import grav_field
from octtree import OctTree, barnes_hut_approximation
from particle_mesh import ParticleMesh

GOT_DATA = False
FORCE_LIST = []
TOTAL_PE = 0.0
PARTICLES = None
TIMESTEP = None
RET_QUEUE = multiprocessing.Queue()
PARTICLE_QUEUE = multiprocessing.Queue()
RADIUS = None
PARTICLE_COUNT = None
PARTICLE_TREE = None
LENGTH = None
PARAM_C = [0.6756, -0.1756, -0.1756, 0.6756]
PARAM_D = [1.3512, -1.7024, 1.3512]

def get_forces_raw(index):
    """
    Calculates the force on PARTICLES[index] using through brute force
        and appends it to FORCE_LIST
    """
    global FORCE_LIST
    global PARTICLES

    total_force = 0.0
    for j, particle in enumerate(PARTICLES):
        if j == index:
            continue
        total_force += grav_field.get_force(PARTICLES[index], particle)

    FORCE_LIST.append((index, total_force))

def get_forces_tree(index):
    """
    Calculates the force on PARTICLES[index] using the octree and appends
        it to FORCE_LIST
    """
    global PARTICLES
    global PARTICLE_TREE

    force = barnes_hut_approximation(PARTICLE_TREE, PARTICLES[index])
    FORCE_LIST.append((index, force))

def get_forces_mesh():
    """
    Calculates the force on PARTICLES[index] using the particle mesh
        method and appends it to FORCE_LIST
    """
    global PARTICLES
    global RADIUS

    mesh = ParticleMesh(grid_points=300, radius=RADIUS)

    mesh.insert_particles(PARTICLES)
    mesh.calculate_potential()
    mesh.calculate_force()

    forces = mesh.get_forces(PARTICLES)
    return forces

def get_energies(indices):
    """
    Calculates the potential energy of PARTICLES[indices[0]] in the field
        produced PARTICLES[indices[1]] and adds double that to TOTAL_PE
    """
    global TOTAL_PE

    potential_energy = grav_field.get_potential_energy(
        PARTICLES[indices[0]], PARTICLES[indices[1]]
    ).value
    TOTAL_PE += potential_energy * 2

def get_data(_):
    """
    Reads a copy of the PARTICLES list from PARTICLE_QUEUE and produces
        an OctTree to store as local copies
    Sets local GOT_DATA

    Called by child processes
    """
    global PARTICLE_TREE
    global PARTICLES
    global PARTICLE_QUEUE
    global GOT_DATA

    if not GOT_DATA:
        PARTICLES = PARTICLE_QUEUE.get()
        PARTICLE_TREE = OctTree(radius=RADIUS)
        PARTICLE_TREE.build_tree(PARTICLES)
        GOT_DATA = True

def send_data(pool):
    """
    Puts a deepcopy of the PARTICLES list into PARTICLE_QUEUE for each
        child process then calls get_data with each child process

    Called by main process
    """
    global PARTICLES
    global PARTICLE_QUEUE

    for _ in range(pool._processes):
        PARTICLE_QUEUE.put(copy.deepcopy(PARTICLES))

    while True:
        pool.map(get_data, range(pool._processes))
        if PARTICLE_QUEUE.empty():
            break

def send_results(_):
    """
    Sends a deepcopy of the local FORCE_LIST onto RET_QUEUE and clears
        the local versions of both GOT_DATA and FORCE_LIST

    Called by child processes
    """
    global FORCE_LIST
    global RET_QUEUE
    global GOT_DATA

    RET_QUEUE.put(copy.deepcopy(FORCE_LIST))
    FORCE_LIST.clear()
    GOT_DATA = False

def get_results(pool):
    """
    Calls send_results with each child process and reads the results
        from RET_QUEUE and returns all the results as a list of lists

    Called by main process
    """
    global RET_QUEUE

    force_list = []

    while len(force_list) < pool._processes:
        pool.map_async(send_results, range(pool._processes))
        for _ in range(pool._processes):
            results = RET_QUEUE.get()
            if len(results) > 0:
                force_list.append(results)

    return force_list

def send_energy_results(_):
    """
    Sends a copy of the local TOTAL_PE onto RET_QUEUE and clears the
        local versions of both GOT_DATA and TOTAL_PE

    Called by child processes
    """
    global TOTAL_PE
    global RET_QUEUE
    global GOT_DATA

    RET_QUEUE.put(TOTAL_PE)
    TOTAL_PE = 0.0
    GOT_DATA = False

def get_energy_results(pool):
    """
    Calls send_results with each child process and reads the results
        from RET_QUEUE and returns the sum of all the results

    Called by main process
    """
    global RET_QUEUE

    pe_list = []

    while len(pe_list) < pool._processes:
        pool.map_async(send_energy_results, range(pool._processes))
        for _ in range(pool._processes):
            result = RET_QUEUE.get()
            if result != 0.0:
                pe_list.append(result)

    return np.sum(pe_list)

def get_energy(pool):
    """
    Calculates the total potential energy and kinetic energy of the
        particles in the PARTICLES list then returns them in a list
        of [total energy, total potential, total kinetic]
    """
    global PARTICLES

    send_data(pool)

    energies = [i.kinetic_energy().value for i in PARTICLES]
    total_ke = np.sum(energies)

    indices = []
    for row in [[(i, j) for j in range(i + 1, len(PARTICLES))]
                for i in range(len(PARTICLES))]:
        for item in row:
            indices.append(item)

    pool.map(get_energies, indices)
    total_pe = get_energy_results(pool)
    total_energy = total_ke + total_pe

    return [total_energy, total_ke, total_pe]

def write_image(timestep_no, name):
    """
    Writes the locations of all the particles in the PARTICLES list to
        a 2000 x 2000 px png image with brightness representing distance
        from the closest edge of the region

    Requires a frames foldes in the current directory to save images in
    """

    img_dims = 2000
    img = [[0] * img_dims for i in range(img_dims)]
    for particle in PARTICLES:
        img_x = int(
            (particle.position[0].value / (RADIUS * 2) + 0.5) * img_dims
        )
        img_y = int(
            (particle.position[1].value / (RADIUS * 2) + 0.5) * img_dims
        )
        img_z = int(
            (particle.position[2].value / (RADIUS * 2) + 0.5) * 255
        )

        if 0 < img_x < img_dims and 0 < img_y < img_dims and 0 < img_z < 256:
            img[img_x][img_y] = img_z

    png.from_array(
        img, 'L'
    ).save(
        "frames/{}_p{}_t{}_l{}_pic{:05}.png"
        .format(
            name, PARTICLE_COUNT, TIMESTEP / (u.yr).to(u.s), LENGTH, timestep_no
        )
    )

def save_data(timestep_no, force_factor_list, mom_list,
              energy_list, timing_list, timestep_list):
    """
    Writes the required variables to a pickled file that can be read
        later to resume the simulation from this point
    """
    pickle.dump(PARTICLES,
                open("sim_data_particles.p", 'wb'))
    pickle.dump([timestep_no, TIMESTEP, RADIUS, PARTICLE_COUNT, LENGTH],
                open("sim_data.p", 'wb'))
    pickle.dump(force_factor_list,
                open("sim_data_force.p", 'wb'))
    pickle.dump(mom_list,
                open("sim_data_mom.p", 'wb'))
    pickle.dump(energy_list,
                open("sim_data_energy.p", 'wb'))
    pickle.dump(timing_list,
                open("sim_data_timing.p", 'wb'))
    pickle.dump(timestep_list,
                open("sim_data_timestep.p", 'wb'))

def read_data():
    """
    Reads the global variables from the pickeled files written to by
        save_data to resume the simulation from the last saved point
    """
    global PARTICLES
    global TIMESTEP
    global RADIUS
    global PARTICLE_COUNT
    global LENGTH

    PARTICLES = pickle.load(open("sim_data_particles.p", 'rb'))
    force_factor_list = pickle.load(open("sim_data_force.p", 'rb'))
    mom_list = pickle.load(open("sim_data_mom.p", 'rb'))
    energy_list = pickle.load(open("sim_data_energy.p", 'rb'))
    timing_list = pickle.load(open("sim_data_timing.p", 'rb'))
    timestep_list = pickle.load(open("sim_data_timestep.p", 'rb'))
    data = pickle.load(open("sim_data.p", 'rb'))
    TIMESTEP = data[1]
    RADIUS = data[2]
    PARTICLE_COUNT = data[3]
    LENGTH = data[4]
    return (data[0], force_factor_list, mom_list,
            energy_list, timing_list, timestep_list)

def write_data(args, force_factor_list, mom_list,
               energy_list, timing_list, timestep_list):
    """
    Writes the data recorded by the simulation to graphs and a .csv file
    """
    energy_interval = args.energy_interval
    timing_interval = args.time_interval

    name = None
    if args.simulation == 'g':
        name = "gal"
    elif args.simulation == 'u':
        name = "univ"

    if args.momentum:
        fig1 = plt.figure()
        ax1 = fig1.add_subplot(111)
        mom_list = np.array(mom_list).T
        ax1.plot(timestep_list, mom_list, label="Total Momentum")
        ax1.legend()
        ax1.set_ylabel("Momentum / Ns")
        ax1.set_xlabel("Time / yr")
        fig1.savefig("{}_sim_momentum_p{}_t{}_l{}.png".format
                     (name, PARTICLE_COUNT, TIMESTEP /
                      (u.yr).to(u.s), LENGTH
                     ), dpi=500)

    if not timing_interval is None:
        timing_list = np.array(timing_list).T

    if not energy_interval is None:
        fig2 = plt.figure()
        ax2 = fig2.add_subplot(111)

        energy_list = np.array(energy_list).T
        timecodes = [timestep_list[energy_interval * i]
                     for i in range(LENGTH // energy_interval + 1)]
        ax2.plot(timecodes, energy_list[0], label="Total Energy")
        ax2.plot(timecodes, energy_list[1], label="Kinetic Energy")
        ax2.plot(timecodes, energy_list[2], label="Potential Energy")
        ax2.legend()
        ax2.set_ylabel("Energy / J")
        ax2.set_xlabel("Time / yr")
        fig2.savefig("{}_sim_energy_p{}_t{}_l{}.png".format
                     (name, PARTICLE_COUNT, TIMESTEP /
                      (u.yr).to(u.s), LENGTH
                     ), dpi=500)

    if args.force:
        force_factor_list = np.array(force_factor_list).T
        fig3 = plt.figure()
        ax3 = fig3.add_subplot(111)
        ax3.errorbar(timestep_list, force_factor_list[0], force_factor_list[1])
        ax3.set_ylabel("Relative Force")
        ax3.set_xlabel("Time / yr")
        fig3.savefig("{}_sim_force_error_p{}_t{}_l{}.png".format
                     (name, PARTICLE_COUNT, TIMESTEP /
                      (u.yr).to(u.s), LENGTH
                     ), dpi=500)

    data_columns = []
    data = []
    data_width = 0
    data_length = 0
    data_index = 0

    # Initialise empty data array
    if args.momentum:
        data_width += 1
        data_length = len(mom_list)

    if args.force:
        data_width += 2
        data_length = len(force_factor_list[0])

    if not energy_interval is None:
        data_width += 3

    if not timing_interval is None:
        data_width += 2

    if data_length == 0:
        data_length = len(timestep_list)

    data = [[np.nan for _ in range(data_width)] for _ in range(data_length)]

    # Fill data array
    if args.momentum:
        for (i, mom) in enumerate(mom_list):
            data[i][data_index] = mom
        data_index += 1
        data_columns.append("Momentum / Ns")

    if args.force:
        for i in range(len(force_factor_list[0])):
            data[i][data_index] = force_factor_list[0][i]
            data[i][data_index + 1] = force_factor_list[1][i]
        data_index += 2
        data_columns.append("Mean_Force_Factor")
        data_columns.append("Force_Factor_Std_Dev")

    if not energy_interval is None:
        for i in range(len(energy_list[0])):
            data[energy_interval * i][data_index] = energy_list[0][i]
            data[energy_interval * i][data_index + 1] = energy_list[1][i]
            data[energy_interval * i][data_index + 2] = energy_list[2][i]
        data_index += 3
        data_columns.append("Total_Energy / J")
        data_columns.append("Total_Kinetic_Energy / J")
        data_columns.append("Total_Potential_Energy / J")

    if not timing_interval is None:
        for i in range(len(timing_list[0])):
            data[timing_interval * i][data_index] = timing_list[0][i]
            data[timing_interval * i][data_index + 1] = timing_list[1][i]
        data_index += 2
        if args.simulation == 'g':
            data_columns.append("Brute_Force_Timing / s")
        data_columns.append("Octtree_Timing / s")
        if args.simulation == 'u':
            data_columns.append("Mesh_Timing / s")

    if (args.momentum or args.force or not energy_interval is None
            or not timing_interval is None):
        dataframe = pd.DataFrame(data,
                                 index=timestep_list,
                                 columns=data_columns)
        dataframe.index.name = "Time / yr"
        dataframe.to_csv("{}_sim_data_p{}_t{}_l{}.csv"
                         .format(name, PARTICLE_COUNT,
                                 TIMESTEP / (u.yr).to(u.s), LENGTH))


'''
    페이지 교체 알고리즘 OPT 방식
    OPT : https://velog.io/@qweadzs/BOJ-1700-%EB%A9%80%ED%8B%B0%ED%83%AD-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%A7%81Python

    멀티탭을 모두 사용하고 있을 때, 어떤 코드를 뽑을 것인지 선택하는 문제
    - 앞으로 다시 사용하지 않을 코드
    위 조건을 만족하는 코드가 없다면
    - 가장 나중에 다시 사용할 코드
    먼저 사용할 것을 빼버리면, 다시 사용할 때 다시 꽂아야 함.
'''

from collections import deque

def solution(N, arr):
    use = []
    cnt = 0

    while(len(arr) > 0):
        # print(use ,arr)
        # 이미 사용하고 있다면 건너뜀
        if arr[0] in use:
            arr.popleft()
        else:
            if len(use) < N:
                use.append(arr.popleft())
            # 사용하고 있지 않다면, 하나를 뽑아야함
            else:
                max_idx = -1
                for idx, item in enumerate(use):
                    # 앞으로 사용하지 않을 것
                    if item not in arr:
                        use[idx] = arr.popleft()
                        cnt += 1
                        break
                    # 가장 나중에 사용할 것
                    else:
                        new_idx = arr.index(item)
                        if new_idx > max_idx:
                            max_idx = new_idx
                            max_item = item
                else:
                    use[use.index(max_item)] = arr.popleft()
                    cnt += 1
    # print(use ,arr)
    print(cnt)

if __name__ == "__main__":
    N, K = map(int, input().split())
    arr = deque(list(map(int, input().split())))

    solution(N, arr)


#Python version 3.6.0
import random as r
from collections import defaultdict
#import numpy as np
#from copy import deepcopy

class HelperFunctions():
    def __init__(self):
        pass

    def kill_ship(self,strike_area):
        self.overall_p_density = self.calculate_strategy()
        while self.open_hits:   #keeps running until all found ships are sunk
            c = self.open_hits[0]
            x , y = c
            x_axis = []
            y_axis= []
            down_list = []
            up_list = []
            r_list = []
            l_list = []

            switch = [1,1,1,1]
            for i in range(1,10): #for the remaining length of the board
                down = (x,c[1]+i)
                up = (x,c[1]-i)
                right = (c[0]+i,y)
                left = (c[0]-i, y)

                #give a contiguous area where ships might be
                #this includes the strike area and self.open_hits
                open_hits_coordinates = list(self.open_hits)
                considered_area = strike_area + open_hits_coordinates

                if down in considered_area and switch[0] ==1:
                    down_list.append(down)
                else:
                    switch[0]=0
                if up in considered_area and switch[1] ==1:
                    up_list.append(up)
                else:
                    switch[1]=0
                if right in considered_area and switch[2] ==1:
                    r_list.append(right)
                else:
                    switch[2]=0
                if left in considered_area and switch[3] ==1:
                    l_list.append(left)
                else:
                    switch[3]=0
            #I am only using these memory addresses for identification for my greedy algorithm
            #Python is not meant to manipulate the memory so i am not doing that
            memory_locations = {}
            memory_locations[hex(id(r_list))] =  r_list#(1,0)
            memory_locations[hex(id(l_list))] = l_list#(-1,0)
            memory_locations[hex(id(up_list))] = up_list#(0,-1)
            memory_locations[hex(id(down_list))] = down_list#(0,1)

            #obtain the next available square for attack
            #for all directions by removing open hits
            adjustment_set = set() #to keep track of where the open hits are
            exit_condition = 0
            while exit_condition == 0:
                exit_condition = 1
                for i in memory_locations:
                    if len(memory_locations[i]) > 0:
                        if memory_locations[i][0] in self.open_hits:
                            memory_locations[i].pop(0)
                            adjustment_set.add(i)
                            exit_condition = 0

            #find the axes with adjacent open hits
            horizontal_adj = 0
            vertical_adj = 0
            for i in adjustment_set:
                if i in [hex(id(r_list)),hex(id(l_list))]:
                    horizontal_adj = 100
                if i in [hex(id(up_list)),hex(id(down_list))]:
                    vertical_adj = 100

            #obtain the probability densities
            try:
                right_val = self.overall_p_density[r_list[0]]
            except:
                right_val = 0
            try:
                left_val = self.overall_p_density[l_list[0]]
            except:
                left_val=0
            try:
                up_val = self.overall_p_density[up_list[0]]
            except:
                up_val=0
            try:
                down_val = self.overall_p_density[down_list[0]]
            except:
                down_val = 0

            #the preferred axis has adjacent open hits
            if left_val > 0:
                left_val = left_val + horizontal_adj
            if right_val > 0:
                right_val = right_val + horizontal_adj
            if up_val > 0:
                up_val = up_val + vertical_adj
            if down_val > 0:
                down_val = down_val + vertical_adj

            #Select an axis
            if (right_val + left_val) > (up_val + down_val):
                axis=[r_list,l_list]
            elif (right_val + left_val) < (up_val + down_val):
                axis=[up_list,down_list]
            else:
                axis=r.choice([[up_list,down_list],[r_list,l_list]])
            #process the selected axis
            strike_area = self.process_axis(c,axis,strike_area)
        return strike_area

    def process_axis(self,c,axis, strike_area):
        current_hits=[c]
        visited_directions =set()
        while (len(axis[0]) + len(axis[1])) > 0:
            current_list = self.maxchoice(axis[0],axis[1])   #current_list is a reference to the original list
            if current_list == "skip":
                #probability for both directions is zero
                #return to open hits loop
                break
            visited_directions.add(hex(id(current_list)))
            attack1 = current_list.pop(0)
            self.board[attack1[0]][attack1[1]].struck =1
            self.strike_summary.append(attack1)
            strike_area.remove(attack1)

            #In the event of a hit
            if self.board[attack1[0]][attack1[1]].occupied ==1:
                current_hits.append(attack1)
                self.open_hits.append(attack1)
                #ship updating
                for i in self.ships:
                    if i.sunk==1:
                        continue
                    new_sunk = i.update(self.board)
                    if new_sunk == 1:
                        self.ships_count = self.ships_count - 1
                        for j in i.location:
                            self.board[j[0]][j[1]].shipsunk = 1
                            try:
                                current_hits.remove((j[0],j[1]))
                            except:
                                pass
                            try:
                                self.open_hits.remove((j[0],j[1]))
                            except:
                                pass
                        #empty out this direction
                        del current_list[:] #empty out original and reference list
                        #switch to opposite list
                        if hex(id(current_list)) ==hex(id(axis[0])):
                            current_list = axis[1]
                        else:
                            current_list = axis[0]
                        if len(current_hits) <2:  #back to open hits loop if another ship is not suspected
                            del current_list[:]
                        break
             #in the event of a miss
            else:
                del current_list[:]
            #recalculating strategy after every strike
            self.overall_p_density = self.calculate_strategy()
            #end of axis loop
        return strike_area

    def maxchoice(self, list1, list2):
        try:
            a = self.overall_p_density[list1[0]]
        except:
            a = 0
        try:
            b = self.overall_p_density[list2[0]]
        except:
            b=0
        if a>b:
            return list1
        if a<b:
            return list2
        if b + a == 0:
        #no hit possible so return to the open hits loop
            return "skip"
        if b == a:
            return r.choice([list1,list2])

    def minmax_ships(self):
        ship_lengths = []
        minimum_ship_length = 0
        maximum_ship_length = 0
        for i in self.ships:
            if i.sunk == 0:
                ship_lengths.append(i.length)
        if ship_lengths:
            minimum_ship_length = min(ship_lengths)
            maximum_ship_length = max(ship_lengths)
        return minimum_ship_length, maximum_ship_length, ship_lengths

    #compute strategy based on the probability density method
    def calculate_strategy(self):
        '''output: probability density'''
        min_ship_length, max_ship_length, available_ships = self.minmax_ships()
        densities = []
        for length in available_ships:
            open_slots,probability_density = self.list_possibilities(length, mode =1)
            densities.append(probability_density)
        overall_p_density = defaultdict(int)
        for i in densities:
            for k in i:
                overall_p_density[k] = overall_p_density[k] + i[k]
        return overall_p_density


import torch
from torch import nn
from torch import optim
import torch.nn.functional as F
from torchvision import datasets, transforms, models
from collections import OrderedDict
import time

data_dir = './images_category/images_category'
train_transforms = transforms.Compose([transforms.RandomRotation(30),
                                       transforms.RandomResizedCrop(224),
                                       transforms.RandomHorizontalFlip(),
                                       transforms.ToTensor(),
                                       transforms.Normalize([185.78795853, 183.04217856, 182.72907307],
                                                            [69.36995286, 70.42220844, 70.29462924])])

test_transforms = transforms.Compose([transforms.Resize(255),
                                      transforms.CenterCrop(224),
                                      transforms.ToTensor(),
                                      transforms.Normalize([185.78795853, 183.04217856, 182.72907307],
                                                           [69.36995286, 70.42220844, 70.29462924])])

# Pass transforms in here, then run the next cell to see how the transforms look
train_data = datasets.ImageFolder(data_dir + '/train', transform=train_transforms)
test_data = datasets.ImageFolder(data_dir + '/test', transform=test_transforms)

trainloader = torch.utils.data.DataLoader(train_data, batch_size=32, shuffle=True)
testloader = torch.utils.data.DataLoader(test_data, batch_size=32, shuffle=True)

model = models.resnet101(pretrained=True)

for param in model.parameters():
    param.requires_grad = False

for param in model.layer4.parameters():
    param.requires_grad = True

from collections import OrderedDict
classifier = nn.Sequential(OrderedDict([
                          ('fc1', nn.Linear(2048, 800)),
                          ('relu', nn.ReLU()),
                          ('dropout', nn.Dropout(0.2)),
                          ('fc2', nn.Linear(800, 300)),
                          ('relu', nn.ReLU()),
                          ('dropout', nn.Dropout(0.2)),
                          ('fc3', nn.Linear(300, 121)),
                          ('output', nn.LogSoftmax(dim=1))
                          ]))

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

model.fc = classifier
criterion = nn.NLLLoss()

# Only train the classifier parameters, feature parameters are frozen
optimizer = optim.Adam(model.fc.parameters(), lr=0.0008)
model.to(device);

epochs = 15
steps = 0
running_loss = 0
for epoch in range(epochs):
    for inputs, labels in trainloader:
        # Move input and label tensors to the default device
        inputs, labels = inputs.to(device), labels.to(device)

        optimizer.zero_grad()

        logps = model.forward(inputs)
        loss = criterion(logps, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()

    test_loss = 0
    accuracy = 0
    model.eval()
    with torch.no_grad():
        for inputs, labels in testloader:
            inputs, labels = inputs.to(device), labels.to(device)
            logps = model.forward(inputs)
            batch_loss = criterion(logps, labels)

            test_loss += batch_loss.item()

            # Calculate accuracy
            ps = torch.exp(logps)
            top_p, top_class = ps.topk(1, dim=1)
            equals = top_class == labels.view(*top_class.shape)
            accuracy += torch.mean(equals.type(torch.FloatTensor)).item()

    print(f"Epoch {epoch+1}/{epochs}.. "
          f"Test loss: {test_loss/len(testloader):.3f}.. "
          f"Test accuracy: {accuracy/len(testloader):.3f}")
    running_loss = 0
    model.train()
    if accuracy/len(testloader) > 0.812:
        torch.save(model.state_dict(), '/warehouse/weight_resnet101_pytorch.pth')


import os
import json

CONFIG_FILE = "config.json"
ROLEPLAY_DIR = "roleplay/"

def print_config(config):
    print(f"- Role: {config['role']}")
    print(f"- Memorable: {config['memorable']}")
    print(f"- Temperature: {config['temperature']}")
    print(f"- Presence Penalty: {config['presence_penalty']}")
    print(f"- Frequency Penalty: {config['frequency_penalty']}")
    print()

def init_config():
    oldConfig = {}
    if os.path.exists(CONFIG_FILE):
        oldConfig = load_config()

    def config_key(key, prompt, defaultValBackUp):
        defaultVal = oldConfig.get(key)
        if defaultVal is None:
            defaultVal = defaultValBackUp
        res = input(f"{prompt}(Default: {defaultVal}): ").strip()
        if not res:
            res = str(defaultVal)
        return res

    print("Configuring...")
    newConfig = {}

    newConfig["roleDir"] = config_key("roleDir", "Roleplay Content Directory", ROLEPLAY_DIR)

    roleFileList = os.listdir(ROLEPLAY_DIR)
    if roleFileList is None:
        roleFileList[0] = None
    roleListStr = " ".join(roleFile[:-4] for roleFile in roleFileList)
    newConfig["role"] = config_key("role", f"Role ({roleListStr})", roleFileList[0][:-4])

    newConfig["model"] = float(config_key("model", "Model", "gpt-3-turbo"))
    newConfig["memorable"] = config_key("memorable", "Memorable (y/n)", "n")
    newConfig["temperature"] = float(config_key("temperature", "Temperature", 1))
    newConfig["presence_penalty"] = float(config_key("presence_penalty", "Presence Penalty", 0))
    newConfig["frequency_penalty"] = float(config_key("frequency_penalty", "Frequency Penalty", 0))

    return newConfig

def load_config():
    return json.load(open(CONFIG_FILE))

def save_config(config):
    try:
        json.dump(
            config,
            open(CONFIG_FILE, mode="w", encoding="utf-8"),
            indent=4
        )
    except Exception as e:
        print(f"\nError: {e.__class__.__name__}. Failed to create config file.")
        exit(1)