import pygame
from pygame.locals import *
from sys import exit
from board import Board, BLANCO, NEGRO, VACIO
from utils import *
from resources.sounds import Sounds
from resources.fonts import Fonts
from resources.images import Images
from referee import Referee
import thread, logging
from player import PC, HUMANO
from input_output.audio import Channel
from main.constants import RESOLUTION1, RESOLUTION2, IZQUIERDA, DERECHA, ARRIBA, ABAJO, STANDART_BOARD_DIMENSION

log = logging.getLogger( 'src.othello.gui' )
log.setLevel( logging.DEBUG )

class GraphicBoard:
    def __init__(self, tablero_logico="", resolucion=RESOLUTION1):
        if tablero_logico == "":
            self.__tablero = Board(STANDART_BOARD_DIMENSION)
        else:
            self.__tablero = tablero_logico
        #Inicializo el una matriz donde guardo las coordenadas de pantalla de cada celda
        self.__coordenadas = []
        for _ in xrange(self.__tablero.get_dimension()):
            self.__coordenadas.append([None]*self.__tablero.get_dimension())
        #Inicializo las variables x e y de las posiciones iniciales del tablero dependiendo de la resolucion
        self.__init_constants(resolucion)
        #Inicializo las imagenes del tablero
        self.__init_images(resolucion)
        #Inicializo las fuentes
        self.__init_fonts()
        #Incializo variables que se utilizan en el algoritmo para la animacion de la celda seleccionada
        self.__nro_imagen = 0
        self.__factor = -1
        self.__cambiar_imagen = 0
        #Cargo en un vector las imagenes utilizadas en la animacion de la celda seleccionada
        efectos = Images.get_images_file_names(resolucion, "animacion_celda")
        self.__imagenes_celda_animada = [pygame.image.load(efectos["chica"]).convert(),pygame.image.load(efectos["mediana"]).convert(),pygame.image.load(efectos["grande"]).convert()]
        self.__animated_cell = Coordinate()
        #Inicializo un reloj que utilizo para la animacion de la celda
        self.__reloj = pygame.time.Clock()
        self.lista_jugadas = []
        #Inicializo constantes para identificar las esquinas del tablero
        self.__IARRIBA = 2
        self.__IABAJO = 3
        self.__DARRIBA = 4
        self.__DABAJO = 5
        #Inicializo la superficie donde voy a guardar el tablero
        self.__init_surface()
        self.__do_animation_cell = True
        self.__marcador = Score()
        self.__render_board()
        self.__up_images = []

    def __init_images(self,resolucion):
        na_imagenes_tablero = Images.get_images_file_names(resolucion, "tablero")
        na_imagenes_piezas = Images.get_images_file_names(resolucion, "piezas")
        #Cargo la imagen de la celda vacia
        self.__imagen_celda = pygame.image.load(na_imagenes_tablero["celda_vacia"]).convert()
        #Cargo la imagen de las cabeceras del tablero
        self.__imagen_tablero_bordev = pygame.image.load(na_imagenes_tablero["borde_vertical"]).convert()
        self.__imagen_tablero_bordeh = pygame.image.load(na_imagenes_tablero["borde_horizontal"]).convert()
        #Cargo las imagenes de las piezas
        self.__imagen_pieza_blanca = pygame.image.load(na_imagenes_piezas["blanca"]).convert_alpha()
        self.__imagen_pieza_negra = pygame.image.load(na_imagenes_piezas["negra"]).convert_alpha()
        self.__imagen_marca = pygame.image.load(na_imagenes_tablero["marca"]).convert_alpha()

    def __init_constants(self,resolucion):
        #Si la resolucion es de 800x600
        if resolucion == RESOLUTION1:
            self.__pos_ini = Coordinate(0,0)
            #Constantes que se utilizan en la funcion que renderea el tablero
            self.__inc_numero_borde_h = Coordinate(25,-5)
            self.__inc_numero_borde_vi = Coordinate(8,10)
            self.__inc_numero_borde_vd = Coordinate(7,10)
            self.__inc_borde_vd = Coordinate(70,0)
            self.__mitad_tam_celda = 35
            self.__tam_celda = 70
            if self.__tablero.get_dimension() == 4:
                self.__pos_graphic_board = Coordinate(200,0)
            else:
                self.__pos_graphic_board = Coordinate(155,0)
            self.__tam_fuente = 35
            self.__current_cell = Coordinate(0,0)
        #Si al resolucion es de 1200x900
        elif resolucion == RESOLUTION2:
            self.__pos_ini = Coordinate(0,0)
            #Constantes que se utilizan en la funcion que renderea el tablero
            self.__inc_numero_borde_h = Coordinate(35,-7)
            self.__inc_numero_borde_vi = Coordinate(10,17)
            self.__inc_numero_borde_vd = Coordinate(11,17)
            self.__inc_borde_vd = Coordinate(105,0)
            self.__mitad_tam_celda = 52
            self.__tam_celda = 105
            if self.__tablero.get_dimension() == 4:
                self.__pos_graphic_board = Coordinate(340,0)
            else:
                self.__pos_graphic_board = Coordinate(225,0)
            self.__tam_fuente = 50
            self.__current_cell = Coordinate(0,0)
        else:
            raise Exception("Resolucion desconocida (GraphicBoard Class)")

    def __init_fonts(self):
        self.__fuente = pygame.font.Font(Fonts.get_fonts_file_names()["fuente1"],self.__tam_fuente)

    def __init_surface(self):
        tam = (self.__mitad_tam_celda * 2) + (self.__tam_celda * self.__tablero.get_dimension())
        self.__surface = pygame.Surface((tam, tam), SRCALPHA,32)
        self.__conf_surface = pygame.Surface((tam, tam), SRCALPHA,32)

    def set_coord_selected_cell(self, nueva_coord):
        self.__current_cell.set(nueva_coord)

    def get_coord_selected_cell(self):
        return self.__current_cell

    def get_logical_board(self):
        return self.__tablero

    def get_board_pos(self):
        return self.__pos_graphic_board

    def get_board_surface(self):
        return self.__surface

    def get_configuration_surface(self):
        return self.__conf_surface

    """

        Render a piece in a specific position on the board

        Params:
            - valor_casilla : BLACK or WHITE
            - x_s, y_s : surface render coordinate
            - superficie : render surface
    """
    def __render_piece(self,valor_casilla,x_s,y_s,superficie):
        if valor_casilla == BLANCO:
            superficie.blit(self.__imagen_pieza_blanca, (x_s,y_s))
        elif valor_casilla == NEGRO:
            superficie.blit(self.__imagen_pieza_negra, (x_s,y_s))

    """

        Render an empty cell in the screen

        Params:
            - celda_coord : cell screen coordinate
            - tablero_coord : cell board coordinate
            - ventana : render screen
    """
    def __render_cell(self,celda_coord, superficie):
        #Coordenadas de pantalla
        x = celda_coord.x
        y = celda_coord.y
        #Muestro la celda en blanco
        superficie.blit(self.__imagen_celda, (x,y))

    def render(self):
        #self.__set_corner_pixels(self.__surface)
        #self.__render_all_corners()
        #self.__render_board()
        return self.__surface
        #superficie.blit(self.__surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))

    """

        Render the board in the graphic board surface

        Params:
            None
    """
    def __render_board(self):
        x = self.__pos_ini.x + self.__mitad_tam_celda
        for j in xrange(0, self.__tablero.get_dimension()):
            y = self.__pos_ini.y
            for i in xrange(0, self.__tablero.get_dimension()):
                #Muestro el borde horizontal junto con el numero si es la primera fila
                if i == 0:
                    self.__surface.blit(self.__imagen_tablero_bordeh, (x,y))
                    #Numero del borde horizontal superior
                    nro_horizontal = self.__fuente.render(str(j+1), True, (255,255,0))
                    nx = x + self.__inc_numero_borde_h.x
                    ny = y + self.__inc_numero_borde_h.y
                    self.__surface.blit(nro_horizontal, (nx,ny))
                    y += self.__mitad_tam_celda
                #Calculo el numero a imprimir en el borde vertical
                nro_vertical = str(self.__tablero.get_dimension() - i)
                nro_vertical_text = self.__fuente.render(nro_vertical, True, (255,255,0))
                #Muestro el borde vertical izquierda junto con el numero si es la primera columna
                if j == 0:
                    self.__surface.blit(self.__imagen_tablero_bordev, (self.__pos_ini.x,y))
                    #Numero del borde vertical izquierda
                    nx = self.__pos_ini.x + self.__inc_numero_borde_vi.x
                    ny = y + self.__inc_numero_borde_vi.y
                    self.__surface.blit(nro_vertical_text, (nx,ny))
                #Render una celda vacio
                self.__render_cell(Coordinate(x,y), self.__surface)
                #Guardo la coordenada de pantalla de la celda
                self.__coordenadas[i][j] = Coordinate(x,y)
                #Muestro el borde vertical derecha junto con los numeros si es la ultima columna
                if j == self.__tablero.get_dimension() - 1:
                    nx = x + self.__inc_borde_vd.x
                    ny = y + self.__inc_borde_vd.y
                    self.__surface.blit(self.__imagen_tablero_bordev, (nx,ny))
                    #Numero del borde vertical izquierda
                    nx = nx + self.__inc_numero_borde_vd.x
                    ny = ny + self.__inc_numero_borde_vd.y
                    self.__surface.blit(nro_vertical_text, (nx,ny))
                #Muestro el borde horizontal si es la ultima fila
                if i == self.__tablero.get_dimension() - 1:
                    y += self.__tam_celda
                    self.__surface.blit(self.__imagen_tablero_bordeh, (x,y))
                    #Numero del borde horizontal inferior
                    nx = x + self.__inc_numero_borde_h.x
                    ny = y + self.__inc_numero_borde_h.y
                    self.__surface.blit(nro_horizontal, (nx,ny))
                y += self.__tam_celda
            x += self.__tam_celda

    def render_configuration(self, ventana=""):
        #self.__render_board()
        tam = (self.__mitad_tam_celda * 2) + (self.__tam_celda * self.__tablero.get_dimension())
        self.__conf_surface = pygame.Surface((tam, tam),SRCALPHA,32)
        dim = self.__tablero.get_dimension()
        #superficie = self.__surface
        for i in xrange(0,dim):
            for j in xrange(0,dim):
                valor_casilla = self.__tablero.get_valor_casilla(i,j)
                if valor_casilla != VACIO:
                    x_s = self.__coordenadas[i][j].x
                    y_s = self.__coordenadas[i][j].y
                    self.__render_piece(valor_casilla, x_s, y_s, self.__conf_surface)
        ventana.blit(self.__surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
        ventana.blit(self.__conf_surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))

    def __render_background_corner(self,esquina):
        if esquina == self.__IARRIBA:
            x_ini = self.__pos_ini.x
            y_ini = self.__pos_ini.y
            esquina = self.__esquinaiarriba
        elif esquina == self.__IABAJO:
            x_ini = self.__pos_ini.x
            y_ini = self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
            esquina = self.__esquinaiabajo
        elif esquina == self.__DARRIBA:
            x_ini = self.__pos_ini.x + self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
            y_ini = self.__pos_ini.y
            esquina = self.__esquinadarriba
        elif esquina == self.__DABAJO:
            x_ini = self.__pos_ini.x + self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
            y_ini = self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
            esquina = self.__esquinadabajo
        for i in xrange(0,self.__mitad_tam_celda):
            x = x_ini + i
            for j in xrange(0,self.__mitad_tam_celda):
                y = y_ini + j
                self.__surface.set_at((x,y),esquina[i][j])

    def __render_all_corners(self):
        self.__render_background_corner(self.__IARRIBA)
        self.__render_background_corner(self.__IABAJO)
        self.__render_background_corner(self.__DARRIBA)
        self.__render_background_corner(self.__DABAJO)

    def __get_corner_pixels(self,x_ini,y_ini,ventana):
        esquina = []
        for _ in xrange(self.__mitad_tam_celda):
            esquina.append([None]*self.__mitad_tam_celda)

        for i in xrange(0,self.__mitad_tam_celda):
                for j in xrange(0,self.__mitad_tam_celda):
                    x = x_ini + i
                    y = y_ini + j
                    esquina[i][j] = ventana.get_at((x,y))
        return esquina

    def __set_corner_pixels(self,ventana):
        #Esquina Izquierda Arriba
        x_ini = self.__pos_graphic_board.x
        y_ini = self.__pos_graphic_board.y
        self.__esquinaiarriba = self.__get_corner_pixels(x_ini,y_ini,ventana)
        #Esquina Izquierda Abajo
        y_ini = self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
        self.__esquinaiabajo = self.__get_corner_pixels(x_ini, y_ini, ventana)
        #Esquina Derecha Arriba
        x_ini = self.__pos_graphic_board.x + self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
        y_ini = self.__pos_graphic_board.y
        self.__esquinadarriba = self.__get_corner_pixels(x_ini, y_ini, ventana)
        #Esquina Derecha Abajo
        y_ini = self.__mitad_tam_celda + (self.__tam_celda * self.__tablero.get_dimension())
        self.__esquinadabajo = self.__get_corner_pixels(x_ini, y_ini, ventana)

    def __seleccion_is_on_corner(self):
        #coord_logica = self.graphic_coord_to_logic_coord(self.__current_cell)
        coord_logica = self.__current_cell
        dim = self.__tablero.get_dimension()
        if (coord_logica.x == 0 or coord_logica.x == dim - 1) and \
           (coord_logica.y == 0 or coord_logica.y == dim - 1):
            return True
        else:
            return False

    def __seleccion_is_on_border(self):
        #coord_logica = self.graphic_coord_to_logic_coord(self.__current_cell)
        coord_logica = self.__current_cell
        dim = self.__tablero.get_dimension()
        if coord_logica.x == 0 or coord_logica.x == dim - 1 or \
           coord_logica.y == 0 or coord_logica.y == dim - 1:
            return True
        else:
            return False

    def end_animation_cell(self):
        self.__do_animation_cell = False
        pygame.time.wait(200)

    def animation_cell_running(self):
        return self.__do_animation_cell

    def set_up_image(self, hash_image):
        self.__up_images.append(hash_image)

    def remove_up_images(self):
        self.__up_images = []

    def __re_calculate_update_rect(self):
        dim = self.__tablero.get_dimension()
        coord_logica = self.__current_cell
        coord_actual = Coordinate(self.__coordenadas[coord_logica.x][coord_logica.y].x, self.__coordenadas[self.__current_cell.x][self.__current_cell.y].y)
        if self.__seleccion_is_on_corner():
            if coord_logica.x == 0 and coord_logica.y == 0:
                #Esquina Izquierda Arriba
                x = coord_actual.x - self.__imagen_tablero_bordev.get_width()
                y = coord_actual.y - self.__imagen_tablero_bordeh.get_height()
            elif coord_logica.x == 0 and coord_logica.y == dim - 1:
                #Esquina Derecha Arriba
                x = coord_actual.x - self.__imagen_celda.get_width()
                y = coord_actual.y - self.__imagen_tablero_bordeh.get_height()
            elif coord_logica.x == dim - 1 and coord_logica.y == 0:
                #Esquina Izquierda Abajo
                x = coord_actual.x - self.__imagen_tablero_bordev.get_width()
                y = coord_actual.y - self.__imagen_celda.get_height()
            else:
                #Esquina Derecha Abajo
                x = coord_actual.x - self.__imagen_celda.get_width()
                y = coord_actual.y - self.__imagen_celda.get_height()
            w = 2 * self.__imagen_celda.get_width() + self.__imagen_tablero_bordev.get_width()
            h = 2 * self.__imagen_celda.get_height() + self.__imagen_tablero_bordeh.get_height()
        elif self.__seleccion_is_on_border():
            #log.debug("La seleccion esta en un borde")
            if coord_logica.x == 0:
                #Borde Arriba
                x = coord_actual.x - self.__imagen_celda.get_width()
                y = coord_actual.y - self.__imagen_tablero_bordeh.get_height()
                w = 3 * self.__imagen_celda.get_width()
                h = 2 * self.__imagen_celda.get_height() + self.__imagen_tablero_bordeh.get_height()
            elif coord_logica.y == dim - 1:
                #Borde Derecha
                x = coord_actual.x - self.__imagen_celda.get_width()
                y = coord_actual.y - self.__imagen_celda.get_height()
                w = 2 * self.__imagen_celda.get_width() + self.__imagen_tablero_bordev.get_width()
                h = 3 * self.__imagen_celda.get_height()
            elif coord_logica.y == 0:
                #Borde Izquierdo
                x = coord_actual.x - self.__imagen_tablero_bordev.get_width()
                y = coord_actual.y - self.__imagen_celda.get_height()
                w = 2 * self.__imagen_celda.get_width() + self.__imagen_tablero_bordev.get_width()
                h = 3 * self.__imagen_celda.get_height()
            else:
                #Borde Abajo
                x = coord_actual.x-self.__imagen_celda.get_width()
                y = coord_actual.y-self.__imagen_celda.get_height()
                w = 3 * self.__imagen_celda.get_width()
                h = 2 * self.__imagen_celda.get_height() + self.__imagen_tablero_bordeh.get_height()
        else:
            x = coord_actual.x-self.__imagen_celda.get_width()
            y = coord_actual.y-self.__imagen_celda.get_height()
            w = 3 * self.__imagen_celda.get_width()
            h = 3 * self.__imagen_celda.get_height()
        rect = pygame.Rect((x,y),(w,h))
        return rect

    """

        Render the animated cell

        Params:
            - pos_casilla : animated cell coordinate
            - ventana : render screen
    """
    def render_animation_cell(self, superficie):
        #update_rect = pygame.Rect((self.__pos_graphic_board.x, self.__pos_graphic_board.y), (self.__surface.get_width(), self.__surface.get_height()))
        self.__set_corner_pixels(superficie)
        self.__do_animation_cell = True
        while self.__do_animation_cell:
            render_all_board = False
            if not self.__current_cell.equal(self.__animated_cell):
                self.__animated_cell.set(self.__current_cell)
                self.__factor = -1
                self.__nro_imagen = 0
                render_all_board = True
            #Coordenadas de pantalla
            x = self.__coordenadas[self.__current_cell.x][self.__current_cell.y].x + self.__pos_graphic_board.x
            y = self.__coordenadas[self.__current_cell.x][self.__current_cell.y].y + self.__pos_graphic_board.y
            i = self.__nro_imagen
            f = self.__factor
            if i == 2 or i == 0:
                f *= -1
            i = i + f
            if self.__seleccion_is_on_corner():
                self.__render_all_corners()
            if render_all_board:
                superficie.blit(self.__surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
                superficie.blit(self.__conf_surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
                rect_sub_sup = self.__re_calculate_update_rect()
            else:
                sub_sup_tablero = self.__surface.subsurface(rect_sub_sup)
                sub_sup_conf = self.__conf_surface.subsurface(rect_sub_sup)
                superficie.blit(sub_sup_tablero,(rect_sub_sup.left+self.__pos_graphic_board.x,rect_sub_sup.top+self.__pos_graphic_board.y))
                superficie.blit(sub_sup_conf,(rect_sub_sup.left+self.__pos_graphic_board.x,rect_sub_sup.top+self.__pos_graphic_board.y))
            superficie.blit(self.__imagenes_celda_animada[i], (x-(i+i),y-(i+i)))
            #Muestro alguna pieza si es que existe alguna en la celda animada
            valor_casilla = self.__tablero.get_valor_casilla(self.__current_cell.x,self.__current_cell.y)
            if valor_casilla != VACIO:
                self.__render_piece(valor_casilla,x,y,superficie)
            else:
                #Si es una casilla vacia muestro alguna marca si es que existe alguna en la celda animada
                self.__render_possible_move(self.__current_cell, superficie)
            self.__nro_imagen = i
            self.__factor = f
            if not render_all_board:
                #Si no rendereo todo el tablero, el rectangulo de actualizacion es solo el tamano del sub rectangulo.
                update_rect = pygame.Rect((rect_sub_sup.left + self.__pos_graphic_board.x,rect_sub_sup.top + self.__pos_graphic_board.y),(rect_sub_sup.width,rect_sub_sup.height))
            else:
                update_rect = pygame.Rect((self.__pos_graphic_board.x, self.__pos_graphic_board.y), (self.__surface.get_width(), self.__surface.get_height()))
            if not self.__up_images == []:
                for image in self.__up_images:
                    rect_img = pygame.Rect(image['posicion'],(image['imagen'].get_width(),image['imagen'].get_height()))
                    if rect_img.colliderect(update_rect):
                        superficie.blit(image['imagen'],image['posicion'])
            pygame.time.wait(150)
            pygame.display.update(update_rect)

    def __print_log(self, archivo, mensaje):
        if archivo != "":
            print >> archivo, mensaje

    def do_move(self, direccion, audio, archivo_juego=""):
        #No Pudo realizar el movimiento
        if  not self.__set_new_coordinates(direccion):
            audio.play_fx_sound("board","mal_mov")
            if direccion == ARRIBA:
                dir_mov = "arriba"
            elif direccion == ABAJO:
                dir_mov = "abajo"
            elif direccion == DERECHA:
                dir_mov = "derecha"
            else:
                dir_mov = "izquierda"
            self.__print_log(archivo_juego, "ERROR!, estaba en: " + str(self.__current_cell) + " e hizo un movimiento " + dir_mov)
        else:
            volumen_laterales = {"volumen":self.__stereo_pan(self.__current_cell.y)}
            self.play_box_sound(audio, self.__current_cell, self.lista_jugadas, volumen_laterales)

    def __set_new_coordinates(self, direccion):
        nueva_coord = self.get_new_coordinates(direccion)
        if not nueva_coord == False:
            self.__current_cell = nueva_coord
            return True
        else:
            return False

    """

        Get the new coordinate from the actual coordinate

        Params:
            - direccion : new coordinate direction (left,right,up,down)
            - coordenada_actual : actual coordinate
    """
    def get_new_coordinates(self,direccion):
        coordenada_nueva = self.__current_cell

        if direccion == IZQUIERDA:
            if self.__current_cell.y > 0:
                coordenada_nueva.y -= 1
            else:
                return False
        elif direccion == DERECHA:
            if self.__current_cell.y < self.__tablero.get_dimension() - 1:
                coordenada_nueva.y += 1
            else:
                return False
        elif direccion == ARRIBA:
            if self.__current_cell.x > 0:
                coordenada_nueva.x -= 1
            else:
                return False
        elif direccion == ABAJO:
            if self.__current_cell.x < self.__tablero.get_dimension() - 1:
                coordenada_nueva.x += 1
            else:
                return False

        return coordenada_nueva

    """

        Render a possible move if exist in the list of possibles moves
        Params:
            - coord : possible move coordinate
            - ventana : render screen
    """
    def __render_possible_move(self,coord,ventana):
        coordenadas = self.__coordenadas
        for c in self.lista_jugadas:
            if c.x == coord.x and c.y == coord.y:
                x = coordenadas[c.x][c.y].x + self.__pos_graphic_board.x
                y = coordenadas[c.x][c.y].y + self.__pos_graphic_board.y
                #Muestro la marca en el tablero
                ventana.blit(self.__imagen_marca, (x,y))
                break

    """

        Render a list of possibles moves

        Params:
            - ventana : render screen
    """
    def render_list_possible_moves(self, ventana):
        coordenadas = self.__coordenadas
        for c in self.lista_jugadas:
            x = coordenadas[c.x][c.y].x
            y = coordenadas[c.x][c.y].y
            #Muestro la marca en el tablero
            self.__conf_surface.blit(self.__imagen_marca, (x,y))
            ventana.blit(self.__conf_surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))

    def __render_animation_image(self,imagen,inc,coord,ventana):
        x = self.__coordenadas[coord.x][coord.y].x + self.__pos_graphic_board.x
        y = self.__coordenadas[coord.x][coord.y].y + self.__pos_graphic_board.y
        ventana.blit(imagen, (x-inc,y-inc))
        self.__render_piece(self.__tablero.get_valor_casilla(coord.x,coord.y),x,y,ventana)

    def __render_line_animation(self,linea,color,sonido,audio,ventana):
        audio.play_voice_sound("board","linea_entre")
        graphic_coord = self.convert_logic_coord_to_graphic_coord(linea["casilla_inicial"])
        volumen_laterales = {"volumen":self.__stereo_pan(linea["casilla_inicial"].y)}
        self.__play_coordinate_sound(audio, graphic_coord,volumen_laterales)
        audio.play_voice_sound("otros","y")
        volumen_laterales = {"volumen":self.__stereo_pan(linea["casilla_final"].y)}
        graphic_coord = self.convert_logic_coord_to_graphic_coord(linea["casilla_final"])
        self.__play_coordinate_sound(audio, graphic_coord,volumen_laterales)
        i = self.__nro_imagen
        f = self.__factor
        can_casillas_v = linea["can_fichas_volteables"] - 1
        update_rect = pygame.Rect((self.__pos_graphic_board.x, self.__pos_graphic_board.y), (self.__surface.get_width(), self.__surface.get_height()))
        while can_casillas_v >= 0:
            if audio.silence_channel():
                self.__tablero.turn_pieces2(linea["casillas_volteables"][can_casillas_v],color)
                can_casillas_v = can_casillas_v - 1
                volumen_laterales = {"volumen":self.__stereo_pan(linea["casillas_volteables"][can_casillas_v].y)}
                audio.play_fx_sound("board",sonido,volumen_laterales)
                self.render_configuration(ventana)
            if i == 2:
                f *= -1
            elif i == 0:
                f *= -1
            i = i + f
            ventana.blit(self.__surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
            ventana.blit(self.__conf_surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
            self.__render_animation_image(self.__imagenes_celda_animada[i], i+i, linea["casilla_inicial"], ventana)
            for casilla in linea["casillas_volteables"]:
                self.__render_animation_image(self.__imagenes_celda_animada[i], i+i, casilla, ventana)
            self.__render_animation_image(self.__imagenes_celda_animada[i], i+i, linea["casilla_final"], ventana)
            pygame.display.update(update_rect)
            pygame.time.wait(150)
        ventana.blit(self.__surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
        ventana.blit(self.__conf_surface,(self.__pos_graphic_board.x,self.__pos_graphic_board.y))
        pygame.display.update(update_rect)

    def __play_coordinate_sound(self, audio, coord, vol):
        audio.play_voice_sound("numero",get_number_word(coord.y),vol)
        audio.play_voice_sound("numero",get_number_word(coord.x),vol)

    def __play_move_sound(self, audio, coord):
        volumen_laterales = {"volumen":self.__stereo_pan(coord.y)}
        audio.play_voice_sound("board","jugada_en",volumen_laterales)
        graphic_coord = self.convert_logic_coord_to_graphic_coord(coord)
        self.__play_coordinate_sound(audio, graphic_coord,volumen_laterales)

    def set_piece(self, coord, color, sonido_color, audio, ventana):
        pudo_colocar = self.__tablero.set_piece(coord,color)
        if pudo_colocar:
            self.lista_jugadas = []
            self.render_configuration(ventana)
            volumen_laterales = {"volumen":self.__stereo_pan(coord.y)}
            audio.play_fx_sound("board",sonido_color,volumen_laterales)
            self.__play_move_sound(audio, coord)
            update_rect = pygame.Rect((self.__pos_graphic_board.x, self.__pos_graphic_board.y), (self.__surface.get_width(), self.__surface.get_height()))
            pygame.display.update(update_rect)
            return True
        else:
            return False

    def convert_logic_coord_to_graphic_coord(self, logic_coord):
        return Coordinate(self.__tablero.get_dimension()-logic_coord.x,logic_coord.y+1)

    def graphic_coord_to_logic_coord(self, coord_grafica):
        return Coordinate(self.__tablero.get_dimension()-coord_grafica.x,coord_grafica.y-1)

    def do_line_animation(self, coord, color, sonido, canal, marcador, ventana):
        lineas_formadas = self.get_logical_board().get_lines(color,coord)
        for linea in lineas_formadas:
            self.__render_line_animation(linea,color,sonido,canal,ventana)
            marcador.render_numbers(self.get_logical_board(),color,ventana)

    def play_possible_moves_sound(self, audio):
        audio.play_voice_sound("board","jugada_posible")
        can_jp = len(self.lista_jugadas)
        con = 0
        if can_jp > 0:
            for jp_coord in self.lista_jugadas:
                con += 1
                if con == can_jp:
                    audio.play_voice_sound("otros","y_en")
                else:
                    audio.play_voice_sound("otros","en")
                graphic_coord = self.convert_logic_coord_to_graphic_coord(jp_coord)
                volumen_laterales = {"volumen":self.__stereo_pan(jp_coord.y)}
                self.__play_coordinate_sound(audio, graphic_coord, volumen_laterales)
                audio.play_voice_sound("otros","silencio")
        else:
            audio.play_voice_sound("board","no_hay_jugadas_posibles")

    def play_count_pieces_sound(self, audio, juego=""):
        if juego != "":
            jugador = juego.get_turn()
            can_jugadas_posibles = len(juego.get_list_possible_moves())
        else:
            can_jugadas_posibles = len(self.lista_jugadas)
        audio.play_voice_sound("board","tablero_tiene")
        can_fichas_blancas = self.__tablero.get_number_white_pieces()
        can_fichas_negras = self.__tablero.get_number_black_pieces()
        if can_fichas_blancas > 1:
            audio.play_voice_sound("numero",get_number_word(can_fichas_blancas))
            audio.play_voice_sound("board","fichas_blancas")
        else:
            audio.play_voice_sound("numero","una")
            audio.play_voice_sound("board","ficha_blanca")
        if can_fichas_negras > 1:
            audio.play_voice_sound("numero",get_number_word(can_fichas_negras))
            audio.play_voice_sound("board","fichas_negras")
        else:
            audio.play_voice_sound("numero","una")
            audio.play_voice_sound("board","ficha_negra")
        if juego == "" or (juego != "" and not juego.game_ended()):
            audio.play_voice_sound("otros","y")
            if not can_jugadas_posibles == 1:
                if can_jugadas_posibles == 0 and (jugador != "" and jugador.get_name() == HUMANO):
                    audio.play_voice_sound("game","no_hay_jugadas_posbiles")
                    return False
                else:
                    audio.play_voice_sound("numero",get_number_word(can_jugadas_posibles))
                    audio.play_voice_sound("board","jugadas_posibles")
                    return True
            else:
                audio.play_voice_sound("numero","una")
                audio.play_voice_sound("board","jugada_posible")
                return True

    def __stereo_pan(self, coord_columna):
        volumen_derecha = float(coord_columna) / (self.__tablero.get_dimension() - 1)
        volumen_izquierda = 1.0 - volumen_derecha
        return {"derecha":volumen_derecha, "izquierda":volumen_izquierda}

    def __play_coordinate_sound_extra_info(self, audio, coord, volumen):
        graphic_coord = self.convert_logic_coord_to_graphic_coord(coord)
        audio.play_voice_sound("board","columna",volumen)
        audio.play_voice_sound("numero",get_number_word(graphic_coord.y),volumen)
        audio.play_voice_sound("board","fila",volumen)
        audio.play_voice_sound("numero",get_number_word(graphic_coord.x),volumen)

    def play_box_info_sound(self, audio, coord=""):
        if coord == "":
            coord = self.__current_cell
        volumen_laterales = {"volumen":self.__stereo_pan(coord.y)}
        self.__play_coordinate_sound_extra_info(audio, coord, volumen_laterales)
        self.play_box_sound(audio, coord, [], volumen_laterales)

    def play_box_sound(self, audio, coord, lista_jugadas, volumen_laterales):
        tablero = self.__tablero
        jugada_posible = False
        for jugada in lista_jugadas:
            if jugada.x == coord.x and jugada.y == coord.y:
                audio.play_fx_sound("board","jugada_posible",volumen_laterales)
                audio.play_voice_sound("board","jugada_posible",volumen_laterales)
                jugada_posible = True
                break
        if not jugada_posible:
            valor_casilla = tablero.get_valor_casilla(coord.x,coord.y)
            if valor_casilla == VACIO:
                audio.play_fx_sound("board","vacio",volumen_laterales)
                audio.play_voice_sound("board","vacio",volumen_laterales)
            elif valor_casilla == BLANCO:
                audio.play_fx_sound("board","blanco",volumen_laterales)
                audio.play_voice_sound("board","blanco",volumen_laterales)
            elif valor_casilla == NEGRO:
                audio.play_fx_sound("board","negro",volumen_laterales)
                audio.play_voice_sound("board","negro",volumen_laterales)
            else:
                raise Exception("Error!, valor de casilla desconocido (Othello.Gui Class)")

class Score:
    def __init__(self, resolucion=(800,600)):
        #Inicilializo las constasntes graficas dependiendo de la resolucion
        self.__init_constants(resolucion)
        self.__resolucion = resolucion
        #Inicializo las fuentes
        self.__init_fonts()
        #Inicializo las imagenes
        self.__init_images(resolucion)
        self.__sup_marcador_izq = ""
        self.__sup_marcador_der = ""

    def __init_constants(self,resolucion):
        #Si la resolucion es de 800x600
        if resolucion == RESOLUTION1:
            self.__barra = Coordinate(0,0)
            self.__bandera = Coordinate(6,6)
            self.__tam_fuente = 60
            self.__x_marcador = 15
            self.__y_marcador = 300
        #Si la resolucion es de 1200x900
        elif resolucion == RESOLUTION2:
            self.__barra = Coordinate(0,0)
            self.__bandera = Coordinate(9,7)
            self.__tam_fuente = 80
            self.__x_marcador = 25
            self.__y_marcador = 350
        else:
            raise Exception("Resolucion desconocida (Score Class)")
        #Iniciliazo los colores de las fuentes
        self.__color_negro = (0,0,0)
        self.__color_amarillo = (255,255,0)

    def __init_fonts(self):
        self.__fuente = pygame.font.Font(Fonts.get_fonts_file_names()["fuente1"],self.__tam_fuente)

    def __init_images(self, resolucion):
        na_imagenes_marcador = Images.get_images_file_names(resolucion, "marcador")
        self.__imagen_barra_negra = pygame.image.load(na_imagenes_marcador["barra_negra"]).convert_alpha()
        self.__imagen_barra_amarilla = pygame.image.load(na_imagenes_marcador["barra_amarilla"]).convert_alpha()
        self.__imagen_bandera_negra = pygame.image.load(na_imagenes_marcador["bandera_negra"]).convert_alpha()
        self.__imagen_bandera_blanca = pygame.image.load(na_imagenes_marcador["bandera_blanca"]).convert_alpha()

    def refresh(self, ventana):
        if not self.__sup_marcador_der == "" and not self.__sup_marcador_izq == "":
            ventana.blit(self.__sup_marcador_izq,(0,0))
            ventana.blit(self.__sup_marcador_der,(self.__resolucion[0] - self.__sup_marcador_der.get_width(),0))
            x1 = self.__resolucion[0] - self.__imagen_barra_negra.get_width()
            x2 = self.__imagen_barra_negra.get_width()
            y2 = self.__imagen_barra_negra.get_height()
            update_rect_izq = pygame.Rect((0, 0), (x2, y2))
            update_rect_der = pygame.Rect((x1, 0), (x2, y2))
            pygame.display.update([update_rect_izq,update_rect_der])

    def render_numbers(self, tablero, turno, ventana):
        self.__render(tablero, ventana, turno)
        x1 = self.__resolucion[0] - self.__imagen_barra_negra.get_width()
        x2 = self.__imagen_barra_negra.get_width()
        y2 = self.__imagen_barra_negra.get_height() / 2
        update_rect_izq = pygame.Rect((0, 0), (x2, y2))
        update_rect_der = pygame.Rect((x1, 0), (x2, y2))
        pygame.display.update([update_rect_izq,update_rect_der])

    def render_all(self, tablero, turno, ventana):
        self.__render(tablero, ventana, turno)
        x1 = self.__resolucion[0] - self.__imagen_barra_negra.get_width()
        x2 = self.__imagen_barra_negra.get_width()
        y2 = self.__imagen_barra_negra.get_height()
        update_rect_izq = pygame.Rect((0, 0), (x2, y2))
        update_rect_der = pygame.Rect((x1, 0), (x2, y2))
        #pygame.display.update([update_rect_izq,update_rect_der])

    def __render(self,tablero,ventana,turno):
        self.__sup_marcador_izq = self.render_left_score(tablero, turno)
        self.__sup_marcador_der = self.render_right_score(tablero, turno)
        #Pego las superficies de los marcadores a la ventana
        ventana.blit(self.__sup_marcador_izq,(0,0))
        ventana.blit(self.__sup_marcador_der,(self.__resolucion[0] - self.__sup_marcador_der.get_width(),0))

    def render_left_score(self, tablero, turno):
        superficie = pygame.Surface(self.__imagen_barra_amarilla.get_size(),SRCALPHA,32)
        #Copio la barra izquierda del marcador cambiando el color dependiendo del turno
        if turno == BLANCO:
            superficie.blit(self.__imagen_barra_amarilla, (self.__barra.x,self.__barra.y))
            color_fuente_barra_iz = self.__color_negro
        else:
            superficie.blit(self.__imagen_barra_negra, (self.__barra.x,self.__barra.y))
            color_fuente_barra_iz = self.__color_amarillo
        #Copio la bandera blanca sobre la barra
        superficie.blit(self.__imagen_bandera_blanca, (self.__bandera.x,self.__bandera.y))
        marcador_blancas = tablero.get_number_white_pieces()
        if marcador_blancas < 10:
            marcador_blancas = "0" + str(marcador_blancas)
        else:
            marcador_blancas = str(marcador_blancas)
        #Muestro los marcadores
        superficie.blit(self.__fuente.render(marcador_blancas, True, color_fuente_barra_iz), (self.__x_marcador,self.__y_marcador))
        self.__sup_marcador_izq = superficie
        return superficie

    def render_right_score(self, tablero, turno):
        superficie = pygame.Surface(self.__imagen_barra_negra.get_size(),SRCALPHA,32)
        #Copio la barra derecha del marcador
        if turno == NEGRO:
            superficie.blit(self.__imagen_barra_amarilla, (self.__barra.x,self.__barra.y))
            color_fuente_barra_de = self.__color_negro
        else:
            superficie.blit(self.__imagen_barra_negra, (self.__barra.x,self.__barra.y))
            color_fuente_barra_de = self.__color_amarillo
        #Copio la bandera negra sobre la barra
        superficie.blit(self.__imagen_bandera_negra, (self.__bandera.x,self.__bandera.y))
        marcador_negras = tablero.get_number_black_pieces()
        if marcador_negras < 10:
            marcador_negras = "0" + str(marcador_negras)
        else:
            marcador_negras = str(marcador_negras)
        #Muestro los marcadores
        superficie.blit(self.__fuente.render(marcador_negras, True, color_fuente_barra_de), (self.__x_marcador,self.__y_marcador))
        self.__sup_marcador_der = superficie
        return superficie


import pytest
import sys
import mock

sys.path.append('..')

from reddit import Reddit
from prawcore.exceptions import OAuthException
from prawcore import NotFound

class Submission:

  def __init__(self, title, date, source, comment_count, post_id):
    self.title = title
    self.date = date
    self.source = source
    self.comment_count = comment_count
    self.post_id = post_id

submission = [
  Submission('title1', 'date1', 'source1', 1, 'a'),
  Submission('title2', 'date2', 'source2', 2, 'b'),
  Submission('title3', 'date3', 'source3', 3, 'c')
]

class TestReddit:

  @pytest.fixture(scope="function")
  def mock_reddit(self):
    client = mock.Mock()
    return Reddit(client)

  def test_query_when_success(self, mock_reddit):
    mock_query = mock.MagicMock()
    mock_subreddit = mock.Mock()
    mock_reddit.client.subreddit.return_value = mock_subreddit
    mock_subreddit.search.return_value = iter(submission)
    assert len(mock_reddit.query(mock_query)) is 3

  def test_query_when_failure(self, mock_reddit):
    mock_query = mock.MagicMock()
    mock_subreddit = mock.Mock()
    mock_reddit.client.subreddit.return_value = mock_subreddit
    mock_submission = mock.MagicMock()
    mock_title = mock.PropertyMock(side_effect=OAuthException('test', 'test', 'test'))
    type(mock_submission).title = mock_title
    mock_subreddit.search.return_value = iter([mock_submission])
    assert not mock_reddit.query(mock_query)

  def test_subreddit_exists_when_exists(self, mock_reddit):
    mock_subreddits = mock.Mock()
    mock_reddit.client.subreddits.return_value = mock_subreddits
    mock_reddit.subreddit_exists('testsubreddit')
    mock_reddit.client.subreddits.search_by_name.assert_called_once()

  def test_subreddit_exists_when_doesnt_exists(self, mock_reddit):
    mock_subreddits = mock.Mock()
    mock_reddit.client.subreddits.return_value = mock_subreddits
    mock_reddit.client.subreddits.search_by_name.side_effect = NotFound(mock.Mock())
    assert mock_reddit.subreddit_exists('testsubreddit') is False


from torch import nn
from torch import cat

class MelCNN(nn.Module):
    def __init__(self):
        super(MelCNN, self).__init__()

        self.conv1 = self.conv_block(1, 16)
        self.conv2 = self.conv_block(16, 32)
        self.conv3 = self.conv_block(32, 64)
        self.conv4 = self.conv_block(64, 64)

        self.flatten = nn.Flatten()
        self.linear = nn.Sequential(nn.Linear(252 * 64, 128),
                                    nn.ReLU(),
                                    nn.Linear(128, 1),
                                    nn.Sigmoid())

    @staticmethod
    def conv_block(n_in, n_out):
        return nn.Sequential(nn.Conv2d(n_in, n_out, (3, 3)),
                             nn.ReLU(),
                             nn.AvgPool2d(2))

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

class EncoderBlock(nn.Module):
    def __init__(self, f_in, f_out):
        super(EncoderBlock, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(f_in, f_out, (3, 3), padding=(1, 1)),
            nn.ReLU(),
            # nn.Dropout2d(drop_p),
            nn.Conv2d(f_out, f_out, (3, 3), padding=(1, 1)),
            nn.ReLU(),
            # nn.Dropout2d(drop_p)
        )

        self.pool = nn.AvgPool2d(2)

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

class DecoderBlock(nn.Module):
    def __init__(self, f_in, f_out):
        super(DecoderBlock, self).__init__()
        self.upconv = nn.Sequential(
            nn.Upsample(scale_factor=2),
            nn.Conv2d(f_in, f_out, (3, 3), padding=(1, 1)),
        )

        self.conv = nn.Sequential(
            nn.Conv2d(f_out * 2, f_out, (3, 3), padding=(1, 1)), nn.ReLU(),
            # nn.Dropout2d(drop_p),
            nn.Conv2d(f_out, f_out, (3, 3), padding=(1, 1)), nn.ReLU(),
            # nn.Dropout2d(drop_p)
        )

    def forward(self, x, e):
        x = self.upconv(x)
        return self.conv(cat((e, x), dim=1))

class Mel2MelCNN(nn.Module):
    def __init__(self):
        super(Mel2MelCNN, self).__init__()

        self.enc1 = EncoderBlock(1, 8)    # [80 1376] -> [40 688]
        self.enc2 = EncoderBlock(8, 16)   # [40 688]  -> [20 344]
        self.enc3 = EncoderBlock(16, 32)  # [20 344]  -> [10 172]
        self.enc4 = EncoderBlock(32, 64)  # [10 172]  -> [5   86]

        self.bneck = nn.Sequential(
            nn.Conv2d(64, 128, (3, 3), padding=(1, 1)), nn.ReLU(),
            # nn.Dropout2d(drop_p),
            nn.Conv2d(128, 128, (3, 3), padding=(1, 1)), nn.ReLU(),
            # nn.Dropout2d(drop_p),
        )

        self.dec1 = DecoderBlock(128, 64)  # [5   86] -> [10  172]
        self.dec2 = DecoderBlock(64, 32)   # [10 172] -> [20  344]
        self.dec3 = DecoderBlock(32, 16)   # [20 344] -> [40  688]
        self.dec4 = DecoderBlock(16, 8)    # [40 688] -> [80 1376]

        self.outconv = nn.Sequential(
            nn.Conv2d(8, 1, (1, 1)), nn.Sigmoid(),
        )

    def forward(self, x):
        x_in = x

        e1, x = self.enc1(x)
        e2, x = self.enc2(x)
        e3, x = self.enc3(x)
        e4, x = self.enc4(x)

        x = self.bneck(x)

        x = self.dec1(x, e4)
        x = self.dec2(x, e3)
        x = self.dec3(x, e2)
        x = self.dec4(x, e1)

        return x_in * self.outconv(x)


import math
import csv
import xlrd
import numpy

def calculate_standard_deviation(arr_values):
    return numpy.std(arr_values)

location = ("1650107370-2--Sample----with-criteria.xlsx")
wb = xlrd.open_workbook(location)
s1 = wb.sheet_by_index(0)
s1.cell_value(0, 0)  # initializing cell from the cell position

print(" No. of rows: ", s1.nrows)
print(" No. of columns: ", s1.ncols)
stock_data = list(s1.get_rows())[2:]
len_stock_data = len(stock_data)
forward_column_map = {"1-Day Return": -1, "10-Day Return": 10, "25-Day Return": 25}
new_column_map_results = {"criteria-90": [None] * 10}
first, last = 0, 0
for key in forward_column_map:
    forward_index = forward_column_map[key]
    if key not in new_column_map_results:
        new_column_map_results[key] = []
    for index in range(0, len_stock_data):
        try:
            stat_value = stock_data[index][4].value
            last_value = (stock_data[index + forward_index][4]).value
            if ((index + forward_index) < 0) or ((index + forward_index) > len_stock_data):
                result = -1
            else:
                if forward_index > 0:
                    result = ((last_value - stat_value) / (stat_value * 1.0)) * 100.0
                else:
                    result = ((stat_value - last_value) / (last_value * 1.0)) * 100.0
            new_column_map_results[key].append(result)
        except:
            # noinspection PyTypeChecker
            new_column_map_results[key].append(-1)

last_new_column_map = {"V-10": 10, "V-90": 90}
for key in last_new_column_map:
    last = last_new_column_map[key]
    if key not in new_column_map_results:
        new_column_map_results[key] = [0] * last
    for index in range(last, len_stock_data):
        last = last + 1
        start_index = last - last_new_column_map[key]
        values = new_column_map_results["1-Day Return"][start_index:last]
        std_dev = calculate_standard_deviation([i for i in values if i != -1])
        new_column_map_results[key].append(std_dev * math.sqrt(365))
hvg = []
for index in range(0, len_stock_data):
    try:
        hvg.append(new_column_map_results["V-10"][index] / new_column_map_results["V-90"][index])
    except:
        hvg.append(-1)

criteria_map = {"criteria-10": 10}

for key in criteria_map:
    last = criteria_map[key]
    if key not in new_column_map_results:
        new_column_map_results[key] = [None] * 90
    for index in range(90, len_stock_data):
        last = index
        start_index = last - criteria_map[key]
        values = hvg[start_index + 1:last + 1]
        max_value = max([i for i in values if i != math.inf])
        max_value_index = [index for index in range(0, len(values)) if max_value == values[index]][-1]
        main_index = (last - 10) + max_value_index + 1
        close_value = stock_data[len_stock_data - 1 if main_index == len_stock_data else main_index][4].value
        divide_value = ((stock_data[len_stock_data - 1 if last == len_stock_data else last][
                             4].value * 1.0) / close_value) / (hvg[index] / max_value)
        new_column_map_results[key].append(divide_value * 100.0)

for index in range(10, len_stock_data):
    last = index
    start_index = last - 10
    values_2 = new_column_map_results["V-10"][start_index + 1:last + 1]
    # noinspection PyTypeChecker
    max_value_2 = max([i for i in values_2 if i != math.inf])
    max_value_index_2 = [index for index in range(0, len(values_2)) if max_value_2 == values_2[index]][-1]
    main_index_2 = (last - 10) + max_value_index_2 + 1
    close_value_2 = stock_data[len_stock_data - 1 if main_index_2 == len_stock_data else main_index_2][4].value
    divide_value_2 = ((stock_data[len_stock_data - 1 if last == len_stock_data else last][
                           4].value * 1.0) / close_value_2) / (new_column_map_results["V-10"][index] / max_value_2)
    new_column_map_results["criteria-90"].append(divide_value_2 * 100.0)

t1, t2, n = 200, 200, 3
check_mapping = {str(n) + "days": n}
c1_check_array, check_one_passed = [], False
c1, c2, cons_check_days_count = False, False, 0

for index in range(0, len_stock_data):
    c1, c2 = False, False
    # noinspection PyTypeChecker
    if new_column_map_results["criteria-90"][index] is not None and float(
            new_column_map_results["criteria-90"][index]) > t1:
        c1 = True
    elif new_column_map_results["criteria-10"][index] is not None and float(
            new_column_map_results["criteria-10"][index]) > t2:
        c2 = True
    if c1 or c2:
        cons_check_days_count += 1
        c1_check_array.append(index)
    else:
        if check_one_passed:
            break
        cons_check_days_count = 0
        c1_check_array = []
    if cons_check_days_count >= n:
        check_one_passed = True

if not check_one_passed:
    print("Check one failed")
    exit()
print(new_column_map_results)


# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
import numpy as np
import pytest
from matplotlib import pyplot
from PySDM_examples.Arabas_et_al_2015 import Settings, SpinUp
from PySDM_examples.Szumowski_et_al_1998 import Simulation

from PySDM import Formulae
from PySDM.backends import CPU
from PySDM.physics import si
from PySDM.products import SuperDropletCountPerGridbox

from .dummy_storage import DummyStorage

@pytest.mark.parametrize(
    "rtol",
    (
        pytest.param(None, marks=pytest.mark.xfail(strict=True)),
        pytest.param(1e-0, marks=pytest.mark.xfail(strict=True)),
        pytest.param(1e-1, marks=pytest.mark.xfail(strict=True)),
        pytest.param(1e-2),
    ),
)
def test_adaptive_displacement(rtol, plot=False):
    # Arrange
    settings = Settings(formulae=Formulae(seed=666))
    settings.dt = 5 * si.s
    settings.grid = (10, 10)
    settings.n_sd_per_gridbox = 10
    settings.rhod_w_max = 10 * si.m / si.s * si.kg / si.m**3

    settings.simulation_time = 1000 * si.s
    settings.spin_up_time = settings.simulation_time
    settings.output_interval = settings.simulation_time
    if rtol is not None:
        settings.displacement_adaptive = True
        settings.displacement_rtol = rtol
    else:
        settings.displacement_adaptive = False
    settings.processes["condensation"] = False

    storage = DummyStorage()
    simulation = Simulation(settings, storage, SpinUp=SpinUp, backend_class=CPU)
    simulation.reinit(products=[SuperDropletCountPerGridbox()])

    # Act
    simulation.run()
    sd_count = simulation.products["super droplet count per gridbox"].get()

    # Plot
    pyplot.imshow(
        sd_count.T, origin="lower", extent=(0, settings.grid[0], 0, settings.grid[1])
    )
    cbar = pyplot.colorbar()
    cbar.set_label("#SD / cell")
    pyplot.clim(0, 2 * settings.n_sd_per_gridbox)
    pyplot.title(
        f"adaptive: {settings.displacement_adaptive} {f'(rtol={rtol})' if rtol else ''}"
    )
    pyplot.xlabel("x/dx")
    pyplot.ylabel("z/dz")
    pyplot.xticks(np.arange(settings.grid[0] + 1))
    pyplot.yticks(np.arange(settings.grid[1] + 1))
    if plot:
        pyplot.show()

    # Assert
    if rtol is not None:
        assert 1 < simulation.particulator.dynamics["Displacement"]._n_substeps < 50
    assert np.count_nonzero(sd_count) == np.product(settings.grid)
    assert np.std(sd_count) < settings.n_sd_per_gridbox / 2.5
    assert np.max(sd_count) < 2.5 * settings.n_sd_per_gridbox


from __future__ import print_function
import cv2
import numpy
import copy
import sys

filename = sys.argv[1]
print("Load %s" % filename)

def search(filename, threshold_limits = (210, 255)):

    while True:
        img = cv2.imread(filename)
        original = copy.copy(img)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        '''
        img2 = cv2.imread('without.jpg')
        img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

        img = img2
        '''

        def erode(img):
            x = 8
            k0 = numpy.ones((x, x), numpy.uint8)
            return cv2.erode(img, k0, iterations=1)

        def dilate(img):
            x = 7
            k0 = numpy.ones((x, x), numpy.uint8)
            return cv2.dilate(img, k0, iterations=1)

        def threshold(img, limits=threshold_limits):
            #_, thresh = cv2.threshold(img, 235, 255, cv2.THRESH_TOZERO)
            _, thresh = cv2.threshold(img, limits[0], limits[1], cv2.THRESH_TOZERO)
            return thresh

        IMAGE_SIZE = (1024, 768)
        IMAGE_SUBSTRACT_WIDTH = 200

        img = img[0:IMAGE_SIZE[1] / 2, IMAGE_SUBSTRACT_WIDTH / 2:IMAGE_SIZE[0] - IMAGE_SUBSTRACT_WIDTH / 2]

        img = threshold(img)

        img = erode(img)

        #img = cv2.GaussianBlur(img,(5,5),0)
        img = dilate(img)

        cv2.imwrite('debug.jpg', img); #sys.exit()

        def drawInfo(out, x, y, text, color=(255, 255, 0)):

            x += IMAGE_SUBSTRACT_WIDTH / 2

            font = cv2.FONT_HERSHEY_COMPLEX

            # Write on image
            cv2.putText(out, str(text), (x, y - 5), font, 2, color)
            #img = cv2.line(img,(0,0),(511,511),(255,0,0),5)
            #cv2.drawContours(out,[cnt],0,color,-1)
            cv2.rectangle(out, (x, y),(x + w, y + h), (0, 0, 255), 1)
            coordinates = x + w / 2, y + h / 2
            # Draw target
            cv2.line(out, (x + w / 2, y), (x + w / 2, y + h), (0, 0, 255), 1)
            cv2.line(out,(x, y + h / 2),(x + w, y + h / 2), (0, 0, 255), 1)

        #mode = cv2.RETR_EXTERNAL
        mode = cv2.RETR_LIST
        #mode = cv2.RETR_CCOMP
        #mode = cv2.RETR_TREE

        min_carea = 700
        max_carea = 2100
        egg_count = 0

        #wsize=(30, 62)
        wsize=(30, 82)
        hsize=(20, 62)

        #method = cv2.CHAIN_APPROX_NONE
        method = cv2.CHAIN_APPROX_SIMPLE
        ##method = cv2.CHAIN_APPROX_TC89_L1
        #method = cv2.CHAIN_APPROX_TC89_KCOS
        contours, h = cv2.findContours(img, mode, method)
        if len(contours) > 15: #04
            mint, maxt = threshold_limits
            mint += 10
            if mint >= 250:
                break

            threshold_limits = (mint, maxt)
            print('Contour not good (%i), new threshold limits %s !' % (len(contours), str(threshold_limits)))
            continue

        for cnt in contours:
            approx = cv2.approxPolyDP(cnt, 0.01 * cv2.arcLength(cnt, True), True)
            #print(len(approx))
            #if 11 < len(approx) < 15:
            if not (5 < len(approx) < 21):
                continue

            ellipse = cv2.fitEllipse(cnt)
            h = ellipse[1][0]
            w = ellipse[1][1]

            adist = cv2.arcLength(cnt, True)
            carea = cv2.contourArea(cnt)

            if not (hsize[0] < h < hsize[1] and wsize[0] < w < wsize[1]):
                #print('Size (w:%i, h:%i) not in range (w:%i-%i, h:%ix%i)' % (w, h, wsize[0], wsize[1], hsize[0], hsize[1]))
                continue

            if not (min_carea < carea < max_carea):
                print('Contour area (%i) not in range (min:%i) !' % (carea, min_carea))
                continue

            print('found: carea:%i, size:%ix%i, adist:%i, approx:%i' % (carea, w, h, adist, len(approx)))

            #marea = cv2.minEnclosingCircle(cnt)
            #print('Min area:', marea)

            egg_count += 1
            x,y,w,h = cv2.boundingRect(cnt)
            drawInfo(original, x, y, egg_count)

            #drawInfo(img, x, y, egg_count)
            #else:
            #    print('h:', h, hsize, ', w:', w, wsize)
            #    print('len approx: %i, height: %i, width: %i, aDist:%i, contour area:%i' % (len(approx), h, w, adist, carea))
            #    print('--')

            return egg_count, original

        break

    return 0, original

egg_count, img = search(filename)
cv2.imwrite('out.jpg', img)

#out = original
#out = cv2.absdiff(img1, img2)

#cv2.imshow('desc', img1)
#cv2.imwrite('img1.jpg', img1)
#cv2.imwrite('img2.jpg', img2)
#cv2.imwrite('out.jpg', out)

#cv2.imwrite('out.jpg', original)

print('Found %i egg(s)' % egg_count)


import time
import serial
import struct
#import calendar
import datetime

sn = 0            #Адрес счётчика, если счётчик один то можно оставить 0
koof = 1.0848     #Коофициент потерь для НВК 1.0848

def COM_Init():
  global ser
  # configure the serial connections (the parameters differs on the device you are connecting to)
  ser = serial.Serial()
  ser.port = 'COM10'
  ser.baudrate = 9600
  ser.bytesize = serial.EIGHTBITS #number of bits per bytes
  ser.parity = serial.PARITY_NONE #set parity check: no parity
  ser.stopbits = serial.STOPBITS_ONE #number of stop bits
  ser.timeout = 0.1            #non-block read
  ser.xon  = False
  ser.xoff = False     #disable software flow control
  ser.open()
  ser.write(pack_data(b'\x01\x01\x01\x01\x01\x01\x01\x01'))
  time.sleep(0.5)
  while ser.inWaiting() > 0:
    out = ser.read(ser.inWaiting())

def crc16(data):
    crc = 0xFFFF
    l = len(data)
    i = 0
    while i < l:
        j = 0
        crc = crc ^ data[i]
        while j < 8:
            if (crc & 0x1):
                mask = 0xA001
            else:
                mask = 0x00
            crc = ((crc >> 1) & 0x7FFF) ^ mask
            j += 1
        i += 1
    if crc < 0:
        crc -= 256

    result = data + chr(crc % 256).encode('latin-1') + chr(crc // 256).encode('latin-1')
    return result

def pack_data(data):
  chunk = struct.pack('B', int(sn))
  chunk += data
  chunk = crc16(chunk)
  return chunk

COM_Init()

ser.write(pack_data(b'\x08\x13'))
time.sleep(0.5)
while ser.inWaiting() > 0:
  out = ser.read(ser.inWaiting())
#print(out)
adr = int('{:02x}'.format(out[1])+'{:02x}'.format(out[2]),16)
tt = '{:02x}'.format(out[4])+':'+'{:02x}'.format(out[5])
dd = datetime.date(int('20'+'{:02x}'.format(out[8])),int('{:02x}'.format(out[7])),int('{:02x}'.format(out[6])))
dl = str(out[9])

if '{:02x}'.format(out[5])=='30':
  tmp_h = int('{:02x}'.format(out[4]))*32
else:
  tmp_h = int('{:02x}'.format(out[4]))*32-16

if dd.month == 1:
  tmp_year = dd.year - 1
  tmp_month = 12
else:
  tmp_year = dd.year
  tmp_month = dd.month - 1

dd_first = datetime.date(tmp_year,tmp_month,1) # Первый день предыдущего месяца
dd_last = datetime.date(dd.year,dd.month,1)    # Первый день текущего месяца
mday =  dd_last - dd_first                     # Кол-во дней в месяце
hours = mday.days*24
tmp_mday = dd - dd_first
adr_first = adr - (tmp_mday.days*768+tmp_h)
if adr_first < 0:
  adr_first = 65536 + adr_first

print ('ADR: '+hex(adr_first))

print('адрес: '+str(adr)+' время: '+tt+' дата: '+str(dd.year)+' длительность: '+dl)

f = open(str(dd_first.year)+'-'+str(dd_first.month)+'.csv','w')
sutki = 1

for i in range(hours):   #hours
  pack_d = b'\x06\x03'
  adr_hex = '{:04x}'.format(adr_first)
  #print(adr_hex[0:2])
  #print(adr_hex[2:4])
  pack_d += struct.pack('B', int(adr_hex[0:2],16))
  pack_d += struct.pack('B', int(adr_hex[2:4],16))
  pack_d += struct.pack('B', int('1e',16))

  ser.write(pack_data(pack_d))
  time.sleep(0.5)
  while ser.inWaiting() > 0:
    out = ser.read(ser.inWaiting())

  pw = ((int('{:02x}'.format(out[9])+'{:02x}'.format(out[8]),16)+ int('{:02x}'.format(out[24])+'{:02x}'.format(out[23]),16))/2000)*koof

  f.write(str(pw).replace('.',',')+';')
  if sutki==24:
    sutki = 0
    f.write('\n')

  sutki += 1
  #print('{:02x}'.format(out[2])+':'+'{:02x}'.format(out[3])+'\t'+'20'+'{:02x}'.format(out[6])+'-'+'{:02x}'.format(out[5])+'-'+'{:02x}'.format(out[4])+'\t'+str(pw))

  print('{:02x}'.format(out[17])+':'+'{:02x}'.format(out[18])+'\t'+'20'+'{:02x}'.format(out[21])+'-'+'{:02x}'.format(out[20])+'-'+'{:02x}'.format(out[19])+'\t'+str(pw))
  adr_first = adr_first + 32
  if adr_first > 65535:
    adr_first = adr_first - 65536

f.close()
ser.close()
exit()


import datetime

class fruit_tree:                                                           # create class
    __YEAR, __HEIGHT, __BRANCHES, __DATE_START = 1, 0.1, 3, 2000            # start value

    __slots__ = ["__year", "__height", "__branches", "__date_now", "__date_start"]    #limitation instaces

    def __init__(self):
        self.__year = fruit_tree.__YEAR
        self.__height = fruit_tree.__HEIGHT
        self.__branches = fruit_tree.__BRANCHES
        self.__date_start = fruit_tree.__DATE_START
        self.__date_now = datetime.date.today().year

    def info(self):                                                                   # return values instaces
        return self.__year, self.__height, self.__branches

    def grow(self, year, height, branch):                                             # method grow
        self.__year = self.__year + year
        self.__height = self.__height + height
        self.__branches = self.__branches + branch

    def time_live(self):                                                               #determine the number of iterations, iterate and passing magnification values
        while self.__date_start <= self.__date_now:
            self.__date_start = self.__date_start + 1
            x.grow(1, 1.5, 3)
            print(f"Через год: {x.info()}")

x = fruit_tree()                                                                       #redefining the class into a variable for subsequent calls to methods of this class
print(f"В первый год: {x.info()}")                                                     #print start values
x.time_live()


from torch.autograd.grad_mode import no_grad
from .base import base
import argparse
import torch
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
import sys
import numpy as np

from derivs import *

# generates point in the interior and on the boundary of a unit ball in d dimensions.
# It also puts all of them together

def generate_points(dimension, npoints_interior, npoints_boundary):

    #(Muller method)
    # It returns an array

    x_array = []
    ## Points in the interior
    for i in range(npoints_interior):
        u = np.random.normal(0,1,dimension)
        r = np.random.rand()**(1.0/dimension)
        w = r*u/np.sqrt(np.sum(u**2))
        x_array.append(w)

    ## Points on the boundary

    for i in range(npoints_boundary):
        u = np.random.normal(0,1,dimension)
        w = u/np.sqrt(np.sum(u**2))
        x_array.append(w)

    return x_array

# defines the network for the PDE problem
class network_PDE(torch.nn.Module):
    def __init__(self):
        # call constructor from superclass
        super().__init__()

        self.depth = 3

        self.width = 200

        # here define the layers
        # first layer
        self.linear_input = torch.nn.Linear(2, self.width)
        # middle layers
        self.linear_layers = torch.nn.ModuleList([torch.nn.Linear(self.width, self.width) for i in range(self.depth)])
        # final layer
        self.linear_output = torch.nn.Linear(self.width, 1)

        # here we initialize the network
        torch.nn.init.xavier_normal_(self.linear_input.weight, gain=torch.sqrt(torch.tensor(1.)))

        for i in range(self.depth):
            torch.nn.init.xavier_normal_(self.linear_layers[i].weight, gain=torch.sqrt(torch.tensor(1.)))

        torch.nn.init.xavier_normal_(self.linear_output.weight, gain=torch.sqrt(torch.tensor(1.)))

    def forward(self, x):
        # This is a simple residual NN.
        # The difference with a fully connected is the x+ ... in each intermediate layer
        # notice that by design all the intermediate layers have the same width

        x = torch.sigmoid(self.linear_input(x))

        for layer in (self.linear_layers):
            x = x + torch.sigmoid(layer(x))

        x = self.linear_output(x)

        return x

class PDE_PoissonD(base):
    def __init__(self):
        super().__init__()

    def parser(self):
        parser = argparse.ArgumentParser(
            description="Using NNs to solve Poisson equations in arbitrary dimensions", add_help=False
        )
        parser.add_argument(
            "--iterations",
            default=1000,
            type=int,
            metavar="iterations_per_epoch",
            help="Number of iterations for each epoch.",
        )

        parser.add_argument(
            "--progress",
            default="true",
            type=str,
            metavar="progress",
            help="Show the progress bar, default true.",
        )

        parser.add_argument(
            "--feature",
            default=0,
            type=int,
            metavar="feature",
            help="The feature number to add to the loss. Defaults to 0 (no feature).",
        )

        return parser

    # Here the different features to add to the loss

    def feature_1(self):
        # just \theta^2 (L2)
        barrier_smooth = torch.tensor(0., device=self.device)
        for param in self.net.parameters():
            barrier_smooth += 2e-4*torch.sum(torch.pow(param,2))
        return barrier_smooth

    def feature_42(self):
        # This is an "empty" feature.
        barrier_smooth = torch.tensor(0., device=self.device)
        return barrier_smooth

    def initialize(self, args, files):
        super().initialize(args, files)

        # If "True" generates new points in the domain at each epoch (adds noise)
        self.update_points = False

        self.dtype = torch.float32

        self.iterations_per_epoch = args.iterations
        self.criterion = torch.nn.MSELoss()

        self.epochs = args.epochs
        self.progress = args.progress

        self.criterion_test = torch.nn.L1Loss()

        self.stats["loss train"] = list()
        self.stats["solution"] = list()

        self.stats["losses check"] = list()
        self.stats["parameters"] = list()
        self.stats["inputs"] = list()

        ## The equation is Delta u +coeff*u^2 = inhom
        ## we impose also Dirichlet boundary conditions

        self.dimension = 2
        self.non_linearity = 1.0
        self.analytic_sol = lambda x: torch.sin(20*torch.norm(x,dim = 1)**2)**2
        self.inhom_term = lambda x: 0.125*( 3-4*(1+6400*torch.norm(x,dim = 1)**2)*torch.cos(40*(torch.norm(x,dim = 1)**2))+torch.cos(80*(torch.norm(x,dim = 1)**2))-640*torch.sin(40*(torch.norm(x,dim = 1)**2)))
        self.Dirichlet_value = 0.833469

        # network
        self.net = network_PDE().to(self.device)

        self.npoints_interior = int(1e4)
        self.npoints_boundary = int(1e3)

        # The weight factor of the boundary conditions in the loss
        self.bc_weight = 100000.0

        #If this is 1 it saves the full model at each epoch, otherwise only each self.saving.epochs epochs
        # The features, loss, and plots are always saved after each epoch
        self.saving_epochs = 1000

        ## Remember to add the feature also here!
        if args.feature == 1:
            self.feature = self.feature_1

        elif args.feature == 42:
            self.feature = self.feature_42

        else:
            if (self.progress == 'true'): print("Feature not defined, or no feature specified. Working without features")
            self.feature = lambda : torch.tensor(0., device=self.device)

        #Points
        x_array = generate_points(self.dimension, self.npoints_interior, self.npoints_boundary)

        self.x = torch.tensor(x_array, device=self.device, requires_grad=True, dtype = self.dtype)

        ### Check the analytic solution
        points = torch.tensor(x_array, device='cpu', requires_grad=True, dtype = self.dtype)
        f1 = self.analytic_sol(points)
        f1p = diff_single(f1, points, device = 'cpu')
        f1pp = diff_many(f1p, points, device = 'cpu')

        lap = torch.zeros_like(f1pp[0][0], device='cpu')
        for i in range(self.dimension): lap-= f1pp[i][i]
        diff_eq1 =lap + self.non_linearity*(f1**2) - self.inhom_term(points)

        if (self.progress == 'true'): print("L1 error of the analytic solution (should be small): ", torch.norm(diff_eq1).item())
        if (self.update_points == False): self.stats["inputs"].append(self.x.tolist())

    def train(self, epoch, optimizer):
        super().train(epoch, optimizer)

        for i in range(self.iterations_per_epoch):

            y = self.net(self.x)
            loss = self.get_loss(y)

            # kill if there are nans
            if (torch.isnan(loss).any().item() == True): sys.exit()

            #bp()
            optimizer.zero_grad()
            loss.backward()

            if  self.progress == "true":
                if i == 0: print("Starting loss: ", loss.item())

            def closure():
                return loss

            optimizer.step(closure)
            if  self.progress == "true":
                self.progress_bar.next(
                    i, self.iterations_per_epoch, "Loss: %.3f" % (loss.item())
                )

        if (self.update_points == True):
            x_array = generate_points(self.dimension, self.npoints_interior, self.npoints_boundary)
            self.x = torch.tensor(x_array, device=self.device, requires_grad=True, dtype = self.dtype)

        self.stats["loss train"].append(loss.item())
        self.stats["solution"].append(y.tolist())

        if (epoch%self.saving_epochs == 0) or (epoch == self.start_epoch+self.epochs-1):
            new_list = list(self.net.parameters())
            list_pars = []
            for item in new_list:
                list_pars.append(item.tolist())

            self.stats["parameters"].append(list_pars)

    def test(self, epoch):
        super().test(epoch)

        y = self.net(self.x)
        losses = self.get_loss_test(y)
        if  self.progress == "true": print(losses)

        #if (epoch%self.saving_epochs == 0):
        state = {
            "net": self.net.state_dict(),
            "epoch": epoch,
        }

        torch.save(state, self.files["checkpoint"])

        self.stats["losses check"].append(losses)

         # saving plot loss
        plt.figure(1)

        # set the title
        plt.suptitle("Inhomogeneous Poisson")
        plt.suptitle(" ".join(sys.argv[1:]))

        ii = 0
        for loss in list(zip(*self.stats["losses check"])):

            if (ii == 0): lab ="log10(L1 error eq 1)"
            if (ii == 1): lab ="log10(L1 error bc 1)"
            if (ii == 2): lab ="log10(max abs error eq 1)"

            plt.plot(
                self.stats["epoch"],
                np.log10(loss),
                "--.",
                #color="tab:red",
                label = lab,
            )
            plt.grid()
            ii += 1

        plt.setp(plt.gca().spines.values(), color="#bfbfbf")
        lgd = plt.gca().legend()
        lgd.set_frame_on(True)

        # save the image
        plt.savefig(self.files["plot_check_losses"])
        plt.clf()
        plt.close()

    def save(self, epoch):
        super().save(epoch)

        # saving plot loss
        plt.figure(1)

        # set the title
        plt.suptitle("Solving higher dimensional PDE")
        plt.suptitle(" ".join(sys.argv[1:]))

        plt.plot(
            self.stats["epoch"],
            np.log10(self.stats["loss train"]),
            "--.",
            color="tab:red",
            label="log10(loss)",
        )
        plt.grid()

        plt.setp(plt.gca().spines.values(), color="#bfbfbf")
        lgd = plt.gca().legend()
        lgd.set_frame_on(True)

        # save the image
        plt.savefig(self.files["plot"])
        plt.clf()
        plt.close()

        #save the first function

        starting_boundary =self.npoints_interior

        #plot a radial slice
        ax = plt.axes()

        points_plot = []
        elems = np.linspace(-1, 1, 1000)
        xs = np.array(elems)

        # create the radial slice
        for elem in elems:
            temp = np.zeros(self.dimension-1)
            temp = np.insert(temp,0,elem)
            points_plot.append(temp)

        xs2 = torch.tensor(points_plot, device = self.device, dtype = self.dtype, requires_grad = True)

        with torch.no_grad():
            pred = np.array(self.net(xs2).tolist())
            analytic = np.array(self.analytic_sol(xs2).tolist())

        ax.plot(xs,
            pred,
            ".",
            color="tab:orange",
            label="NN")
        ax.plot(xs,
            analytic,
            ".",
            color="tab:blue",
            label="analytic")
        #bp()

        plt.grid()

        plt.setp(plt.gca().spines.values(), color="#bfbfbf")
        lgd = plt.gca().legend()
        lgd.set_frame_on(True)

        # save the image
        plt.savefig(self.files["plot_function"])
        plt.clf()
        plt.close()

        if  self.progress == "true": print("Image saved as: %s" % self.files["plot"])

    def get_loss(self, y):
        f1 = y.transpose(0,1)[0]

        f1p = diff_single(f1, self.x, self.device)
        f1pp = diff_many(f1p, self.x, self.device)

        starting_boundary =self.npoints_interior

        lap = torch.zeros_like(f1pp[0][0], device=self.device)
        for i in range(self.dimension): lap-= f1pp[i][i]
        diff_eq1 =lap + self.non_linearity*(f1**2) - self.inhom_term(self.x)

        eqn1 =  self.criterion(diff_eq1, torch.zeros_like(diff_eq1))+self.feature()

        if self.bc_weight == 0: return eqn1
        else:
            bc1 =  self.criterion(f1[starting_boundary:], torch.full_like(f1[starting_boundary:],self.Dirichlet_value))
            return eqn1+self.bc_weight*bc1

    def get_loss_test(self, y):

        f1 = y.transpose(0,1)[0]

        f1p = diff_single(f1, self.x, self.device)
        f1pp = diff_many(f1p, self.x, self.device)

        starting_boundary =self.npoints_interior

        bc1 =  self.criterion_test(f1[starting_boundary:], torch.full_like(f1[starting_boundary:],self.Dirichlet_value))

        lap = torch.zeros_like(f1pp[0][0], device=self.device)
        for i in range(self.dimension): lap-= f1pp[i][i]
        diff_eq1 =lap + self.non_linearity*(f1**2) - self.inhom_term(self.x)

        eqn1 =  self.criterion_test(diff_eq1, torch.zeros_like(diff_eq1))

        max1 = (torch.max(torch.abs(diff_eq1)))

        return (eqn1.item(), bc1.item(), max1.item())