"""
    IBPPose for COCO Keypoint, implemented in PyTorch.
    Original paper: 'Simple Pose: Rethinking and Improving a Bottom-up Approach for Multi-Person Pose Estimation,'
    https://arxiv.org/abs/1911.10529.
"""

__all__ = ['ibppose_coco']

import torch
from torch import nn
from common import conv1x1_block, conv3x3_block, conv7x7_block, SEBlock

class IbpResBottleneck(nn.Module):
    """
    Bottleneck block for residual path in the residual unit.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    out_channels : int
        Number of output channels.
    stride : int or tuple/list of 2 int
        Strides of the convolution.
    bias : bool, default False
        Whether the layer uses a bias vector.
    bottleneck_factor : int, default 2
        Bottleneck factor.
    activation : function or str or None, default nn.ReLU(inplace=True)
        Activation function or name of activation function.
    """
    def __init__(self,
                 in_channels,
                 out_channels,
                 stride,
                 bias=False,
                 bottleneck_factor=2,
                 activation=(lambda: nn.ReLU(inplace=True))):
        super(IbpResBottleneck, self).__init__()
        mid_channels = out_channels // bottleneck_factor

        self.conv1 = conv1x1_block(
            in_channels=in_channels,
            out_channels=mid_channels,
            bias=bias,
            activation=activation)
        self.conv2 = conv3x3_block(
            in_channels=mid_channels,
            out_channels=mid_channels,
            stride=stride,
            bias=bias,
            activation=activation)
        self.conv3 = conv1x1_block(
            in_channels=mid_channels,
            out_channels=out_channels,
            bias=bias,
            activation=None)

    def forward(self, x):
        x = self.conv1(x)
        x = self.conv2(x)
        x = self.conv3(x)
        return x

class IbpResUnit(nn.Module):
    """
    ResNet-like residual unit with residual connection.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    out_channels : int
        Number of output channels.
    stride : int or tuple/list of 2 int, default 1
        Strides of the convolution.
    bias : bool, default False
        Whether the layer uses a bias vector.
    bottleneck_factor : int, default 2
        Bottleneck factor.
    activation : function or str or None, default nn.ReLU(inplace=True)
        Activation function or name of activation function.
    """
    def __init__(self,
                 in_channels,
                 out_channels,
                 stride=1,
                 bias=False,
                 bottleneck_factor=2,
                 activation=(lambda: nn.ReLU(inplace=True))):
        super(IbpResUnit, self).__init__()
        self.resize_identity = (in_channels != out_channels) or (stride != 1)

        self.body = IbpResBottleneck(
            in_channels=in_channels,
            out_channels=out_channels,
            stride=stride,
            bias=bias,
            bottleneck_factor=bottleneck_factor,
            activation=activation)
        if self.resize_identity:
            self.identity_conv = conv1x1_block(
                in_channels=in_channels,
                out_channels=out_channels,
                stride=stride,
                bias=bias,
                activation=None)
        self.activ = nn.ReLU(inplace=True)

    def forward(self, x):
        if self.resize_identity:
            identity = self.identity_conv(x)
        else:
            identity = x
        x = self.body(x)
        x = x + identity
        x = self.activ(x)
        return x

class IbpBackbone(nn.Module):
    """
    IBPPose backbone.

    Parameters:
    ----------
    in_channels : int
        Number of input channels.
    out_channels : int
        Number of output channels.
    activation : function or str or None
        Activation function or name of activation function.
    """
    def __init__(self,
                 in_channels,
                 out_channels,
                 activation):
        super(IbpBackbone, self).__init__()
        dilations = (3, 3, 4, 4, 5, 5)
        mid1_channels = out_channels // 4
        mid2_channels = out_channels // 2

        self.conv1 = conv7x7_block(
            in_channels=in_channels,
            out_channels=mid1_channels,
            stride=2,
            activation=activation)
        self.res1 = IbpResUnit(
            in_channels=mid1_channels,
            out_channels=mid2_channels)
        self.pool = nn.MaxPool2d(
            kernel_size=2,
            stride=2)
        self.res2 = IbpResUnit(
            in_channels=mid2_channels,
            out_channels=mid2_channels)
        self.dilation_branch = nn.Sequential()
        for i, dilation in enumerate(dilations):
            self.dilation_branch.add_module("block{}".format(i + 1), conv3x3_block(
                in_channels=mid2_channels,
                out_channels=mid2_channels,
                padding=dilation,
                dilation=dilation,
                activation=activation))

    def forward(self, x):
        x = self.conv1(x)
        x = self.res1(x)
        x = self.pool(x)
        x = self.res2(x)
        y = self.dilation_branch(x)
        x = torch.cat((x, y), dim=1)
        return x

class Hourglass(nn.Module):
    """
    Instantiate an n order Hourglass Network block using recursive trick.
    """
    def __init__(self,
                 channels,  # input and output channels
                 depth,  # oder number
                 increase,  # increased channels while the depth grows
                 use_bn,
                 activation):
        super(Hourglass, self).__init__()
        self.channels = channels
        self.depth = depth
        self.increase = increase
        self.use_bn = use_bn
        self.activation = activation

        self.hg = self._make_hour_glass()

        self.down = nn.MaxPool2d(
            kernel_size=2,
            stride=2)
        self.up = nn.Upsample(
            scale_factor=2,
            mode="nearest")

    def _make_lower_residual(self,
                             depth_id):
        pack_layers = [
            IbpResUnit(
                in_channels=self.channels + self.increase * depth_id,
                out_channels=self.channels + self.increase * depth_id,
                activation=self.activation),
            IbpResUnit(
                in_channels=self.channels + self.increase * depth_id,
                out_channels=self.channels + self.increase * (depth_id + 1),
                activation=self.activation),
            IbpResUnit(
                in_channels=self.channels + self.increase * (depth_id + 1),
                out_channels=self.channels + self.increase * depth_id,
                activation=self.activation),
            conv3x3_block(
                in_channels=self.channels + self.increase * depth_id,
                out_channels=self.channels + self.increase * depth_id,
                bias=(not self.use_bn),
                use_bn=self.use_bn,
                activation=self.activation),
        ]
        return pack_layers

    def _make_single_residual(self, depth_id):
        # the innermost conve layer, return as a layer item
        # ###########  Index: 4
        return IbpResUnit(
            in_channels=self.channels + self.increase * (depth_id + 1),
            out_channels=self.channels + self.increase * (depth_id + 1),
            activation=self.activation)

    def _make_hour_glass(self):
        """
        pack conve layers modules of hourglass block
        :return: conve layers packed in n hourglass blocks
        """
        hg = []
        for i in range(self.depth):
            res = self._make_lower_residual(i)
            if i == (self.depth - 1):
                res.append(self._make_single_residual(i))
            hg.append(nn.ModuleList(res))
        return nn.ModuleList(hg)

    def _hour_glass_forward(self,
                            depth_id,
                            x,
                            up_fms):
        """
        built an hourglass block whose order is depth_id
        :param depth_id: oder number of hourglass block
        :param x: input tensor
        :return: output tensor through an hourglass block
        """
        up1 = self.hg[depth_id][0](x)
        low1 = self.down(x)
        low1 = self.hg[depth_id][1](low1)
        if depth_id == (self.depth - 1):  # except for the highest-order hourglass block
            low2 = self.hg[depth_id][4](low1)
        else:
            # call the lower-order hourglass block recursively
            low2 = self._hour_glass_forward(depth_id + 1, low1, up_fms)
        low3 = self.hg[depth_id][2](low2)
        up_fms.append(low2)
        # ######################## # if we don't consider 8*8 scale
        # if depth_id < self.depth - 1:
        #     self.up_fms.append(low2)
        up2 = self.up(low3)
        deconv1 = self.hg[depth_id][3](up2)
        # deconv2 = self.hg[depth_id][4](deconv1)
        # up1 += deconv2
        # out = self.hg[depth_id][5](up1)  # relu after residual add
        return up1 + deconv1

    def forward(self, x):
        """
        :param: x a input tensor warpped wrapped as a list
        :return: 5 different scales of feature maps, 128*128, 64*64, 32*32, 16*16, 8*8
        """
        up_fms = []  # collect feature maps produced by low2 at every scale
        feature_map = self._hour_glass_forward(0, x, up_fms)
        return [feature_map] + up_fms[::-1]

class Merge(nn.Module):
    """
    Change the channel dimension of the input tensor
    """
    def __init__(self,
                 x_dim,
                 y_dim,
                 use_bn):
        super(Merge, self).__init__()
        self.conv = conv1x1_block(
            in_channels=x_dim,
            out_channels=y_dim,
            bias=(not use_bn),
            use_bn=use_bn,
            activation=None)

    def forward(self, x):
        return self.conv(x)

class Features(nn.Module):
    """
    Input: feature maps produced by hourglass block
    Return: 5 different scales of feature maps, 128*128, 64*64, 32*32, 16*16, 8*8
    """
    def __init__(self,
                 out_channels,
                 use_bn,
                 increase,
                 scales):
        super(Features, self).__init__()
        self.scales = scales
        # Regress 5 different scales of heatmaps per stack
        self.before_regress = nn.ModuleList(
            [nn.Sequential(
                conv3x3_block(
                    in_channels=(out_channels + i * increase),
                    out_channels=out_channels,
                    bias=(not use_bn),
                    use_bn=use_bn),
                conv3x3_block(
                    in_channels=out_channels,
                    out_channels=out_channels,
                    bias=(not use_bn),
                    use_bn=use_bn),
                SEBlock(channels=out_channels),
            ) for i in range(scales)])

    def forward(self, fms):
        return [self.before_regress[i](fms[i]) for i in range(self.scales)]

class IbpPose(nn.Module):
    def __init__(self,
                 nstack,
                 backbone_out_channels,
                 oup_dim,
                 bn=False,
                 increase=128,
                 init_weights=True,
                 in_channels=3,
                 **kwargs):
        """
        Pack or initialize the trainable parameters of the network
        :param nstack: number of stack
        :param backbone_out_channels: input tensor channels fed into the hourglass block
        :param oup_dim: channels of regressed feature maps
        :param bn: use batch normalization
        :param increase: increased channels once down-sampling
        :param kwargs:
        """
        super(IbpPose, self).__init__()
        activation = (lambda: nn.LeakyReLU(inplace=True))
        self.scales = 5
        self.nstack = nstack

        self.backbone = IbpBackbone(
            in_channels=in_channels,
            out_channels=backbone_out_channels,
            activation=activation)

        self.hourglass = nn.ModuleList([Hourglass(
            channels=backbone_out_channels,
            depth=4,
            increase=increase,
            use_bn=bn,
            activation=activation) for _ in range(nstack)])

        self.features = nn.ModuleList([Features(
            out_channels=backbone_out_channels,
            use_bn=bn,
            increase=increase,
            scales=self.scales) for _ in range(nstack)])

        self.outs = nn.ModuleList(
            [nn.ModuleList([conv1x1_block(
                in_channels=backbone_out_channels,
                out_channels=oup_dim,
                bias=True,
                use_bn=False,
                activation=None) for j in range(self.scales)]) for i in
             range(nstack)])

        self.merge_features = nn.ModuleList(
            [nn.ModuleList([Merge(
                backbone_out_channels,
                backbone_out_channels + j * increase,
                use_bn=bn) for j in range(self.scales)]) for i in range(nstack - 1)])

        self.merge_preds = nn.ModuleList(
            [nn.ModuleList([Merge(
                oup_dim,
                backbone_out_channels + j * increase,
                use_bn=bn) for j in range(self.scales)]) for i in range(nstack - 1)])

        if init_weights:
            self._initialize_weights()

    def _initialize_weights(self):
        for m in self.modules():
            if isinstance(m, nn.Conv2d):
                m.weight.data.normal_(0, 0.001)
                if m.bias is not None:
                    m.bias.data.zero_()
            elif isinstance(m, nn.BatchNorm2d):
                m.weight.data.fill_(1)
                m.bias.data.zero_()
            elif isinstance(m, nn.Linear):
                torch.nn.init.normal_(m.weight.data, 0, 0.01)
                m.bias.data.zero_()

    def forward(self, imgs):
        x = imgs
        x = self.backbone(x)
        pred = []
        for i in range(self.nstack):
            preds_instack = []
            hourglass_feature = self.hourglass[i](x)

            if i == 0:
                features_cache = [torch.zeros_like(hourglass_feature[scale]) for scale in range(self.scales)]
            else:
                hourglass_feature = [hourglass_feature[scale] + features_cache[scale] for scale in range(self.scales)]

            features_instack = self.features[i](hourglass_feature)

            for j in range(self.scales):
                preds_instack.append(self.outs[i][j](features_instack[j]))
                if i != self.nstack - 1:
                    if j == 0:
                        x = x + self.merge_preds[i][j](preds_instack[j]) +\
                            self.merge_features[i][j](features_instack[j])
                        features_cache[j] = self.merge_preds[i][j](preds_instack[j]) +\
                                            self.merge_features[i][j](features_instack[j])

                    else:
                        # reset the res caches
                        features_cache[j] = self.merge_preds[i][j](preds_instack[j]) +\
                                            self.merge_features[i][j](features_instack[j])
            pred.append(preds_instack)

        y = pred[-1][0]
        return y

def ibppose_coco(pretrained=False, num_classes=3, in_channels=3, **kwargs):
    model = IbpPose(4, 256, 54, bn=True, **kwargs)
    return model

def _calc_width(net):
    import numpy as np
    net_params = filter(lambda p: p.requires_grad, net.parameters())
    weight_count = 0
    for param in net_params:
        weight_count += np.prod(param.size())
    return weight_count

def _test():
    import torch

    pretrained = False

    models = [
        ibppose_coco,
    ]

    for model in models:

        net = model(pretrained=pretrained)

        # net.train()
        net.eval()
        weight_count = _calc_width(net)
        print("m={}, {}".format(model.__name__, weight_count))
        assert (model != ibppose_coco or weight_count == 129050040)

        x = torch.randn(14, 3, 256, 256)
        y = net(x)
        y.sum().backward()
        assert (tuple(y.size()) == (14, 54, 64, 64))

if __name__ == "__main__":
    _test()


#!/usr/bin/env python
from PIL import Image, ImageFilter
import os
import argparse
import colorsys
from termcolor import cprint
from math import sqrt

outputPixelValues = ['.',':','"','*',"x","X","#","$","@"]
# ['.',':','*',"x","X","#","$","@"]
class Size():
    Fit = 1
    Default = 2
    Custom = 3

parser = argparse.ArgumentParser(description='Output an image into terminal. By default scales according to terminal width whilst preserving ratio.')
parser.add_argument('image', metavar='Img', type=str,
                    help='The input image to be printed to terminal')
parser.add_argument('-F',  action='store_true',
                    help='Make the image fit the terminal window')
parser.add_argument('-S', nargs = 2, type=int,
                    help='Scale the image to custom size (width, height)')
parser.add_argument('-C', action='store_true',
                    help='Try to output the image coloured')
parser.add_argument('-T', action='store_true',
                    help='Use custom thresholding. Better for images with a lot of similar colour')
parser.add_argument('--no-ascii', action='store_true',
                    help="Don't use ascii for colour and use coloured bg instead")
parser.add_argument('-E', action='store_true',
                    help='Draw edges from the image instead')

# gets the corresponding character to pixel value
# decides according to threshold values or 0-255 range
def getPixelChar(value,thresholds=None):
    if (thresholds != None):
        for m in range(len(thresholds)):
            if (value <= thresholds[m]):
                return outputPixelValues[m]

    size = len(outputPixelValues)

    for p in range(size):
        if (255/(size-p) >= value):
            return(outputPixelValues[p])

def printPixelGrey(pixel,thresholds):
    print(getPixelChar(pixel,thresholds),end="")

# separate pixel values into n bins with approx same size where n is the number of output chars
def thresholdImage(array):

    values = {}

    for p in array:
        if (p in values):
            values[p] += 1
        else:
            values[p] = 1

    thresholds = []
    curSum = 0
    for v in dict(sorted(values.items())):
        curSum += values[v]
        if (curSum > len(img.getdata())/len(outputPixelValues)):
            curSum = 0
            thresholds.append(v)
    return thresholds

def printPixelColour(pixel,thresholds):
    (h , s , v) = colorsys.rgb_to_hsv(pixel[0],pixel[1],pixel[2])

    char = getPixelChar(v,thresholds)
    colour = "red"
    if (h <= 30/360):
        colour = "red"
    elif (h <= 70/360):
        colour = "yellow"
    elif (h <= 160/360):
        colour = "green"
    elif (h <=200/360):
        colour = "cyan"
    elif (h <= 280/360):
        colour = "blue"
    else:
        colour = "magenta"

    if (s <= 0.05):
        colour = "black"
    elif (s >= 0.8):
        colour = "white"

    if (args.no_ascii):
        cprint(" ",colour,force_color=True,on_color="on_"+colour,end="")
    else:
        cprint(char,colour,force_color=True,end="")

def printImage(img):
    (w , _) = os.get_terminal_size()
    wDif = w - img.width
    if (wDif < 0):
        wDif = 0
    wDif //= 2

    thresholds = None
    if (args.T):
        if (args.C == False):
            thresholds = thresholdImage(sorted(list(img.getdata())))
        else:
            array = []
            for p in img.getdata():
                (_ , _ , v) = colorsys.rgb_to_hsv(p[0],p[1],p[2])
                array.append(v)
            thresholds = thresholdImage(sorted(array))

    for i in range(img.height):
        for j in range(img.width+wDif):
            if (j < wDif):
                print(' ',end="")
            else:
                pixel = img.getpixel((j-wDif,i))
                if (args.C):
                    printPixelColour(pixel,thresholds)
                else:
                    printPixelGrey(pixel,thresholds)
        print()

def resizeImg(type,img):
    (w,h) = os.get_terminal_size()
    ratio = img.height/img.width
    if (type == Size.Fit):
        newW = int(h*2/ratio)
        if (newW > w):
            img = img.resize((w,int(ratio*w/2)))
        else:
            img = img.resize((newW,h))
    elif (type == Size.Default):
        img = img.resize((w,int(w*ratio/2)))
    elif (type == Size.Custom):
        img = img.resize(args.S)
    return img

args = parser.parse_args()

if (args.no_ascii and args.C == False):
        parser.error('Cannot use --no-ascii without -C flag.')
if (args.S and args.F):
        parser.error('Can only use either F or S')

originalImage = Image.open(args.image)
greyImage = originalImage.convert('L')

if (args.F):
    type = Size.Fit
elif(args.S != None):
    type = Size.Custom
else:
    type = Size.Default

img = resizeImg(type,originalImage)
if (args.C == False):
    img = resizeImg(type,greyImage)
if (args.E):
    img = resizeImg(type,greyImage).filter(ImageFilter.FIND_EDGES)
printImage(img)


import ply.lex as lex
from ply.lex import TOKEN

class CoolLexer:
	"""
	CoolLexer Class.

	Deals with lexical analysis of Cool programs

	To use the lexer, create an object from the class and feed the source code to the object by calling
	'input(str)', where 'str' is the program's code as a string.

	New tokens are fetched from the lexer using the 'token()' function (1 by 1).

	To access the last token that was fetched without moving on to a new token, use 'last_token' property
	"""

	def __init__(self):
		# Properties used by ply.lex
		self.tokens = self.token_names + tuple(self.reserved_keywords.values())  # ply tokens collection
		self.reserved = self.reserved_keywords.keys()  # ply reserved keywords map

		# Build Ply.Lex
		self.lexer = lex.lex(module=self)

		# Save program's code to check for error's columns
		self.program = ""

	def test(self,input):
		"""
		Prints all tokens from the input source code to stdout.
		"""
		self.input(input)
		a = self.lexer.token()
		while a:
			print(a)
			a = self.lexer.token()

	def input(self, input):
		"""
		Feeds input source code to the lexical analyzer.
		"""
		self.lexer.input(input)

	def token(self):
		"""
		Returns the next token in the program's code. This token can be accessed
		again with the last_token property.
		"""
		self.last_token = self.lexer.token()
		return self.last_token

   #################################### ITERATOR PROTOCOL ############################################

	def __iter__(self):
		return self

	def __next__(self):
		t = self.token()
		if t is None:
			raise StopIteration
		return t

	def next(self):
		return self.__next__()

	# ####################################	TOKENS DEFINITIONS	######################################

	#	 Collection of COOL Syntax Tokens.
	token_names = (
		# Identifiers
		"ID", "TYPE",

		# Primitive Types
		"INTEGER", "STRING", "BOOLEAN",

		# Literals
		"LPAREN", "RPAREN", "LBRACE", "RBRACE", "COLON", "COMMA", "DOT", "SEMICOLON", "AT",

		# Operators
		"PLUS", "MINUS", "MULTIPLY", "DIVIDE", "EQ", "LT", "LTEQ", "ASSIGN", "INT_COMP",

		# Special Operators
		"ARROW"
	)

	#	 Map of COOL reserved keywords.
	reserved_keywords = {
		"case": "CASE",
		"class": "CLASS",
		"else": "ELSE",
		"esac": "ESAC",
		"fi": "FI",
		"if": "IF",
		"in": "IN",
		"inherits": "INHERITS",
		"isvoid": "ISVOID",
		"let": "LET",
		"loop": "LOOP",
		"new": "NEW",
		"of": "OF",
		"pool": "POOL",
		"self": "SELF",
		"then": "THEN",
		"while": "WHILE",
		"not": "NOT"
	}

	# #################################### LEXICAL RULES	######################################

	# Ignore rule for single line comments
	t_ignore_SINGLE_LINE_COMMENT = r"\-\-[^\n]*"

	# Simple tokens
	t_LPAREN = r'\('        # (
	t_RPAREN = r'\)'        # )
	t_LBRACE = r'\{'        # {
	t_RBRACE = r'\}'        # }
	t_COLON = r'\:'         # :
	t_COMMA = r'\,'         # ,
	t_DOT = r'\.'           # .
	t_SEMICOLON = r'\;'     # ;
	t_AT = r'\@'            # @
	t_MULTIPLY = r'\*'      # *
	t_DIVIDE = r'\/'        # /
	t_PLUS = r'\+'          # +
	t_MINUS = r'\-'         # -
	t_INT_COMP = r'~'       # ~
	t_LT = r'\<'            # <
	t_EQ = r'\='            # =
	t_LTEQ = r'\<\='        # <=
	t_ASSIGN = r'\<\-'      # <-
	t_ARROW = r'\=\>'       # =>

	@TOKEN(r"(true|false)")
	def t_BOOLEAN(self, token):
		token.value = True if token.value == "true" else False
		return token

	@TOKEN(r"\d+")
	def t_INTEGER(self, token):
		token.value = int(token.value)
		return token

	@TOKEN(r"[A-Z][a-zA-Z_0-9]*")
	def t_TYPE(self, token):
		token.type = self.reserved_keywords.get(token.value, 'TYPE')
		return token

	@TOKEN(r"[a-z_][a-zA-Z_0-9]*")
	def t_ID(self, token):
		token.type = self.reserved_keywords.get(token.value, 'ID')
		return token

	@TOKEN(r"\n+")
	def t_newline(self, token):
		token.lexer.lineno += len(token.value)

	# Ignore Whitespace Character Rule
	t_ignore = ' \t\r\f'

	########################## STATEFUL LEXICAL RULES ###############################

	# LEXER STATES
	@property
	def states(self):
		return (
			("STRING", "exclusive"),
			("COMMENT", "exclusive")
		)

	######################### THE STRING STATE #####################

	@TOKEN(r"\"")
	def t_start_string(self, token):
		token.lexer.push_state("STRING")
		token.lexer.string_backslashed = False
		token.lexer.stringbuf = ""

	@TOKEN(r"\n")
	def t_STRING_newline(self, token):
		token.lexer.lineno += 1
		if not token.lexer.string_backslashed:
			print("String newline not escaped")
			token.lexer.skip(1)
		else:
			token.lexer.string_backslashed = False

	@TOKEN(r"\"")
	def t_STRING_end(self, token):
		if not token.lexer.string_backslashed:
			token.lexer.pop_state()
			token.value = token.lexer.stringbuf
			token.type = "STRING"
			return token
		else:
			token.lexer.stringbuf += '"'
			token.lexer.string_backslashed = False

	@TOKEN(r"[^\n]")
	def t_STRING_anything(self, token):
		if token.lexer.string_backslashed:
			if token.value == 'b':
					token.lexer.stringbuf += '\b'
			elif token.value == 't':
					token.lexer.stringbuf += '\t'
			elif token.value == 'n':
					token.lexer.stringbuf += '\n'
			elif token.value == 'f':
					token.lexer.stringbuf += '\f'
			elif token.value == '\\':
					token.lexer.stringbuf += '\\'
			else:
					token.lexer.stringbuf += token.value
			token.lexer.string_backslashed = False
		else:
			if token.value != '\\':
					token.lexer.stringbuf += token.value
			else:
					token.lexer.string_backslashed = True

	# STRING error handler
	def t_STRING_error(self, token):
		print("Illegal character! Line: {0}, character: {1}".format(token.lineno, token.value[0]))
		token.lexer.skip(1)

	# STRING ignored characters
	t_STRING_ignore = ''

	####################### THE COMMENT STATE ######################
	@TOKEN(r"\(\*")
	def t_start_comment(self, token):
		token.lexer.push_state("COMMENT")
		token.lexer.comment_count = 0

	@TOKEN(r"\(\*")
	def t_COMMENT_startanother(self, t):
		t.lexer.comment_count += 1

	@TOKEN(r"\*\)")
	def t_COMMENT_end(self, token):
		if token.lexer.comment_count == 0:
			token.lexer.pop_state()
		else:
			token.lexer.comment_count -= 1

	# COMMENT error rule
	def t_COMMENT_error(self, token):
		token.lexer.skip(1)

	# COMMENT ignored characters
	t_COMMENT_ignore = ''

	##################### ERROR REPORTING RULE ######################

	def t_error(self, token):
		line_start = self.program.rfind('\n', 0, token.lexpos) + 1
		column = (token.lexpos - line_start) + 1
		print("Illegal character! Line: {0}, Column: {1}".format(token.lineno, column))
		token.lexer.skip(1)

# #----------- TESTS

# s = CoolLexer()
# fpath = "D:\Scripts\cool-compiler-hieu-luis-alejandro\examples\\graph.cl"
# with open(fpath, encoding="utf-8") as file:
# 	cool_program_code = file.read()
# 	s.test(cool_program_code)


import asyncio
import websockets
import json

# Diccionario para mantener un registro de las conexiones de los ESP32
connections = {}
client_names = {}

async def handle_client(websocket, path):
    try:
        # Pide un nombre o identificador al cliente
        await websocket.send("*")
        name = await websocket.recv()

        # Almacena la conexión en el diccionario
        connections[websocket.remote_address] = websocket
        # Almacena el nombre en el diccionario de nombres
        client_names[websocket.remote_address] = name

        print(f"Cliente {websocket.remote_address} se identificó como {name}")

        while True:
            try:
                # Recibe datos del cliente
                data = await websocket.recv()

                # Decodifica los datos JSON
                json_data = json.loads(data)
                print(f"Recibido desde {websocket.remote_address}: {json_data}")

                # Procesa los datos según sea necesario

                # Envía una respuesta al cliente
                response = {"mensaje": "Respuesta desde el servidor"}
                await websocket.send(json.dumps(response))
            except websockets.ConnectionClosed:
                print(f"Conexión cerrada por el cliente {websocket.remote_address}")
                break

    except websockets.ConnectionClosed:
        pass

async def main():
    server = await websockets.serve(handle_client, "LocalHost", 12345)

    print("Servidor WebSocket iniciado")

    await server.wait_closed()

if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())


import statistics
import pandas as pd
import os
os.system("cls")
df = pd.read_csv("Internet Users.csv")
users = df["InternetUsers"].tolist()
sum = 0
for i in users:
    sum+=float(i)
mean = sum/len(users)

n = len(users)
if n%2==0:
    n1 = users[n//2]
    n2 = users[(n//2)-1]
    median = (n1+n2)/2
else:
    median = users[n//2]

mode = statistics.mode(users)
print("Mode: " + str(mode))
print("Median: " + str(median))
print("Mean: " + str(mean))


from __future__ import annotations

import typing

if typing.TYPE_CHECKING:
    import hikari

    EventImpl = typing.Callable[..., typing.Coroutine[typing.Any, typing.Any, None]]

__all__: tuple[str, ...] = ("mark",)

def mark(event_impl: EventImpl) -> EventImpl:
    setattr(event_impl, "__mark__", get_event_type(event_impl))
    return event_impl

def get_event_type(event_impl: EventImpl) -> hikari.Event:
    return typing.get_type_hints(event_impl)["event"]


import pandas as pd
from time import time
from nltk.sentiment import SentimentIntensityAnalyzer

DATASET = "amazon_reviews_us_Grocery_v1_00.tsv"

rtime = time()

print("loading dataset...")
ds = pd.read_table(DATASET, error_bad_lines=False, header=0, warn_bad_lines=False)
print("done!")

# Drop null rows
ds = ds.dropna()

sia = SentimentIntensityAnalyzer()

def sentiment_heuristic(row):
    return sia.polarity_scores(row)['compound']

#ds = ds.head()

print("calculating sentiment...")
ds["sentiment_score"] = ds["review_body"].apply(sentiment_heuristic)
print("done!")

ds.to_csv("sentiment_ds.csv")

rtime = time() - rtime
print("The program ran in", rtime, "seconds")


from vibes import vibes
from pyibex import *
# Libraries we need
# import pyxhook
import time
import math
import curses
import curses.ascii

from curses import wrapper
from Catan2 import Catan2

def print2(itv):
  return "[%s; %s]"%(itv.lb().hex(),  itv.ub().hex())

def main(stdscr):
    # Clear screen
    stdscr.clear()
    stdscr.keypad(1)
    begin_x = 20; begin_y = 7
    height = 5; width = 40
    win = curses.newwin(height, width, begin_y, begin_x)
    stdscr.addstr(0,10,"Hit 'space' to quit")
    stdscr.refresh()
    cpt = 0
    key = ''
    x = Interval(1,1.5)
    y = Interval(2,3)
    dth = Interval.HALF_PI.mid()/32.
    th = Interval(0).inflate(2*dth)

    Interval.__str__ = print2
    fout = open("test.txt", "a")
    fout.write("#================ RUN ===============\n")

    while key != ord(' '):

        key = stdscr.getch()
        lower_key = chr(key).lower()

        if lower_key == 'z' :
          y += 0.5 if curses.ascii.isupper(key) else 0.1
        elif lower_key == 's' :
          y -= 0.5 if curses.ascii.isupper(key) else 0.1
        elif lower_key == 'd' :
          x += 0.5 if curses.ascii.isupper(key) else 0.1
        elif lower_key == 'q' :
          x -= 0.5 if curses.ascii.isupper(key) else 0.1
        elif lower_key == 'a':
          th += dth/8. if curses.ascii.isupper(key) else dth
        elif lower_key == 'e':
          th -= dth/8. if curses.ascii.isupper(key) else dth

        elif key == curses.KEY_NPAGE:
          th = th.lb() + (Interval(0,th.diam()-dth) | Interval(0))
        elif key == curses.KEY_PPAGE:
          th = th.lb() + (Interval(0,th.diam()+dth) | Interval(0))
        elif key == curses.KEY_RIGHT:
          x = x.lb() + (Interval(0,x.diam()+1) | Interval(0))
        elif key == curses.KEY_LEFT:
          x = x.lb() + (Interval(0,x.diam()-1) | Interval(0))
        elif key == curses.KEY_UP:
          y = y.lb() + (Interval(0,y.diam()+1) | Interval(0))
        elif key == curses.KEY_DOWN:
          y = y.lb() + (Interval(0,y.diam()-1) | Interval(0))

        #
        # # if curses.ascii.ascii(key) == "a":
        #
        # if key == curses.KEY_UP or key == 566:
        #   y += 0.5 if key==566 else 0.1
        # elif key == curses.KEY_DOWN or key == 525:
        #   y -= 0.5 if key==525 else 0.1
        # elif key == curses.KEY_RIGHT or key == 560:
        #   x += 0.5 if key==560 else 0.1
        # elif key == curses.KEY_LEFT or key == 545:
        #   x -= 0.5 if key==545 else 0.1
        # elif key == ord('a') or key == 1:
        #   th += 0.1 if key==1 else 0.05
        # elif key == ord('e') or key == 5:
        #   th -= 0.1 if key==5 else 0.05
        #
        # elif key == ord('A'):
        #   th = th.lb() + (Interval(0,th.diam()-0.1) | Interval(0))
        # elif key == ord('E'):
        #   th = th.lb() + (Interval(0,th.diam()+0.1) | Interval(0))
        # elif key == ord('z'):
        #   x = x.lb() + (Interval(0,x.diam()+1) | Interval(0))
        # elif key == ord('s'):
        #   x = x.lb() + (Interval(0,x.diam()-1) | Interval(0))
        # elif key == ord('d'):
        #   y = y.lb() + (Interval(0,y.diam()+1) | Interval(0))
        # elif key == ord('q'):
        #   y = y.lb() + (Interval(0,y.diam()-1) | Interval(0))

        elif key == ord('R'):
          x = Interval(1,1.5)
          y = Interval(2,3)
          th = Interval(0).inflate(1)

        xx,yy,thh = Catan2(x,y,Interval(th))

        # print(event.Key, event.Ascii, th, thh)
        # print(x, y, th  )
        vibes.clearFigure()
        vibes.drawBox(x[0], x[1], y[0], y[1], 'r')
        vibes.drawPie((0,0), (1e-5,5), th*180./math.pi, "r")
        vibes.drawPie((0,0), (1e-5,5), thh*180./math.pi, "b")
        vibes.drawBox(xx[0], xx[1], yy[0], yy[1], 'b')

        # if key in actions:
        #   x, y, th = actions[key]( x,y,th )
        # fct = actions.get(key, lambda x,y,th:x,y,th)

        stdscr.clear()
        stdscr.addstr(1, 10, "Pressed %s %d"%(lower_key, key))
        stdscr.addstr(1,25,str(curses.ascii.ctrl(key)))
        stdscr.addstr(1,40,str(curses.ascii.ascii(key)))
        stdscr.addstr(1,40,str(curses.ascii.alt(key)))
        stdscr.addstr(3, 20, "x    : %s\t\t%s"%(x,xx))
        stdscr.addstr(4, 20, "y    : %s\t\t%s"%(y,yy))
        stdscr.addstr(5, 20, "theta : %s\t\t%s"%(th, thh))
        if key == ord("t"):
          fout.write("%s %s %s -> %s %s %s\n"%(x,y,th, xx, yy, thh))
        stdscr.refresh()

    curses.endwin()
    fout.write("#===================== END ===============\n");

if __name__ == '__main__':
  vibes.beginDrawing()
  vibes.newFigure("Debut Catan2")
  vibes.setFigureSize(500,500)
  vibes.drawBox(-5,5,-5,5)
  vibes.axisAuto()

  wrapper(main)


import numpy as np
import matplotlib.pyplot as plt
from project_math import *
from scipy.integrate import solve_ivp
from matplotlib.animation import FuncAnimation

x_min = -1e-7
x_max = 1e-7
B0 = 1
xs = np.linspace(x_min,x_max,N)
delta = xs[1]-xs[0]

gaussianA = generateGaussianWavePacket(xs,-5e-8,5e-9,9e8)
gaussianB = generateGaussianWavePacket(xs,0,5e-9,-9e8)
As = generateMagneticVectorPotential(xs,B0,5e-10)

gaussianAUp = np.zeros(N*N*2, dtype=complex)
gaussianADown = np.zeros(N*N*2, dtype=complex)
gaussianBUp = np.zeros(N*N*2, dtype=complex)
gaussianBDown = np.zeros(N*N*2, dtype=complex)

for z1 in range(N):
    for z2 in range(N):
        gaussianAUp  [mapZ12StoLin(z1,z2,0,N)] = 1/np.sqrt(N)*gaussianA[z1]
        gaussianADown[mapZ12StoLin(z1,z2,1,N)] = 1/np.sqrt(N)*gaussianA[z1]
        gaussianBUp  [mapZ12StoLin(z1,z2,0,N)] = 1/np.sqrt(N)*gaussianB[z2]
        gaussianBDown[mapZ12StoLin(z1,z2,1,N)] = 1/np.sqrt(N)*gaussianB[z2]

H = hamiltonian2Particles(xs,delta, As)

def f(t, phi):
    return 1/(1j*constants.hbar) * (H @ phi)

tmax = 15e-13
timesteps = 100

startCond = gaussianAUp

sol = solve_ivp(f, [0, tmax], startCond,  t_eval=np.linspace(0,tmax,timesteps), method='DOP853')
print(sol)

solutionUpA   = np.zeros((timesteps,N),dtype=complex)
solutionDownA = np.zeros((timesteps,N),dtype=complex)
solutionUpB   = np.zeros((timesteps,N),dtype=complex)
solutionDownB = np.zeros((timesteps,N),dtype=complex)

for i in range(timesteps):
    for z in range(N):
        solutionUpA  [i][z] = np.sum(np.power(np.abs([(sol.y[mapZ12StoLin(z,x,0,N)][i]) for x in range(N)]),2))
        solutionDownA[i][z] = np.sum(np.power(np.abs([(sol.y[mapZ12StoLin(z,x,1,N)][i]) for x in range(N)]),2))

        solutionUpB  [i][z] = np.sum(np.power(np.abs([(sol.y[mapZ12StoLin(x,z,0,N)][i]) for x in range(N)]),2))
        solutionDownB[i][z] = np.sum(np.power(np.abs([(sol.y[mapZ12StoLin(x,z,1,N)][i]) for x in range(N)]),2))

ys = solutionUpA[0]
print(np.trapz(ys,xs))

# Create a figure and axis
fig, ((ax1,ax3),(ax2,ax4)) = plt.subplots(2,2)

# Set the x-axis limits
ax1.set_xlim(x_min,x_max)
ax2.set_xlim(x_min,x_max)
ax3.set_xlim(x_min,x_max)
ax4.set_xlim(x_min,x_max)

ax1.set_ylim(0,1e8)
ax2.set_ylim(0,1e8)
ax3.set_ylim(0,1e8)
ax4.set_ylim(0,1e8)

# Initialize an empty point for the plot
point1, = ax1.plot(xs, solutionUpA[0])
point2, = ax2.plot(xs, solutionDownA[0])
point3, = ax3.plot(xs, solutionUpB[0])
point4, = ax4.plot(xs, solutionDownB[0])
ax1.plot(xs, As*1e14)
ax2.plot(xs, As*1e14)
ax3.plot(xs, As*1e14)
ax4.plot(xs, As*1e14)

# Function to initialize the plot
def init():
    point1.set_data(xs, ys)
    point2.set_data(xs, ys)
    point3.set_data(xs, ys)
    point4.set_data(xs, ys)
    return point1, point2, point3, point4

# Function to update the plot for each frame
def update(frame):
    x = xs
    point1.set_data(xs, solutionUpA[frame])
    point2.set_data(xs, (solutionDownA[frame]))
    point3.set_data(xs, solutionUpB[frame])
    point4.set_data(xs, (solutionDownB[frame]))
    return point1, point2, point3, point4

ani = FuncAnimation(fig, update, frames=len(sol.y[0]), init_func=init, blit=True,interval=1/60*1000)
ani.save(f"testTwoParticle{N}_B{B0}.gif")
plt.show()


import pickle

import numpy
import matplotlib
matplotlib.use('Agg')
from matplotlib import pyplot as plt

_output_dir = 'figures/'
_output_ext = '.png'
#_output_ext = '.pdf'

class Hist1():
    def __init__(self, plot_name, plot_title, overlay_list, bin_count, bin_range, **kwargs):
        arg_vals = {
            'normalize': True, 'legend_args':{}, 'xlabel':'', 'ylabel':'',
            'xlim':None, 'ylim':None, 'ylog':False, 'labelmaker':None,
            'cumulative': 0, 'zooms': []
        }
        self.plot_name = plot_name
        self.plot_title = plot_title
        self.data = { label:([],[]) for label in overlay_list }
        self.bins = bin_count
        self.range = bin_range

        arg_vals.update(kwargs)
        for kw,arg in arg_vals.items(): setattr(self, kw, arg)

    def fill(self, value, key=None, weight=1):
        if key == None: key = list(self.data)[0]
        self.data[key][0].append(value)
        self.data[key][1].append(weight)

    def generate_plot(self, cache, refresh=None):
        print('Plotting '+self.plot_name)
        if refresh == None and cache != None: self.data = cache
        elif refresh: cache[self.plot_name] = self.data
        else: self.data = cache[self.plot_name]

        plot_values = {'x':[],'weights':[],'label':[]}
        for label, (values, weights) in self.data.items():
            counts, bins = numpy.histogram(values, weights=weights, bins=self.bins, range=self.range)
            if counts.sum() == 0:
                print('WARNING: '+self.plot_name + ' has no data for label ' + str(label))
                if len(values) == 0:
                    print('Label list is empty')
                else:
                    print( 'Label list contains {} values between {} and {}'.format(len(values), min(values), max(values)) )
                return
            binned_vals = counts / counts.sum() if self.normalize else counts
            if self.cumulative != 0:
                if isinstance(self.cumulative,dict):
                    sumdir = self.cumulative[label]
                else:
                    sumdir = self.cumulative
                binned_vals = binned_vals[::sumdir].cumsum()[::sumdir]

            plot_values['x'].append( bins[:-1] )
            plot_values['weights'].append(binned_vals)
            label_text = label if self.labelmaker == None else self.labelmaker(label)
            plot_values['label'].append(label_text)
        plot_values['range'] = self.range
        plot_values['bins'] = self.bins

        fig,ax = plt.subplots()
        counts, bins, hist = plt.hist( **plot_values, linewidth=2, histtype='step')

        if len(plot_values['label']) > 1: ax.legend(**self.legend_args)
        plt.grid()
        if self.ylog: plt.yscale('log')
        if self.xlim != None: plt.xlim(*self.xlim)
        if self.ylim != None: plt.ylim(*self.ylim)
        plt.xlabel(self.xlabel)
        plt.ylabel(self.ylabel)
        plt.title(self.plot_title)
        dpi=500
        fig.savefig(_output_dir+self.plot_name+_output_ext, dpi=dpi)
        for index, (xlim, ylim) in enumerate(self.zooms):
            plt.xlim(xlim)
            plt.ylim(ylim)
            plt.savefig(_output_dir+self.plot_name+f'_zoom{index}'+_output_ext, dpi=dpi)
        plt.close()

class Hist2():
    def __init__(self, plot_name, plot_title, bin_count, bin_range, **kwargs):
        arg_vals = { 'normalize': True, 'xlabel':'', 'ylabel':'', 'zlim':None }
        self.plot_name = plot_name
        self.plot_title = plot_title
        self.data = ([],[])
        self.bins = bin_count
        self.range = bin_range

        arg_vals.update(kwargs)
        for kw,arg in arg_vals.items(): setattr(self, kw, arg)

    def fill(self, value0, value1):
        self.data[0].append(value0)
        self.data[1].append(value1)

    def generate_plot(self, cache, refresh=None):
        print('Plotting '+self.plot_name)
        if refresh == None and cache != None: self.data = cache
        elif refresh: cache[self.plot_name] = self.data
        else: self.data = cache[self.plot_name]

        hist_counts, xedges, yedges = numpy.histogram2d(*self.data, bins=self.bins, range=self.range)
        flat_counts = hist_counts.flatten()
        if flat_counts.sum() == 0:
            print('WARNING: '+self.plot_name + ' has no data')
            print(*self.data)
            return
        if self.normalize: flat_counts /= hist_counts.sum()
        bin_edges = numpy.array([ (x,y) for x in xedges[:-1] for y in yedges[:-1] ]).transpose()

        fig,ax = plt.subplots()
        counts, xbins, ybins, hist = plt.hist2d( *bin_edges, weights=flat_counts, bins=self.bins, range=self.range)

        cbar = plt.colorbar()
        if self.zlim != None: plt.clim(self.zlim)

        #plt.grid()
        #plt.yscale('log')
        #plt.ylim(10e-6, 1)
        plt.xlabel(self.xlabel)
        plt.ylabel(self.ylabel)
        plt.title(self.plot_title)
        fig.savefig(_output_dir+self.plot_name+_output_ext)
        plt.close()

class Roc():
    def __init__(self, plot_name, plot_title, overlay_list, **kwargs):
        arg_vals = {
            'legend_args':{}, 'labelmaker':None, 'normalize': True, 'scale_to_y': False,
            'zooms': []
        }
        self.plot_name = plot_name
        self.plot_title = plot_title
        self.data = { label:( ([],[]), ([],[]) ) for label in overlay_list }
        self.marker_requests = { label:[] for label in overlay_list }

        arg_vals.update(kwargs)
        for kw,arg in arg_vals.items(): setattr(self, kw, arg)

    def add_marker(self, label, marker_value, **kwargs):
        if 'annotation' not in kwargs: kwargs['annotation'] = ''
        self.marker_requests[label].append( (marker_value, kwargs) )

    def fill(self, value, bgd, key=None, weight=1):
        if key == None: key = list(self.data)[0]
        self.data[key][bgd][0].append(value)
        self.data[key][bgd][1].append(weight)

    def generate_plot(self, cache, refresh=None):
        print('Plotting '+self.plot_name)
        if refresh == None and cache != None: self.data = cache
        elif refresh: cache[self.plot_name] = self.data
        else: self.data = cache[self.plot_name]

        roc_ax = plt.subplots()
        marker_list = []

        num_bins = 10000
        for label, (signal,background) in self.data.items():
            maximum = max( max(signal[0]), max(background[0]) )
            minimum = min( min(signal[0]), min(background[0]) )
            efficiency,bin_list = numpy.histogram( signal[0], weights=signal[1], bins=num_bins, range=(minimum, maximum) )
            rejection = numpy.histogram( background[0], weights=background[1], bins=num_bins, range=(minimum, maximum) )[0]
            efficiency = ( efficiency/efficiency.sum() )[::-1].cumsum()[::-1]
            rejection = ( rejection/rejection.sum() ).cumsum()
            plt.plot(efficiency, rejection, label=label, linewidth=1)

            bin_width = bin_list[1] - bin_list[0]
            for marker_val, kwargs in self.marker_requests[label]:
                bin_index = int( (marker_val-minimum) / bin_width )
                location = (efficiency[bin_index], rejection[bin_index])
                print(f'Marker: {label}={marker_val} @ {location[0]},{location[1]}')
                marker_list.append( (location, kwargs) )

        plt.legend(prop={'size':6})
        plt.xlabel('Signal Efficiency')
        plt.ylabel('Background Rejection')
        plt.title(self.plot_title)
        plt.grid(True)
        for location, kwargs in marker_list:
            annotation_text = kwargs.pop('annotation')
            plt.annotate(annotation_text, xy=location, fontsize='x-small')
            plt.plot(*location, **kwargs)

        dpi=500
        plt.savefig(_output_dir+self.plot_name+_output_ext, dpi=dpi)
        for index, (xlim, ylim) in enumerate(self.zooms):
            plt.xlim(xlim)
            plt.ylim(ylim)
            plt.savefig(_output_dir+self.plot_name+f'_zoom{index}'+_output_ext, dpi=dpi)
        plt.close()

class plot_wrapper():
    def __init__(self, blacklist):
        self.plot_dict = {}
        self.blacklist = blacklist

    def add_hist1(self, plot_name, *args, **kwargs):
        self.plot_dict[plot_name] = Hist1(plot_name, *args, **kwargs)

    def add_hist2(self, plot_name, *args, **kwargs):
        self.plot_dict[plot_name] = Hist2(plot_name, *args, **kwargs)

    def add_roc(self, plot_name, *args, **kwargs):
        self.plot_dict[plot_name] = Roc(plot_name, *args, **kwargs)

    def plot_all(self, refresh, cache):
        for key,plot in self.plot_dict.items():
            blacklisted = False
            for substring in self.blacklist:
                if substring in key:
                    blacklisted = True
                    break
            if not blacklisted: plot.generate_plot(cache, refresh=refresh)

    def __getitem__(self, plot_name):
        return self.plot_dict[plot_name]


from unittest.mock import MagicMock
import pytest
from project.dao import GenreDAO
from project.dao.models import Genre
from project.exceptions import GenreNotFound
from project.services import GenreService
from project.setup_db import db

@pytest.fixture()
def genre_dao():
    genre_dao = GenreDAO(db.session)

    genre1 = Genre(id=1, name='Comedy')
    genre2 = Genre(id=2, name="Action")
    genre3 = Genre(id=3, name='Documentary')

    genre_dao.get_by_id = MagicMock(return_value=genre1)
    genre_dao.get_all = MagicMock(return_value=[genre1, genre2, genre3])

    return genre_dao

class TestGenreService:
    @pytest.fixture(autouse=True)
    def genre_service(self, genre_dao):
        self.genre_service = GenreService(genre_dao)

    def test_get_one(self):
        director = self.genre_service.get_item_by_id(1)
        assert director is not None
        assert director["id"] == 1
        assert isinstance(director, dict)

    def test_get_all(self):
        genres = self.genre_service.get_all_genres()
        assert len(genres) > 0
        assert isinstance(genres, list)

    def test_get_item_by_id_not_found(self, genre_dao):
        genre_dao.get_by_id = MagicMock(return_value=None)
        with pytest.raises(GenreNotFound):
            self.genre_service.get_item_by_id(2)


from lxml import etree
import webob

from nova.api.openstack.compute.contrib import keypairs
from nova.api.openstack import wsgi
from nova import db
from nova import exception
from nova.openstack.common import jsonutils
from nova import quota
from nova import test
from nova.tests.api.openstack import fakes

QUOTAS = quota.QUOTAS

def fake_keypair(name):
    return {'public_key': 'FAKE_KEY',
            'fingerprint': 'FAKE_FINGERPRINT',
            'name': name}

def db_key_pair_get_all_by_user(self, user_id):
    return [fake_keypair('FAKE')]

def db_key_pair_create(self, keypair):
    pass

def db_key_pair_destroy(context, user_id, name):
    if not (user_id and name):
        raise Exception()

def db_key_pair_get(context, user_id, name):
    pass

class KeypairsTest(test.TestCase):

    def setUp(self):
        super(KeypairsTest, self).setUp()
        self.Controller = keypairs.Controller()
        fakes.stub_out_networking(self.stubs)
        fakes.stub_out_rate_limiting(self.stubs)

        self.stubs.Set(db, "key_pair_get_all_by_user",
                       db_key_pair_get_all_by_user)
        self.stubs.Set(db, "key_pair_create",
                       db_key_pair_create)
        self.stubs.Set(db, "key_pair_destroy",
                       db_key_pair_destroy)
        self.flags(
            osapi_compute_extension=[
                'nova.api.openstack.compute.contrib.select_extensions'],
            osapi_compute_ext_list=['Keypairs'])
        self.app = fakes.wsgi_app(init_only=('os-keypairs',))

    def test_keypair_list(self):
        req = webob.Request.blank('/v2/fake/os-keypairs')
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 200)
        res_dict = jsonutils.loads(res.body)
        response = {'keypairs': [{'keypair': fake_keypair('FAKE')}]}
        self.assertEqual(res_dict, response)

    def test_keypair_create(self):
        body = {'keypair': {'name': 'create_test'}}
        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 200)
        res_dict = jsonutils.loads(res.body)
        self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0)
        self.assertTrue(len(res_dict['keypair']['private_key']) > 0)

    def test_keypair_create_with_empty_name(self):
        body = {'keypair': {'name': ''}}
        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 400)

    def test_keypair_create_with_invalid_name(self):
        body = {
            'keypair': {
                'name': 'a' * 256
            }
        }
        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 400)

    def test_keypair_create_with_non_alphanumeric_name(self):
        body = {
            'keypair': {
                'name': 'test/keypair'
            }
        }
        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        res_dict = jsonutils.loads(res.body)
        self.assertEqual(res.status_int, 400)

    def test_keypair_import(self):
        body = {
            'keypair': {
                'name': 'create_test',
                'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA'
                              'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4'
                              'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y'
                              'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi'
                              'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj'
                              'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1'
                              'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc'
                              'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB'
                              'bHkXa6OciiJDvkRzJXzf',
            },
        }

        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 200)
        # FIXME(ja): sholud we check that public_key was sent to create?
        res_dict = jsonutils.loads(res.body)
        self.assertTrue(len(res_dict['keypair']['fingerprint']) > 0)
        self.assertFalse('private_key' in res_dict['keypair'])

    def test_keypair_import_quota_limit(self):

        def fake_quotas_count(self, context, resource, *args, **kwargs):
            return 100

        self.stubs.Set(QUOTAS, "count", fake_quotas_count)

        body = {
            'keypair': {
                'name': 'create_test',
                'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznA'
                              'x9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4'
                              'a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6Y'
                              'Qj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymi'
                              'MZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7lj'
                              'j5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1'
                              'GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGc'
                              'j7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwB'
                              'bHkXa6OciiJDvkRzJXzf',
            },
        }

        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 413)

    def test_keypair_create_quota_limit(self):

        def fake_quotas_count(self, context, resource, *args, **kwargs):
            return 100

        self.stubs.Set(QUOTAS, "count", fake_quotas_count)

        body = {
            'keypair': {
                'name': 'create_test',
            },
        }

        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 413)

    def test_keypair_create_duplicate(self):
        self.stubs.Set(db, "key_pair_get", db_key_pair_get)
        body = {'keypair': {'name': 'create_duplicate'}}
        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 409)

    def test_keypair_import_bad_key(self):
        body = {
            'keypair': {
                'name': 'create_test',
                'public_key': 'ssh-what negative',
            },
        }

        req = webob.Request.blank('/v2/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 400)

    def test_keypair_delete(self):
        req = webob.Request.blank('/v2/fake/os-keypairs/FAKE')
        req.method = 'DELETE'
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 202)

    def test_keypair_get_keypair_not_found(self):
        req = webob.Request.blank('/v2/fake/os-keypairs/DOESNOTEXIST')
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 404)

    def test_keypair_delete_not_found(self):

        def db_key_pair_get_not_found(context, user_id, name):
            raise exception.KeypairNotFound(user_id=user_id, name=name)

        self.stubs.Set(db, "key_pair_get",
                       db_key_pair_get_not_found)
        req = webob.Request.blank('/v2/fake/os-keypairs/WHAT')
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 404)

    def test_keypair_show(self):

        def _db_key_pair_get(context, user_id, name):
            return {'name': 'foo', 'public_key': 'XXX', 'fingerprint': 'YYY'}

        self.stubs.Set(db, "key_pair_get", _db_key_pair_get)

        req = webob.Request.blank('/v2/fake/os-keypairs/FAKE')
        req.method = 'GET'
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        res_dict = jsonutils.loads(res.body)
        self.assertEqual(res.status_int, 200)
        self.assertEqual('foo', res_dict['keypair']['name'])
        self.assertEqual('XXX', res_dict['keypair']['public_key'])
        self.assertEqual('YYY', res_dict['keypair']['fingerprint'])

    def test_keypair_show_not_found(self):

        def _db_key_pair_get(context, user_id, name):
            raise exception.KeypairNotFound(user_id=user_id, name=name)

        self.stubs.Set(db, "key_pair_get", _db_key_pair_get)

        req = webob.Request.blank('/v2/fake/os-keypairs/FAKE')
        req.method = 'GET'
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        self.assertEqual(res.status_int, 404)

    def test_show_server(self):
        self.stubs.Set(db, 'instance_get',
                        fakes.fake_instance_get())
        req = webob.Request.blank('/v2/fake/servers/1')
        req.headers['Content-Type'] = 'application/json'
        response = req.get_response(fakes.wsgi_app(init_only=('servers',)))
        self.assertEquals(response.status_int, 200)
        res_dict = jsonutils.loads(response.body)
        self.assertTrue('key_name' in res_dict['server'])
        self.assertEquals(res_dict['server']['key_name'], '')

    def test_detail_servers(self):
        self.stubs.Set(db, 'instance_get_all_by_filters',
                        fakes.fake_instance_get_all_by_filters())
        req = fakes.HTTPRequest.blank('/v2/fake/servers/detail')
        res = req.get_response(fakes.wsgi_app(init_only=('servers',)))
        server_dicts = jsonutils.loads(res.body)['servers']
        self.assertEquals(len(server_dicts), 5)

        for server_dict in server_dicts:
            self.assertTrue('key_name' in server_dict)
            self.assertEquals(server_dict['key_name'], '')

    def test_keypair_create_with_invalid_keypairBody(self):
        body = {'alpha': {'name': 'create_test'}}
        req = webob.Request.blank('/v1.1/fake/os-keypairs')
        req.method = 'POST'
        req.body = jsonutils.dumps(body)
        req.headers['Content-Type'] = 'application/json'
        res = req.get_response(self.app)
        res_dict = jsonutils.loads(res.body)
        self.assertEqual(res.status_int, 400)
        self.assertEqual(res_dict['badRequest']['message'],
                         "Invalid request body")

class KeypairsXMLSerializerTest(test.TestCase):
    def setUp(self):
        super(KeypairsXMLSerializerTest, self).setUp()
        self.deserializer = wsgi.XMLDeserializer()

    def test_default_serializer(self):
        exemplar = dict(keypair=dict(
                public_key='fake_public_key',
                private_key='fake_private_key',
                fingerprint='fake_fingerprint',
                user_id='fake_user_id',
                name='fake_key_name'))
        serializer = keypairs.KeypairTemplate()
        text = serializer.serialize(exemplar)

        tree = etree.fromstring(text)

        self.assertEqual('keypair', tree.tag)
        for child in tree:
            self.assertTrue(child.tag in exemplar['keypair'])
            self.assertEqual(child.text, exemplar['keypair'][child.tag])

    def test_index_serializer(self):
        exemplar = dict(keypairs=[
                dict(keypair=dict(
                        name='key1_name',
                        public_key='key1_key',
                        fingerprint='key1_fingerprint')),
                dict(keypair=dict(
                        name='key2_name',
                        public_key='key2_key',
                        fingerprint='key2_fingerprint'))])
        serializer = keypairs.KeypairsTemplate()
        text = serializer.serialize(exemplar)

        tree = etree.fromstring(text)

        self.assertEqual('keypairs', tree.tag)
        self.assertEqual(len(exemplar['keypairs']), len(tree))
        for idx, keypair in enumerate(tree):
            self.assertEqual('keypair', keypair.tag)
            kp_data = exemplar['keypairs'][idx]['keypair']
            for child in keypair:
                self.assertTrue(child.tag in kp_data)
                self.assertEqual(child.text, kp_data[child.tag])

    def test_deserializer(self):
        exemplar = dict(keypair=dict(
                name='key_name',
                public_key='public_key'))
        intext = ("<?xml version='1.0' encoding='UTF-8'?>\n"
                  '<keypair><name>key_name</name>'
                  '<public_key>public_key</public_key></keypair>')

        result = self.deserializer.deserialize(intext)['body']
        self.assertEqual(result, exemplar)


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

import re
import copy
import os
import stat
import errno
import config
import shutil
from zsync_logger import MYLOGGER
from collections import deque

def check_file_same(file_path, file_size, file_mtime):
    if not os.path.exists(file_path):
        return False

    file_stat = os.stat(file_path)

    if abs(file_stat.st_mtime - file_mtime) > config.FILE_MODIFY_WINDOW or \
        file_stat.st_size != file_size:
        return False

    return True

def fix_file_type(file_path, file_type, from_path=''):
    if os.path.exists(file_path):
        exist_type = config.FILE_TYPE_DIR if os.path.isdir(file_path) \
            else config.FILE_TYPE_FILE

        if exist_type != file_type:
            if exist_type == config.FILE_TYPE_FILE:
                os.remove(file_path)
            else:
                shutil.rmtree(file_path)
    else:
        if not file_path.startswith(from_path):
            raise ValueError('fix_file_type args is invalid')

        dirs = file_path[len(from_path):].split(os.path.sep)
        dirs = dirs[:-1]

        check_path = from_path

        for dir_part in dirs:
            check_path = os.path.join(check_path, dir_part)
            if os.path.exists(check_path) and not os.path.isdir(check_path):
                os.remove(check_path)
                return

    return

def makedir(directory, mode = None, mtime = None):
    if not os.path.exists(directory):
        try:
            os.makedirs(directory)
        except OSError as e:
            if e.errno != errno.EEXIST:
                return str(e)

    if mode or mtime:
        dir_stat = os.stat(directory)

        if mode and dir_stat[stat.ST_MODE] != mode:
            os.chmod(directory, mode)

        if mtime and dir_stat[stat.ST_MTIME] != mtime:
            os.utime(directory, (mtime, mtime))
    return None

ip_pattern = re.compile(r'^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?):|^localhost:')

class CommonPath(object):
    def __init__(self, path):
        self.origin_path = path
        self.ip = ''
        self.path = os.path.normpath(path)
        match = ip_pattern.match(self.path)
        if match:
            self.ip = match.group()[:-1]
            self.path = self.path[len(self.ip) + 1:]
        else:
            self.ip = '127.0.0.1'

        if self.ip == 'localhost':
            self.ip = '127.0.0.1'

        self.last_path = os.path.basename(self.path)
        self.prefix_path = self.path[:self.path.find(self.last_path)]
        return

    def isValid(self):
        return self.path and self.ip

    def full(self):
        return self.ip + ':' + self.path

    def isLocal(self):
        return self.ip == '127.0.0.1'

    def visitValid(self):
        if not os.path.exists(self.path):
            return False

        return True

class CommonFile(object):
    def __init__(self):
        self.path = ''
        self.file = None
        self.mode = 0
        self.file_mode = 0
        self.file_mtime = 0
        self.chunk_map = {}
        self.credit = 0
        self.fetch_offset = 0
        self.offset = 0
        self.total = 0
        self.writedone = False
        return

    def __del__(self):
        self.close()
        return

    def is_open(self):
        return self.file

    def writing_file(self):
        return self.file and 'w' in self.mode

    def close(self):
        if self.file:
            self.file.close()
            self.file = None
            if 'w' in self.mode:
                if self.file_mode:
                    os.chmod(self.path, self.file_mode)
                if self.file_mtime:
                    os.utime(self.path, (self.file_mtime, self.file_mtime))
        return

    def open(self, path, mode, total=0, credit=0, file_mode=0, file_mtime=0):
        self.close()
        self.__init__()
        self.path = path
        self.mode = mode
        self.file = open(path, mode)
        self.file_mode = file_mode
        self.file_mtime = file_mtime
        self.total = total
        self.credit = credit
        return

    def fetch(self, offset, size):
        self.offset = offset
        self.file.seek(offset, os.SEEK_SET)
        data = self.file.read(size)
        return data

    def write(self, data):
        if not self.file:
            return
        self.file.write(data)
        self.offset += len(data)
        if self.offset == self.total:
            self.writedone = True
            self.close()
        return

    def write_chunk(self, offset, data):
        if not self.file:
            return

        if offset != self.offset:
            self.chunk_map[offset] = data
        else:
            self.write(data)

            for woffset in sorted(self.chunk_map.keys()):
                if woffset == self.offset:
                    chunk = self.chunk_map.pop(woffset)
                    self.write(chunk)
                else:
                    break
        return

class CommonExclude(object):
    def __init__(self, excludes):
        self.excludes_origin = copy.deepcopy(excludes)
        self.excludes = None
        if excludes:
            self.excludes = [re.compile(exclude) for exclude in excludes]
        return

    # find match exclude
    # if fname is not none, will match fname and join(path, fname)
    # else match path
    def isExclude(self, path, fname=None):
        if not self.excludes:
            return False

        if fname:
            real_path = os.path.join(path, fname)
            for exclude in self.excludes:
                if exclude.match(fname):
                    return True

                if exclude.match(real_path):
                    return True
        else:
            for exclude in self.excludes:
                if exclude.match(path):
                    return True

        return False

def create_sub_process(args_dict):
    import subprocess
    import inspect
    import sys
    file_path = inspect.stack()[-1][1]
    dir_path = os.path.dirname(file_path)
    zsync_path = os.path.join(dir_path, 'zsync.py')

    for key in ['src', 'dst']:
        if type(args_dict[key]) is unicode:
            args_dict[key] = args_dict[key].encode(sys.stdin.encoding)

    sub_args = [
        'python', zsync_path,
        args_dict['src'], \
        args_dict['dst'], \
        '--port', str(args_dict['port']), \
        '--thread-num', str(args_dict['thread_num']), \
        '--timeout', str(args_dict['timeout']), \
        '--pipeline', str(args_dict['pipeline']), \
        '--chunksize', str(args_dict['chunksize']), \
    ]

    if args_dict['excludes']:
        for exclude in args_dict['excludes']:
            sub_args.extend(['--exclude', str(exclude)])

    if args_dict.get('local'):
        sub_args.append('--local')

    if args_dict.get('remote'):
        sub_args.append('--remote')

    if args_dict.get('compress'):
        sub_args.append('--compress')

    MYLOGGER.debug('creating subprocess %s' % sub_args)
    sub = subprocess.Popen(sub_args)
    return sub

CHECKSUM_M = 1 << 16

def calc_checksum(data):
    # a = sum(data) mod M
    # b = sum((i+1) * data[i]) mod M
    # s = a + 2^16 * b
    a = 0
    b = 0
    maxlen = len(data)
    for i, char in enumerate(data):
        ordc = ord(char)
        a += ordc
        b += (maxlen - i) * ordc
        if a > CHECKSUM_M:
            a %= CHECKSUM_M
        if b > CHECKSUM_M:
            b %= CHECKSUM_M
    s = a + (b << 16)
    return a, b, s

class RollingChecksum(object):
    def __init__(self, data=None):
        self.roll_queue = None
        self.a = 0
        self.b = 0
        self.s = 0
        if data:
            self.calc_checksum(data)
        return

    def calc_checksum(self, data):
        self.roll_queue = deque((ord(char) for char in data), len(data))
        maxlen = self.roll_queue.maxlen
        self.a, self.b, self.s = calc_checksum(data)
        return

    def calc_next(self, char):
        ordc = ord(char)
        self.a = (self.a - self.roll_queue[0] + ordc) % CHECKSUM_M
        self.b = (self.b - self.roll_queue.maxlen * self.roll_queue[0] + self.a) % CHECKSUM_M
        self.roll_queue.append(ordc)
        self.s = self.a + (self.b << 16)
        return


from colorama import init
init()
from colorama import Fore, Back, Style
import numpy as np

size = 30

lst = np.array(["'"]*30 + ['♡', '♧'])

for i in range(0, size):
    if i == 0:
        print(Fore.YELLOW + "★".center(size, " "))
    elif i % 2 == 0 and i > 0:
        print(Fore.GREEN + ''.join(np.random.choice(lst, size=i)).center(size, " "))
    else:
        print(Fore.RED + ("'"*(i+1)).center(size, " "))

for i in range(5):
    print(Fore.GREEN + "■".center(size, " "))

init(autoreset=True)
print()
print(Back.RED + "Merry Christmas...!!".center(size+2, " "))
print()


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import multiLateration as ml
def main():
    #initializes the points
    x0, y0, z0 = 4, 1, 10  # point we are trying to find

    x1, y1, z1 = 3, 2, 3    #AP1
    x2, y2, z2 = -1, -3, 5  #AP2
    x3, y3, z3 = 1, 4, -2   #AP3
    p0 = [x0, y0, z0]
    p1 = [x1, y1, z1]
    p2 = [x2, y2, z2]
    p3 = [x3, y3, z3]
    points = [p0, p1, p2, p3]
    print(points)

    #distance between AP's and unknown point
    r1=ml.distance(points[0],points[1])
    r2=ml.distance(points[0],points[2])
    r3=ml.distance(points[0],points[3])

    points=ml.multiLateration(points[1],points[2],points[3],r1,r2,r3)

    # Plots the points into a 3d graph
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    ax.scatter(points[0][0], points[0][1], points[0][2], label="unkown point", c="black")
    ax.scatter(points[1][0], points[1][1], points[1][2], label="AP 1", c="red")
    ax.scatter(points[2][0], points[2][1], points[2][2], label="AP 2", c="red")
    ax.scatter(points[3][0], points[3][1], points[3][2], label="AP 3", c="red")
    ax.legend()
    plt.show()

main()


import operator
import os

import torch
from parse import parse

from core.tags import *

__all__ = ['Saver']

class Saver(object):
    def __init__(self, config, save_range=50):
        self.config = config
        self.save_range = save_range
        self.param_dir = os.path.join(self.config.ckpt_path, 'param')
        if not os.path.exists(self.param_dir):
            os.makedirs(self.param_dir)

        self.params = self.get_params_on_path(self.config.max_to_keep)
        self.best_perf, self.best_list = self.get_best_perf()

    def get_best_perf(self):
        perf = None

        best_epochs = list()
        if os.path.exists(self.param_dir):
            file_list = os.listdir(self.param_dir)
            for param in file_list:
                parsed_list = parse(BEST_CKPT, param)
                if parsed_list:
                    best_epoch = parsed_list[0]
                    best_epochs.append(best_epoch)

        best_epochs = sorted(best_epochs)

        # best_ckpt = os.path.join(self.param_dir, BEST
        if len(best_epochs) > 0:
            ckpt = torch.load(os.path.join(self.param_dir, BEST_CKPT.format(best_epochs[-1])))
            perf = ckpt[PERFORMANCE]

        return perf, best_epochs

    def get_params_on_path(self, max_to_keep):
        params = []
        if os.path.exists(self.param_dir):
            file_list = os.listdir(self.param_dir)
            for param in file_list:
                parsed_list = parse(CKPT_FMT, param)
                if parsed_list:
                    epoch = parsed_list[0]
                    params.append(epoch)

                    params = sorted(params)
                    params = params[-max_to_keep:]

        return params

    def load(self):
        model, optim, step, epoch = self.create()

        if self.params:
            ckpt = torch.load(os.path.join(self.param_dir, CKPT_FMT.format(self.params[-1])),
                              map_location=self.config.device)
            model.load_state_dict(ckpt[STATE])
            model.to(self.config.device)
            optim.load_state_dict(ckpt[OPTIM])  # may create new optimizer

            step = ckpt[STEP]
            epoch = ckpt[EPOCH]
            print("Loaded the model at {} epoch and {} step".format(epoch, step))
        else:
            print("Start from scratch ... ")

        return model, optim, step, epoch

    def load_target(self, target_epoch):
        model, optim, step, epoch = self.create()

        for dirpath, dirnames, filenames in os.walk(self.param_dir):
            for filename in filenames:
                parsed_list = parse(CKPT_FMT, filename)
                if parsed_list is not None:
                    epoch = parsed_list[-1]
                    if epoch == target_epoch:
                        ckpt = torch.load(
                            os.path.join(self.param_dir, filename))
                        model.load_state_dict(ckpt[STATE])
                        optim.load_state_dict(ckpt[OPTIM])
                        step = ckpt[STEP]
                        epoch = ckpt[EPOCH]

                        return model, optim, step, epoch

        raise Exception('Cannot find specific model: epoch %d' % target_epoch)

    def save(self, model, optim, step, epoch, performance=None, perf_op='gt'):
        if perf_op == 'gt':
            _op = operator.gt
        elif perf_op == 'lt':
            _op = operator.lt
        else:
            raise ValueError("perf_op ({})is not supported".format(perf_op))

        filename_to_save = os.path.join(self.param_dir, CKPT_FMT.format(epoch))

        torch.save(
            {
                STATE: model.state_dict(),
                OPTIM: optim.state_dict(),
                STEP: step,
                EPOCH: epoch,
                PERFORMANCE: performance
            },
            filename_to_save
        )
        self.params.append(epoch)

        if len(self.params) > self.config.max_to_keep:
            param_to_del = self.params.pop(0)
            filepath_to_del = os.path.join(self.param_dir,
                                           CKPT_FMT.format(param_to_del))
            os.remove(filepath_to_del)

        if performance:
            quot = epoch // self.save_range
            lquot = 0
            if len(self.best_list) > 0:
                latest_best_epoch = self.best_list[-1]
                lquot = latest_best_epoch // self.save_range

            if quot > lquot:
                # save new perf
                filename_to_save = os.path.join(self.param_dir, BEST_CKPT.format(epoch))
                torch.save(
                    {
                        STATE: model.state_dict(),
                        OPTIM: optim.state_dict(),
                        STEP: step,
                        EPOCH: epoch,
                        PERFORMANCE: performance
                    },
                    filename_to_save
                )
                self.best_perf = performance
                self.best_list.append(epoch)

            if not self.best_perf or _op(performance, self.best_perf):
                if len(self.best_list) > 0:
                    param_to_del = self.best_list.pop()  # get last best perf
                    filepath_to_del = os.path.join(self.param_dir, BEST_CKPT.format(param_to_del))
                    os.remove(filepath_to_del)  # remove

                torch.save(
                    {
                        STATE: model.state_dict(),
                        OPTIM: optim.state_dict(),
                        STEP: step,
                        EPOCH: epoch,
                        PERFORMANCE: performance
                    },
                    os.path.join(self.param_dir, BEST_CKPT.format(epoch))
                )
                self.best_perf = performance
                self.best_list.append(epoch)

    def create(self):
        model = self.config.model(**self.config.model_param).to(self.config.device)
        if hasattr(model, 'init_weights'):
            model.init_weights()

        if hasattr(model, 'transfer_weights') and hasattr(self.config, 'transfer_param'):
            model.transfer_weights(**self.config.transfer_param)

        optim = self.config.optim(model.parameters(), **self.config.optim_param)
        step = 0
        epoch = -1
        return model, optim, step, epoch


import sys

sys.path.append("../")
import os
import time
import argparse
import numpy as np

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.tensorboard import SummaryWriter
import torch.backends.cudnn as cudnn

from utils import dataloader, GaussianBlurAll, AverageMeter, save_model, accuracy
from models import AlexNetCifar10

# Training settings
parser = argparse.ArgumentParser()
parser.add_argument(
    "--no-cuda", action="store_true", default=False, help="Disables CUDA training."
)
parser.add_argument("--seed", type=int, default=42, help="Random seed.")
parser.add_argument(
    "--mode",
    type=str,
    choices=["normal", "blur-all", "blur-half-epochs", "blur-step", "blur-half-data"],
    help="Training mode.",
)
parser.add_argument(
    "--blur-val", action="store_true", default=False, help="Blur validation data."
)
parser.add_argument("--exp-name", "-n", type=str, default="", help="Experiment name.")
parser.add_argument(
    "--sigma", "-s", type=float, default=1, help="Sigma of Gaussian Blur."
)
parser.add_argument(
    "--kernel-size",
    "-k",
    type=int,
    nargs=2,
    default=(7, 7),
    help="Kernel size of Gaussian Blur.",
)
parser.add_argument(
    "--epochs", "-e", type=int, default=60, help="Number of epochs to train."
)
parser.add_argument(
    "--start-epoch",
    default=0,
    type=int,
    metavar="N",
    help="manual epoch number (useful on restarts)",
)
parser.add_argument("--batch-size", "-b", type=int, default=64, help="Batch size.")
parser.add_argument("--lr", type=float, default=0.01, help="Initial learning rate.")
parser.add_argument(
    "--weight-decay",
    "-w",
    type=float,
    default=5e-4,
    help="Weight decay (L2 loss on parameters).",
)
parser.add_argument(
    "--resume",
    default="",
    type=str,
    metavar="PATH",
    help="path to latest checkpoint (default: none)",
)

def main():
    args = parser.parse_args()
    if args.exp_name == "":
        print(
            "ERROR: USE '--exp-name' or '-n' option to define this experiment's name."
        )
        sys.exit()

    # directories settings
    os.makedirs("../logs/outputs", exist_ok=True)
    os.makedirs("../logs/models/{}".format(args.exp_name), exist_ok=True)

    OUTPUT_PATH = "../logs/outputs/{}.log".format(args.exp_name)
    MODEL_PATH = "../logs/models/{}/".format(args.exp_name)

    if os.path.exists(OUTPUT_PATH):
        print(
            "ERROR: This '--exp-name' is already used. Use another name for this experiment."
        )
        sys.exit()

    # recording outputs
    sys.stdout = open(OUTPUT_PATH, "w")
    sys.stderr = open(OUTPUT_PATH, "a")

    # tensorboardX
    writer = SummaryWriter(log_dir="../logs/tb/{}".format(args.exp_name))

    # cuda settings
    args.cuda = not args.no_cuda and torch.cuda.is_available()
    device = torch.device("cuda:0" if args.cuda else "cpu")
    print("device: {}".format(device))

    # for fast training
    cudnn.benchmark = True

    np.random.seed(args.seed)
    torch.manual_seed(args.seed)
    if args.cuda:
        torch.cuda.manual_seed(args.seed)

    # data settings
    trainloader, testloader, _ = dataloader(batch_size=args.batch_size)

    # Model, Criterion, Optimizer
    model = AlexNetCifar10().to(device)
    criterion = nn.CrossEntropyLoss().to(device)
    optimizer = optim.SGD(
        model.parameters(), lr=args.lr, momentum=0.9, weight_decay=args.weight_decay
    )

    # optionally resume from a checkpoint
    if args.resume:
        if os.path.isfile(args.resume):
            print("=> loading checkpoint '{}'".format(args.resume))
            checkpoint = torch.load(args.resume)
            args.start_epoch = checkpoint["epoch"]
            model.load_state_dict(checkpoint["state_dict"])
            optimizer.load_state_dict(checkpoint["optimizer"])
            print(
                "=> loaded checkpoint '{}' (epoch {})".format(
                    args.resume, checkpoint["epoch"]
                )
            )
        else:
            print("=> no checkpoint found at '{}'".format(args.resume))

    # print settings
    print("=" * 5 + " settings " + "=" * 5)
    print("TRAINING MODE: {}".format(args.mode))
    if args.mode == "blur-step":
        print("### BLUR CHANGING STEPS ###")
        print("Step: 1-10 -> 11-20 -> 21-30 -> 31-40 -> 41-{}".format(args.epochs))
        print("Sigma: 4 -> 3 -> 2 -> 1 -> none")
        # print('Kernel-size: (25, 25) -> (19, 19) -> (13, 13) -> (7, 7) -> none')
        print("#" * 20)
    elif args.mode == "blur-half-epochs":
        print("### NO BLUR FROM EPOCH {:d} ###".format(args.epochs // 2))
        print("Sigma: {}".format(args.sigma))
        # print('Kernel-size: {}'.format(tuple(args.kernel_size)))  # radius = sigma * 3 * 2 + 1
    elif args.mode == "blur-all":
        print("Sigma: {}".format(args.sigma))
        # print('Kernel-size: {}'.format(tuple(args.kernel_size)))  # radius = sigma * 3 * 2 + 1
    if args.blur_val:
        print("VALIDATION MODE: blur-val")
    print("Random seed: {}".format(args.seed))
    print("Epochs: {}".format(args.epochs))
    print("Learning rate: {}".format(args.lr))
    print("Weight_decay: {}".format(args.weight_decay))
    print()
    print(model)
    print("=" * 20)
    print()

    # training
    print("Start Training...")
    train_time = time.time()
    blur = True
    for epoch in range(
        args.start_epoch, args.epochs
    ):  # loop over the dataset multiple times
        if args.mode == "normal":
            blur = False
        elif args.mode == "blur-step":
            ### BLUR-STEP SETTINGS ###
            if epoch < 10:
                args.sigma = 4
                # args.kernel_size = (25, 25)  # radius = sigma * 3 * 2 + 1
            elif epoch < 20:
                args.sigma = 3
                # args.kernel_size = (19, 19)
            elif epoch < 30:
                args.sigma = 2
                # args.kernel_size = (13, 13)
            elif epoch < 40:
                args.sigma = 1
                # args.kernel_size = (7, 7)
            else:
                blur = False
        elif args.mode == "blur-half-epochs":
            if epoch >= args.epochs // 2:
                blur = False

        # ===== train mode =====
        train_acc = AverageMeter("train_acc", ":6.2f")
        train_loss = AverageMeter("train_loss", ":.4e")
        model.train()
        for i, data in enumerate(trainloader, 0):
            # get the inputs; data is a list of [inputs, labels]
            inputs, labels = data[0], data[1].to(device)

            # Blur images
            if blur:
                if args.mode == "blur-half-data":
                    half1, half2 = inputs.chunk(2)
                    # blur first half images
                    half1 = GaussianBlurAll(half1, (0, 0), args.sigma)
                    inputs = torch.cat((half1, half2))
                else:
                    inputs = GaussianBlurAll(inputs, (0, 0), args.sigma)
            inputs = inputs.to(device)

            # zero the parameter gradients
            optimizer.zero_grad()

            # forward + record
            outputs = model(inputs)
            loss = criterion(outputs, labels)
            acc1 = accuracy(outputs, labels, topk=(1,))
            train_loss.update(loss.item(), inputs.size(0))
            train_acc.update(acc1[0], inputs.size(0))

            # backward + optimize
            loss.backward()
            optimizer.step()

        # record the values in tensorboard
        writer.add_scalar("loss/train", train_loss.avg, epoch + 1)  # average loss
        writer.add_scalar("acc/train", train_acc.avg, epoch + 1)  # average acc

        # ===== val mode =====
        val_acc = AverageMeter("val_acc", ":6.2f")
        val_loss = AverageMeter("val_loss", ":.4e")
        model.eval()
        with torch.no_grad():
            for data in testloader:
                inputs, labels = data[0], data[1].to(device)
                if args.blur_val:
                    inputs = GaussianBlurAll(inputs, (0, 0), args.sigma)
                inputs = inputs.to(device)
                outputs = model(inputs)
                loss = criterion(outputs, labels)
                acc1 = accuracy(outputs, labels, topk=(1,))
                val_loss.update(loss.item(), inputs.size(0))
                val_acc.update(acc1[0], inputs.size(0))

        # record the values in tensorboard
        writer.add_scalar("loss/val", val_loss.avg, epoch + 1)  # average loss
        writer.add_scalar("acc/val", val_acc.avg, epoch + 1)  # average acc

        # ===== save the model =====
        if (epoch + 1) % 10 == 0:
            save_model(
                {
                    "epoch": epoch + 1,
                    "arch": "alexnet-cifar10",
                    "val_loss": val_loss.avg,
                    "val_acc": val_acc.avg,
                    "state_dict": model.state_dict(),
                    "optimizer": optimizer.state_dict(),
                },
                MODEL_PATH,
                epoch + 1,
            )

    print("Finished Training")
    print("Training time elapsed: {:.4f}mins".format((time.time() - train_time) / 60))
    print()

    writer.close()  # close tensorboardX writer

if __name__ == "__main__":
    run_time = time.time()
    main()
    mins = (time.time() - run_time) / 60
    hours = mins / 60
    print("Total run time: {:.4f}mins, {:.4f}hours".format(mins, hours))