import pygame
from pygame.locals import Color, Rect
import random
from graphics import *
import pieces
import piece

random.seed()

class GameFunctions:
	def new_piece(self):
		"""Creates a new piece and places it at the top of the well."""
		piecesCount = len(pieces.PieceTypes.AllPieceTypes)
		next_piece_type = random.randint(0, piecesCount - 1)
		#next_piece_type = 4
		well_cols, _ = self.well_dimensions()
		left_pos = int((well_cols - 4) / 2)
		top_pos = -4
		p = piece.Piece(self, next_piece_type, (left_pos, top_pos), self.well_dimensions())
		# The piece is above the well.
		# Bring the piece down until its bottom row is inside the well.
		if (self.row_is_empty(p.matrix, 3)):
			p.move_down()
			if (self.row_is_empty(p.matrix, 2)):
				p.move_down()
				if (self.row_is_empty(p.matrix, 1)):
					p.move_down()
					if (self.row_is_empty(p.matrix, 0)):
						raise Exception("Piece matrix seems to be empty.")
		return p

	def row_is_empty(self, matrix, rownum):
		"""Determins whether a given row in a piece matrix contains only 0s."""
		for colnum in range(4):
			if (matrix[rownum][colnum] > 0):
				return False
		return True

	def col_is_empty(self, matrix, colnum):
		"""Determines whether a given columnin a piece matrix contains only 0s."""
		for rownum in range(4):
			if (matrix[rownum][colnum] > 0):
				return False
		return True

	def cell_size_in_pixels(self):
		"""Returns the number of pixels in a cell as (width, height)."""
		return (16, 16)

	def well_dimensions(self):
		"""Returns the cellular dimensions of the well."""
		return (10, 20)

	def well_size(self):
		welldw, welldh = self.well_dimensions()
		return (welldw * 16, welldh * 16)

	def well_size_in_pixels(self):
		"""Returns the pixel size of the well as (width, height)."""
		cell_size_x, cell_size_y = self.cell_size_in_pixels()
		well_dim_x, well_dim_y = self.well_dimensions()
		return (cell_size_x * well_dim_x, cell_size_y * well_dim_y)

	def well_pixel_coords(self, screen_size):
		"""Returns the x,y pixel coordinates of the upper-left corner of the well."""
		screen_width, screen_height = screen_size
		well_size_x, well_size_y = self.well_size_in_pixels()
		x = int((screen_width - well_size_x) / 2)
		y = int((screen_height - well_size_y) / 2)
		return (x, y)

	def draw_well_grid(self, screen, screen_size):
		"""This draws a happy little dot in the well at the center of each cell."""
		wellx, welly = self.well_pixel_coords(screen_size)
		cell_width, cell_height = self.cell_size_in_pixels()
		cols, rows = self.well_dimensions()
		for col in range(cols):
			for row in range(rows):
				dot_pos = (wellx + col * cell_width + int(cell_width / 2), welly + row * cell_height + int(cell_height / 2))
				pygame.draw.circle(screen, Color(128, 128, 128, 255), dot_pos, 3)

	def draw_well_matrix(self, screen, screen_size, well_matrix):
		"""This draws the existing set pieces in the well.  It draws a set brick for each cell
		in the well matrix which contains a 1."""
		wellx, welly = self.well_pixel_coords(screen_size)
		cell_width, cell_height = self.cell_size_in_pixels()
		cols, rows = self.well_dimensions()
		for row in range(rows):
			for col in range(cols):
				cell = well_matrix[row][col]
				if (cell >= 1):
					brick_pos = (wellx + col * cell_width, welly + row * cell_height)
					screen.blit(img_well_brick, brick_pos)

	def draw_piece_matrix(self, screen, screen_size, piece):
		"""This draws the current piece on the screen.  It draws a brick for each cell
		in the piece matrix which contains a 1."""
		posx, posy = piece.position
		for row in range(4):
			for col in range(4):
				cellx, celly = self.cell_position_to_pixels(screen_size, (posx, posy))
				if (GameData.SHOW_MATRIX_SHADOW):
					pygame.draw.line(screen, Color(255, 0, 0, 255), (col * 16 + cellx, row * 16 + celly), ((col+1) * 16 + cellx, (row + 1) * 16 + celly))
				if (posy + row >= 0):
					if (piece.matrix[row][col] == 1):
						screen.blit(img_piece_brick, (col * 16 + cellx, row * 16 + celly))

	def pause_for(self, milliseconds):
		"""Pauses execution for the given time period.  Used for animation sequences and banners."""
		start_ticks = pygame.time.get_ticks()
		while True:
			pygame.time.Clock().tick(1000/30)
			if (pygame.time.get_ticks() > start_ticks + milliseconds):
				break

	def draw_clearings(self, screen, screen_size, well_matrix, crunch_sound):
		"""Draws crunched bricks in each line to be cleared and plays a crunching sound once for each row."""
		wellx, welly = self.well_pixel_coords(screen_size)
		cell_width, cell_height = self.cell_size_in_pixels()
		lines = self.get_lines_to_clear(well_matrix)
		line_count = len(lines)
		ww, wh = self.well_dimensions()
		for line_num in range(line_count):
			for col in range(ww):
				brick_pos = (wellx + col * cell_width, welly + lines[line_num] * cell_height)
				screen.blit(img_crunch_brick, brick_pos)
			pygame.display.update()
			crunch_sound.play()
			self.pause_for(500)

	def draw_well(self, screen, screen_size):
		border_size = 5
		borderx, bordery = self.well_pixel_coords(screen_size)
		borderx = borderx - border_size
		bordery = bordery - border_size
		borderw, borderh = self.well_size_in_pixels()
		borderw = borderw + (2 * border_size)
		borderh = borderh + (2 * border_size)
		pygame.draw.rect(screen, Color(255, 255, 255, 255), Rect(borderx, bordery, borderw, borderh))

		wellx, welly = self.well_pixel_coords(screen_size)
		wellw, wellh = self.well_size_in_pixels()
		pygame.draw.rect(screen, Color(0, 0, 0, 255), Rect(wellx, welly, wellw, wellh))

	def draw_scoreboard(self, screen, screen_size, score):
		"""Draws a box with the score in it onto the screen."""
		screenw, screenh = screen_size
		wellx, welly = self.well_pixel_coords(screen_size)
		wellw, wellh = self.well_size()
		border_size = 5
		padding = 20
		height = 50

		left = wellx + wellw + padding
		right = screenw - padding
		top = welly
		#bottom = top + height
		width = right - left + 1

		pygame.draw.rect(screen, Color(255, 255, 255, 255), Rect(left - border_size, top - border_size, width + border_size * 2, height + border_size * 2))
		pygame.draw.rect(screen, Color(0, 0, 0, 255), Rect(left, top, width, height))

		text = str(score)
		font = pygame.font.SysFont("Lucida Console", 32, 1, 0)
		text_surface = font.render(text, False, Color(255, 255, 255, 255))
		screen.blit(text_surface, (left + (width - text_surface.get_width()) / 2, top + (height - text_surface.get_height()) / 2))

	def compact_well(self, well_matrix):
		"""Bring the contents of the wall down into any recently cleared lines."""
		lines = self.get_lines_to_clear(well_matrix)
		ww, wh = self.well_dimensions()

		# For each cleared line...
		for clear_line_num in lines:
			# Copy each line of the matrix above this one, overwriting the line below it.
			# Don't do it for line 0 because there is no line above it.
			row_num = clear_line_num
			while (row_num >= 1):
				line_above = well_matrix[row_num - 1]

				for col_num in range(ww):
					well_matrix[row_num][col_num] = line_above[col_num]

				row_num = row_num - 1

		# For line 0, we just clear it explicitly.
		for col_num in range(ww):
			well_matrix[0][col_num] = 0

	def create_empty_matrix(self, rows, cols):
		"""Creates a matrix representing a new empty well."""
		result = []
		for row in range(rows):
			this_row = []
			result.append(this_row)
			for col in range(cols):
				this_row.append(0)
		return result

	def new_well(self):
		"""Creates a matrix representing a new empty well."""
		cols, rows = self.well_dimensions()
		return self.create_empty_matrix(rows, cols)

	def set_piece(self, piece, well_matrix):
		pass

	def copy_matrix(self, source):
		"""Create a duplicate of a matrix.  This is used to create a new piece from the pieces templates."""
		result = []
		for row_num in range(len(source)):
			old_row = source[row_num]
			new_row = []
			for col_num in range(len(old_row)):
				old_cell = old_row[col_num]
				new_row.append(old_cell)
			result.append(new_row)
		return result

	def impose_matrix(self, source, target, source_pos):
		"""writes a matrix onto another matrix at the given cell position.  The result is a matrix addition function.
		So if the same cell in both matrixes contain 1, the output cell will contain 2 (1+1). """

		x_offset, y_offset = source_pos
		source_h = len(source)
		source_w = len(source[0])
		target_h = len(target)
		target_w = len(target[0])

		# Copy the target matrix to a temp matrix.
		new_blank_well = self.copy_matrix(target)

		# Impose the piece to the temp matrix
		for source_row_num in range(source_h):
			for source_col_num in range(source_w):
				x, y = (x_offset + source_col_num, y_offset + source_row_num)
				if (x >= 0) and (x <= target_w - 1) and (y >= 0) and (y <= target_h - 1):
					source_cell_value = source[source_row_num][source_col_num]
					target_cell_value = target[y][x]
					new_cell_value = source_cell_value + target_cell_value
					new_blank_well[y][x] = new_cell_value

		# Copy the temp matrix back over the target matrix to send it back out.
		for row_num in range(target_h):
			for col_num in range(target_w):
				target[row_num][col_num] = new_blank_well[row_num][col_num]

	def has_collisions(self, matrix):
		"""Determines if there were any collisions in the result of two merged matrices."""
		matrix_height = len(matrix)
		matrix_width = len(matrix[0])
		for row in range(matrix_height):
			for col in range(matrix_width):
				if (matrix[row][col] > 1):
					return True
		return False

	def line_is_full(self, row):
		"""Indicates if the specified row is completely full of bricks and should be cleared."""
		w = len(row)
		for col in range(w):
			if (row[col] == 0):
				return False
		return True

	def has_lines_to_clear(self, matrix):
		"""Indicates whether any lines are full and need to be cleared."""
		return len(self.get_lines_to_clear(matrix)) > 0

	def get_lines_to_clear(self, matrix):
		"""Returns a list of lines which are full and need to be cleared."""
		result = []
		h = len(matrix)
		for row in range(h):
			if self.line_is_full(matrix[row]):
				result.append(row)
		return result

	def has_hit_bottom(self, piece):
		"""Determines whether the piece has encountered the bottom of the well."""
		x, y = piece.position
		matrix = piece.matrix
		ww, wh = self.well_dimensions()
		wx, wy = (ww - 1, wh - 1)
		result = False
		for rownum in range(4):
			ry = y + rownum
			if (ry >= wy):
				if (not self.row_is_empty(matrix, rownum)):
					result = True
		return result

	def cell_position_to_pixels(self, screen_size, cell_position_colrow):
		"""Convert a cell position within the well to screen x,y coordinates."""
		cell_col, cell_row = cell_position_colrow
		well_pixel_x, well_pixel_y = self.well_pixel_coords(screen_size)
		cell_size_width, cell_size_height = self.cell_size_in_pixels()
		x = well_pixel_x + cell_col * cell_size_width
		y = well_pixel_y + cell_row * cell_size_height
		return (x, y)

	def print_matrix(self, matrix, caption):
		"""Displays the values in the given matrix in the text console.  This is useful for debugging."""
		h = len(matrix)
		w = len(matrix[0])
		print(caption)
		for rownum in range(h):
			print("\t", matrix[rownum])

	def show_game_over(self, screen, screen_size):
		pygame.mixer.music.stop()
		text = "Game Over"
		font = pygame.font.SysFont("Courier New", 32, 1, 0)
		text_surface = font.render(text, False, Color(255, 255, 0, 255))
		surface = pygame.Surface( (text_surface.get_width() + 80, text_surface.get_height() + 80) )

		surface.fill(Color(255, 0, 0, 255))

		w, h = screen_size
		screen.blit(surface, ( ((w - surface.get_width()) / 2), ((h - surface.get_height()) / 2) ) )
		screen.blit(text_surface, ( ((w - text_surface.get_width()) / 2), ((h - text_surface.get_height()) / 2) ) )
		pygame.display.update()
		self.pause_for(5000)

	def piece_will_set(self, piece, well_matrix):
		will_set = False
		(x, y) = piece.position
		hit_bottom = self.has_hit_bottom(piece)
		if (hit_bottom):
			will_set = True
		else:
			# Otherwise, if it is going to collide on the next drop, it sets.
			next_position = (x, y + 1)
			collision_matrix = self.copy_matrix(well_matrix)
			self.impose_matrix(piece.matrix, collision_matrix, next_position)
			will_collide = self.has_collisions(collision_matrix)
			if (will_collide):
				will_set = True
		return will_set


"""
OpenGL Triangle
This is the base for 3D.  It is made up of three Vertexes that
can contain up to four attributes each.
Position, required, a Vector3
Normal, calculated, a Vector3
TexCoord, optional, a Vector2
Color, optional, a Vector3
"""

import math

from .matrix import Matrix
from .vector import Vector3

class Triangle:
    """
    parameters
        list[Vector3]
            positions
        list[Vector3]
            colors
        list[Vector2]
            texcoords
    """

    __slots__ = ["__colors", "__normals", "__positions", "__texcoords"]

    def __init__(self, positions, colors=None, texcoords=None):

        # Position
        if (
            isinstance(positions, list) and len(positions) == 3 and
            all(type(p).__name__ == "Vector" for p in positions)
        ):
            self.__positions = positions
        else:
            raise ValueError("Positions must be a List of Vectors.")

        # Colors
        if colors is not None:
            if (
                isinstance(colors, list) and len(colors) == 3 and
                all(type(c).__name__ == "Vector" for c in colors)
            ):
                self.__colors = colors
            else:
                raise ValueError("Colors must be a List of Vectors.")
        else:
            self.__colors = None

        # TexCoords
        if texcoords is not None:
            if (
                isinstance(texcoords, list) and len(texcoords) == 3 and
                all(type(t).__name__ == "Vector" for t in texcoords)
            ):
                self.__texcoords = texcoords
            else:
                raise ValueError("TexCoords must be a List of Vectors.")
        else:
            self.__texcoords = None

        # Calculate Normals
        """
        So for a triangle p1, p2, p3, if the vector U = p2 - p1
        and the vector V = p3 - p1 then the normal N = U X V and
        can be calculated by:
        Nx = UyVz - UzVy
        Ny = UzVx - UxVz
        Nz = UxVy - UyVx
        """
        position_U = self.__positions[1] - self.__positions[0]
        position_V = self.__positions[2] - self.__positions[0]
        self.__normals = Vector3(
            (position_U.Y * position_V.Z) - (position_U.Z * position_V.Y),
            (position_U.Z * position_V.X) - (position_U.X * position_V.Z),
            (position_U.X * position_V.Y) - (position_U.Y * position_V.X),
        )

    def __repr__(self):
        """
        returns
            __str__
        """
        return self.__str__()

    def __str__(self):
        """
        returns
            string representation of our triangle.
        """
        return str(self.get_vertex_data())

    def __eq__(self, other):
        """
        parameters
            Triangle
        returns
            bool of whether both triangles have the same vertexes.
        """
        if not type(other).__name__ == "Triangle":
            return False
        return all(other.has_position(p) for p in self.__positions)

    def __mul__(self, other):
        """
        parameters
            Matrix
        returns
            Triangle
        """
        # Multiplying against a Matrix.
        if type(other).__name__ == "Matrix":
            return Triangle(
                [
                    Vector3(
                        *(other * p.get_matrix()).get_as_list()
                    ) for p in self.__positions
                ],
                self.__colors,
                self.__texcoords,
            )
        return self

    def get_colors(self):
        """
        returns
            List<Vector3> colors
        """
        return self.__colors

    def get_normals(self):
        """
        returns
            List<Vector3> normals
        """
        return self.__normals

    def get_offset(self, offset):
        """
        parameters
            Vector3
        returns
            Triangle
        """
        if type(offset).__name__ == "Vector":
            return Triangle(
                [p + offset for p in self.__positions],
                self.__colors,
                self.__texcoords
            )
        return self

    def get_positions(self):
        """
        returns
            List<Vector3> positions
        """
        return self.__positions

    def get_rotation(self, yaw=0.0, pitch=0.0, roll=0.0):
        """
        parameters
            float
                yaw, or z
            float
                pitch, or y
            float
                roll, or z
        returns
            Triangle
        """

        if (
            not isinstance(yaw, (float, int)) or
            not isinstance(pitch, (float, int)) or
            not isinstance(roll, (float, int))
        ):
            ValueError("Yaw/Pitch/Roll must be a Float or Int")

        # Convert degrees to radians
        pitch_radians = math.radians(pitch)
        roll_radians = math.radians(roll)
        yaw_radians = math.radians(yaw)

        # Pitch, or Y
        pitch_matrix = Matrix(
            3, 3,
            [
                math.cos(pitch_radians), 0.0, math.sin(pitch_radians),
                0.0, 1.0, 0.0,
                -math.sin(pitch_radians), 0.0, math.cos(pitch_radians),
            ]
        )

        # Roll, or X
        roll_matrix = Matrix(
            3, 3,
            [
                1.0, 0.0, 0.0,
                0.0, math.cos(roll_radians), -math.sin(roll_radians),
                0.0, math.sin(roll_radians), math.cos(roll_radians),
            ]
        )

        # Yaw, or Z
        yaw_matrix = Matrix(
            3, 3,
            [
                math.cos(yaw_radians), -math.sin(yaw_radians), 0.0,
                math.sin(yaw_radians), math.cos(yaw_radians), 0.0,
                0.0, 0.0, 1.0,
            ]
        )

        # Order is Yaw * Pitch * Roll
        rotation_matrix = yaw_matrix * pitch_matrix * roll_matrix

        # Return a new Triangle
        return self * rotation_matrix

    def get_scaled(self, scale=1.0):
        """
        """
        """
        parameters
            Int, or Float
        returns
            Triangle
        """
        if isinstance(scale, (int, float)):
            return self * scale
        return self

    def get_texcoords(self):
        """
        returns
            List<Vector2> texcoords
        """
        return self.__texcoords

    def get_transformed(
        self,
        offset=Vector3(0.0, 0.0, 0.0),
        yaw=0.0, pitch=0.0, roll=0.0,
        scale=1.0,
    ):
        """
        parameters
            Vector3
                offset
            float
                yaw, or z
            float
                pitch, or y
            float
                roll, or x
        returns
            Triangle
        """
        return self.get_scaled(
                scale
            ).get_rotation(
                yaw, pitch, roll
            ).get_offset(
                offset
            )

    def get_vertex_data(
        self, positions=True, normals=True,
        colors=True, texcoords=True,
        offset=Vector3(0.0, 0.0, 0.0),
        yaw=0.0, pitch=0.0, roll=0.0
    ):
        """
        parameters
            4 optional bools; positions, normals, colors, texcoords.
                These tell the getter to return these values or not.
            Vector3
                offset
            float
                yaw, or z
            float
                pitch, or y
            float
                roll, or x
        returns
            [[float x 6]] x 3 vertexes of this triangle.
        """

        # Get a Transformed Triangle
        transformed_triangle = self.get_transformed(
            offset, yaw, pitch, roll
        )

        vertex_data = []
        for i in range(0, 3):
            vertex = []
            if positions:
                vertex.extend(
                    transformed_triangle.get_positions()[i].get_tuple()
                )
            if normals:
                vertex.extend(
                    transformed_triangle.get_normals().get_tuple()
                )
            if colors and self.__colors is not None:
                vertex.extend(
                    transformed_triangle.get_colors()[i].get_tuple()
                )
            if texcoords and self.__texcoords is not None:
                vertex.extend(
                    transformed_triangle.get_texcoords()[i].get_tuple()
                )
            vertex_data.append(vertex)
        return vertex_data
        """
        return [
            [*p.get_tuple(), *self.__normals.get_tuple()]
            for p in self.__positions
        ]
        """

    def has_position(self, position):
        """
        parameters
            Vector3
        returns
            bool of whether the given Vector3 matches one of ours.
        """
        if not type(position).__name__ == "Vector":
            return False
        return position in self.__positions


"""QGIS Unit tests for QgsOpacityWidget

.. note:: This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
"""
__author__ = 'Nyall Dawson'
__date__ = '30/05/2017'
__copyright__ = 'Copyright 2017, The QGIS Project'

from qgis.PyQt.QtTest import QSignalSpy
from qgis.gui import QgsOpacityWidget
import unittest
from qgis.testing import start_app, QgisTestCase

start_app()

class TestQgsOpacityWidget(QgisTestCase):

    def testGettersSetters(self):
        """ test widget getters/setters """
        w = QgsOpacityWidget()

        w.setOpacity(0.2)
        self.assertEqual(w.opacity(), 0.2)

        # bad values
        w.setOpacity(-0.2)
        self.assertEqual(w.opacity(), 0.0)
        w.setOpacity(100)
        self.assertEqual(w.opacity(), 1.0)

    def test_ChangedSignals(self):
        """ test that signals are correctly emitted when setting opacity"""

        w = QgsOpacityWidget()

        spy = QSignalSpy(w.opacityChanged)
        w.setOpacity(0.2)

        self.assertEqual(len(spy), 1)
        self.assertEqual(spy[0][0], 0.2)

        # bad value
        w.setOpacity(100)
        self.assertEqual(len(spy), 2)
        self.assertEqual(spy[1][0], 1.0)

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


class TestBank:
    def __init__(self):
        self.tests = []

    def add_test(self, test):
        """
        Добавляет новый тест в список тестов
        :param test: экземпляр класса Test
        :return: True, если тест успешно добавлен, иначе False
        """
        if isinstance(test, Test):
            self.tests.append(test)
            return True
        return False

    def remove_test(self, test):
        """
        Удаляет тест из списка тестов
        :param test: экземпляр класса Test
        :return: True, если тест успешно удален, иначе False
        """
        if test in self.tests:
            self.tests.remove(test)
            return True
        print("Тест не найден.")
        return False

    def update_test(self, test, **kwargs):
        """
        Обновляет информацию о тесте
        :param test: экземпляр класса Test
        :param kwargs: словарь с новыми значениями атрибутов теста
        :return: True, если информация о тесте успешно обновлена, иначе False
        """
        if test in self.tests:
            for key, value in kwargs.items():
                setattr(test, key, value)
            return True
        print("Тест не найден.")
        return False


import schemas
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from db.database import get_db
from db import models
import uuid

app = APIRouter()

# To rate a user
@app.post('/user_review/', tags=["ratings"])
def create_user_review(user_review: schemas.user_rate, db: Session = Depends(get_db)):
    StarRating = db.query(models.User_Review).filter(models.User_Review.reviewer_id == user_review.reviewer_id).first()
    if StarRating is None:
        new_rating = models.User_Review(
            id=uuid.uuid4(),
            reviewee_id=user_review.reviewee_id,
            reviewer_id=user_review.reviewer_id,
            rate=user_review.rate
        )
        db.add(new_rating)

        db.commit()

        StarRating = db.query(models.User_Review).filter(
            models.User_Review.reviewer_id == user_review.reviewer_id).first()

        return {"response": StarRating}
    else:
        return {"response": "you are aleady rated"}

# To get average rate of a user
@app.post('/average_user_rating', tags=["ratings"])
def average_user_review(review: schemas.user_rate, db: Session = Depends(get_db)):
    ratings = db.query(models.User_Review.rate).filter(models.User_Review.reviewee_id == review.reviewee_id).all()
    if ratings:
        total_user_rate = [sum(rate) for rate in ratings]
        total_count_rate = [len(rate) for rate in ratings]
        average = 0 if len(total_user_rate) == 0 else (float(sum(total_user_rate)) / len(total_count_rate))
        return {"average": average, "response": ratings}
    else:
        return {"response": "Invalid id"}


"""
A module that contains linear algebra routines over Galois fields.
"""
import numpy as np

from ._dtypes import DTYPES

def _lapack_linalg(a, b, function, out=None, n_sum=None):
    """
    In prime fields GF(p), it's much more efficient to use LAPACK/BLAS implementations of linear algebra
    and then reduce modulo p rather than compute manually.
    """
    assert type(a).is_prime_field
    field = type(a)
    characteristic = field.characteristic

    # Determine the return data-type which is the minimum of the two inputs' data-types
    if np.object_ in [a.dtype, b.dtype]:
        return_dtype = np.object_
    else:
        return_dtype = a.dtype if np.iinfo(a.dtype).max < np.iinfo(b.dtype).max else b.dtype

    a = a.view(np.ndarray)
    b = b.view(np.ndarray)

    # Determine the minimum dtype to hold the entire product and summation without overflowing
    if n_sum is None:
        n_sum = 1 if len(a.shape) == 0 else max(a.shape)
    max_value = n_sum * (characteristic - 1)**2
    dtypes = [dtype for dtype in DTYPES if np.iinfo(dtype).max >= max_value]
    dtype = np.object_ if len(dtypes) == 0 else dtypes[0]
    a = a.astype(dtype)
    b = b.astype(dtype)

    # Compute result using native numpy LAPACK/BLAS implementation
    if function in [np.inner, np.vdot]:
        # These functions don't have and `out` keyword argument
        c = function(a, b)
    else:
        c = function(a, b, out=out)
    c = c % characteristic  # Reduce the result mod p

    if np.isscalar(c):
        # TODO: Sometimes the scalar c is a float?
        c = field(int(c), dtype=return_dtype)
    else:
        c = c.astype(return_dtype).view(field)

    return c

###############################################################################
# Matrix/vector products
###############################################################################

def dot(a, b, out=None):  # pylint: disable=unused-argument
    """
    https://numpy.org/doc/stable/reference/generated/numpy.dot.html
    """
    if not type(a) is type(b):
        raise TypeError(f"Operation 'dot' requires both arrays be in the same Galois field, not {type(a)} and {type(b)}.")

    if type(a).is_prime_field:
        return _lapack_linalg(a, b, np.dot, out=out)

    if a.ndim == 0 or b.ndim == 0:
        return a * b
    elif a.ndim == 1 and b.ndim == 1:
        return np.sum(a * b)
    elif a.ndim == 2 and b.ndim == 2:
        return np.matmul(a, b, out=out)
    elif a.ndim >= 2 and b.ndim == 1:
        return np.sum(a * b, axis=-1, out=out)
    # elif a.dnim >= 2 and b.ndim >= 2:
    else:
        raise NotImplementedError("Currently 'dot' is only supported up to 2-D matrices. Please open a GitHub issue at https://github.com/mhostetter/galois/issues.")

def vdot(a, b):
    """
    https://numpy.org/doc/stable/reference/generated/numpy.vdot.html
    """
    if not type(a) is type(b):
        raise TypeError(f"Operation 'vdot' requires both arrays be in the same Galois field, not {type(a)} and {type(b)}.")

    if type(a).is_prime_field:
        return _lapack_linalg(a, b, np.vdot)

    a = a.flatten()
    b = b.flatten().reshape(a.shape)  # This is done to mimic numpy's error scenarios

    return np.sum(a * b)

def inner(a, b):
    """
    https://numpy.org/doc/stable/reference/generated/numpy.inner.html#numpy.inner
    """
    if not type(a) is type(b):
        raise TypeError(f"Operation 'inner' requires both arrays be in the same Galois field, not {type(a)} and {type(b)}.")

    if type(a).is_prime_field:
        return _lapack_linalg(a, b, np.inner)

    if a.ndim == 0 or b.ndim == 0:
        return a * b
    if not a.shape[-1] == b.shape[-1]:
        raise ValueError(f"Operation 'inner' requires `a` and `b` to have the same last dimension, not {a.shape} and {b.shape}.")

    return np.sum(a * b, axis=-1)

def outer(a, b, out=None):  # pylint: disable=unused-argument
    """
    https://numpy.org/doc/stable/reference/generated/numpy.outer.html#numpy.outer
    """
    if not type(a) is type(b):
        raise TypeError(f"Operation 'outer' requires both arrays be in the same Galois field, not {type(a)} and {type(b)}.")

    if type(a).is_prime_field:
        return _lapack_linalg(a, b, np.outer, out=out, n_sum=1)
    else:
        return np.multiply.outer(a.ravel(), b.ravel(), out=out)

###############################################################################
# Matrix decomposition routines
###############################################################################

def row_reduce(A, ncols=None):
    if not A.ndim == 2:
        raise ValueError(f"Only 2-D matrices can be converted to reduced row echelon form, not {A.ndim}-D.")

    ncols = A.shape[1] if ncols is None else ncols
    A_rre = A.copy()
    p = 0  # The pivot

    for j in range(ncols):
        # Find a pivot in column `j` at or below row `p`
        idxs = np.nonzero(A_rre[p:,j])[0]
        if idxs.size == 0:
            continue
        i = p + idxs[0]  # Row with a pivot

        # Swap row `p` and `i`. The pivot is now located at row `p`.
        A_rre[[p,i],:] = A_rre[[i,p],:]

        # Force pivot value to be 1
        A_rre[p,:] /= A_rre[p,j]

        # Force zeros above and below the pivot
        idxs = np.nonzero(A_rre[:,j])[0].tolist()
        idxs.remove(p)
        A_rre[idxs,:] -= np.multiply.outer(A_rre[idxs,j], A_rre[p,:])

        p += 1
        if p == A_rre.shape[0]:
            break

    return A_rre

def lu_decompose(A):
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise ValueError(f"Argument `A` must be a square matrix, not {A.shape}.")

    field = type(A)
    n = A.shape[0]
    Ai = A.copy()
    L = field.Identity(n)

    for i in range(0, n-1):
        if Ai[i,i] == 0:
            raise ValueError("The LU decomposition of `A` does not exist. Use the LUP decomposition instead.")
        l = Ai[i+1:,i] / Ai[i,i]
        Ai[i+1:,:] -= np.multiply.outer(l, Ai[i,:])
        L[i+1:,i] = l
    U = Ai

    return L, U

def lup_decompose(A):
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise ValueError(f"Argument `A` must be a square matrix, not {A.shape}.")

    field = type(A)
    n = A.shape[0]
    Ai = A.copy()
    L = field.Zeros((n,n))
    P = field.Identity(n)

    for i in range(0, n-1):
        if Ai[i,i] == 0:
            idxs = np.nonzero(Ai[i:,i])[0]  # The first non-zero entry in column `i` below row `i`
            if idxs.size == 0:
                L[i,i] = 1
                continue
            j = i + idxs[0]

            # Swap rows `i` and `j`
            P[[i,j],:] = P[[j,i],:]
            Ai[[i,j],:] = Ai[[j,i],:]
            L[[i,j],:] = L[[j,i],:]

        l = Ai[i+1:,i] / Ai[i,i]
        Ai[i+1:,:] -= np.multiply.outer(l, Ai[i,:])  # Zero out rows below row `i`
        L[i,i] = 1  # Set 1 on the diagonal
        L[i+1:,i] = l

    L[-1,-1] = 1  # Set the final diagonal to 1
    U = Ai

    return L, U, P

###############################################################################
# Matrix inversions, solutions, rank, etc
###############################################################################

def matrix_rank(A):
    A_rre = row_reduce(A)
    return np.sum(~np.all(A_rre == 0, axis=1))

def inv(A):
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise np.linalg.LinAlgError(f"Argument `A` must be square, not {A.shape}.")
    field = type(A)
    n = A.shape[0]
    I = field.Identity(n, dtype=A.dtype)

    # Concatenate A and I to get the matrix AI = [A | I]
    AI = np.concatenate((A, I), axis=-1)

    # Perform Gaussian elimination to get the reduced row echelon form AI_rre = [I | A^-1]
    AI_rre = row_reduce(AI, ncols=n)

    # The rank is the number of non-zero rows of the row reduced echelon form
    rank = np.sum(~np.all(AI_rre[:,0:n] == 0, axis=1))
    if not rank == n:
        raise np.linalg.LinAlgError(f"Argument `A` is singular and not invertible because it does not have full rank of {n}, but rank of {rank}.")

    A_inv = AI_rre[:,-n:]

    return A_inv

def triangular_det(A):
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise np.linalg.LinAlgError(f"Argument `A` must be square, not {A.shape}.")
    idxs = np.arange(0, A.shape[0])
    return np.multiply.reduce(A[idxs,idxs])

def det(A):
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise np.linalg.LinAlgError(f"Argument `A` must be square, not {A.shape}.")
    n = A.shape[0]

    if n == 2:
        return A[0,0]*A[1,1] - A[0,1]*A[1,0]
    elif n == 3:
        return A[0,0]*(A[1,1]*A[2,2] - A[1,2]*A[2,1]) - A[0,1]*(A[1,0]*A[2,2] - A[1,2]*A[2,0]) + A[0,2]*(A[1,0]*A[2,1] - A[1,1]*A[2,0])
    else:
        L, U, P = lup_decompose(A)
        idxs = np.arange(0, n)
        nrows = n - np.count_nonzero(P[idxs,idxs]) # The number of moved rows
        S = max(nrows - 1, 0)  # The number of row swaps
        return (-1)**S * triangular_det(L) * triangular_det(U)

def solve(A, b):
    if not type(A) is type(b):
        raise TypeError(f"Arguments `A` and `b` must be of the same Galois field array class, not {type(A)} and {type(b)}.")
    if not (A.ndim == 2 and A.shape[0] == A.shape[1]):
        raise np.linalg.LinAlgError(f"Argument `A` must be square, not {A.shape}.")
    if not b.ndim in [1, 2]:
        raise ValueError(f"Argument `b` must have dimension equal to A or one less, not {b.ndim}.")
    if not A.shape[-1] == b.shape[0]:
        raise ValueError(f"The last dimension of `A` must equal the first dimension of `b`, not {A.shape} and {b.shape}.")

    A_inv = inv(A)
    x = A_inv @ b

    return x


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

import decimal
from functools import reduce
import json
from typing import Any, Dict, List, Optional, Tuple, Union

from django.db import connection

from catmaid.error import ClientError

class StateMatchingError(ClientError):
    """Indicates that a state check wasn't successful"""
    def __init__(self, message, state):
        super().__init__(message)
        self.unmatched_state = state

    def __str__(self) -> str:
        return "{}: {}".format(self.args[0],
            str(self.unmatched_state) or "(no details found)")

class SQL:
    was_edited = """
        SELECT 1 FROM location t
        WHERE t.id = %s
        AND t.edition_time >=(%s::timestamptz - '1 ms'::interval)
        AND t.edition_time < (%s::timestamptz + '1 ms'::interval)
        LIMIT 1
    """
    is_parent = """
        SELECT 1 FROM treenode t
        WHERE t.parent_id = %s AND t.id = %s
    """
    is_child = """
        SELECT 1 FROM treenode t
        WHERE t.id = %s AND t.parent_id = %s
    """
    is_root = """
        SELECT 1 FROM treenode t
        WHERE t.parent_id IS NULL AND t.id = %s
    """
    all_children = """
        SELECT CASE WHEN sub.c=0 THEN 1 ELSE 0 END FROM (
            SELECT COUNT(*) FROM treenode t %s WHERE t.parent_id = %s %s
        ) sub(c)
    """
    all_links = """
        SELECT CASE WHEN sub.c=0 THEN 1 ELSE 0 END FROM (
            SELECT COUNT(*) FROM treenode_connector l %s WHERE l.treenode_id = %s %s
        ) sub(c)
    """
    all_links_c = """
        SELECT CASE WHEN sub.c=0 THEN 1 ELSE 0 END FROM (
            SELECT COUNT(*) FROM treenode_connector l %s WHERE l.connector_id = %s %s
        ) sub(c)
    """

    @staticmethod
    def edited(table):
        return f"""
            SELECT 1 FROM {table} t
            WHERE t.id = %s
            AND t.edition_time >=(%s::timestamptz - '1 ms'::interval)
            AND t.edition_time < (%s::timestamptz + '1 ms'::interval)
        """

class StateCheck:
    """A simple wraper arround state check SQL and parameters for it"""

    def __init__(self, sql, params):
        if not isinstance(sql, str):
            raise ValueError("No SQL string")
        self.sql = sql
        self.params = params if type(params) in (list, tuple) else (params,)

    def __str__(self) -> str:
        return f"SQL: {self.sql} Parameters: {self.params}"

def make_all_children_query(child_ids, node_id) -> StateCheck:
    if child_ids:
        table_sql, table_args = list_to_table(child_ids, 1)
        args = table_args
        child_query = f" LEFT JOIN {table_sql} p(id) ON t.id = p.id "
        constraints = "AND p.id IS NULL"
        args.append(node_id)
        return StateCheck(SQL.all_children % (child_query, "%s", constraints), args)
    else:
        return StateCheck(SQL.all_children % ("", "%s", ""), [node_id])

def make_all_links_query(link_ids, node_id, is_connector=False) -> StateCheck:
    template = SQL.all_links_c if is_connector else SQL.all_links
    if link_ids:
        table_sql, table_args = list_to_table(link_ids, 1)
        args = table_args
        link_query = f" LEFT JOIN {table_sql} p(id) ON l.id = p.id "
        constraints = "AND p.id IS NULL"
        args.append(node_id)
        return StateCheck(template % (link_query, "%s", constraints), args)
    else:
        return StateCheck(template % ("", "%s", ""), [node_id])

def list_to_table(input_list, n=1) -> Tuple[str, Any]:
    if n == 1:
        args:List[Tuple] = [(e,) for e in input_list]
    elif n == 2:
        args = [(e[0], e[1]) for e in input_list]
    else:
        raise ValueError("Invalid n parameter for list_to_table")
    if not args: # empty input_list or invalid n
        raise ValueError("Could't parse list argument in state check")

    records_list_template = ','.join(['%s'] * len(args))
    return (f"(VALUES {records_list_template})", args)

def has_only_truthy_values(element, n=2) -> bool:
    return n == len(element) and all(element)

def parse_state(state):
    """Expect a JSON string and returned the parsed object."""
    if not state:
        raise ValueError("No state provided")

    if isinstance(state, str):
        state = json.loads(state, parse_float=decimal.Decimal)

    def check_ref(name, ref):
        if type(ref) not in (list, tuple) or len(ref) != 2:
            raise ValueError(f"Invalid state provided, {name} is not a list of two elements")

    def parse_id(name, id):
        try:
            return int(id)
        except (ValueError, TypeError):
            raise ValueError(f"Invalid state provided, couldn't parse {name} id: {id}")

    # Make sure child, parent and link ids are integers
    parsed_state_type = type(state)
    if parsed_state_type == dict:
        parent = state.get('parent')
        if parent:
            check_ref('parent', parent)
            # Allow parent to be None, as an alternative way to say there is no
            # parent.
            parent[0] = None if not parent[0] else parse_id('parent', parent[0])
        children = state.get('children')
        if children:
            if type(children) not in (list, tuple):
                raise ValueError("Invald state provided, 'children' is not a list")
            for c in children:
                check_ref("child", c)
                c[0] = parse_id("child", c[0])
        links = state.get('links')
        if links:
            if type(links) not in (list, tuple):
                raise ValueError("Invald state provided, 'links' is not a list")
            for link in links:
                check_ref("link", link)
                link[0] = parse_id("link", link[0])
    elif parsed_state_type == list:
        for n in state:
            check_ref('node', n)
            n[0] = parse_id('node', n[0])

    return state

def collect_state_checks(node_id, state, cursor, node=False,
        parent_edittime=False, is_parent=False, children=False,
        links=False, c_links=False, multinode=False) -> List[StateCheck]:
    """Collect state checks for a single node, but don't execute them.

    If <children> is a list of node IDs, only these nodes will be checked if
    they are valid children. If <children> is the boolean True, a state check is
    added that tests if the state provided children represent *all* children.
    """
    state_checks:List = []

    if node:
        if 'edition_time' not in state:
            raise ValueError("No valid state provided, missing edition time")
        node = [node_id, state['edition_time']]

        # Make sure the node itself is valid
        state_checks = [StateCheck(SQL.was_edited, (node[0], node[1], node[1]))]
    else:
        node = [node_id]

    if parent_edittime or is_parent:
        parent = state.get('parent')
        if not parent:
            parent_id = None
        elif 2 != len(parent):
            raise ValueError("No valid state provided, invalid parent")
        else:
            parent_id = parent[0]

        if parent_id and -1 != parent_id and not parent[1]:
            raise ValueError("No valid state provided, invalid parent")

        # Collect qurey components, startwith parent relation
        if parent_id and -1 != parent_id:
            if is_parent:
                if parent_id == node_id:
                    raise ValueError(f"No valid state provided, parent is same as node ({parent_id})")
                state_checks.append(StateCheck(SQL.is_parent, (parent_id, node_id)))
            state_checks.append(StateCheck(SQL.was_edited,
                (parent_id, parent[1], parent[1])))
        else:
            state_checks.append(StateCheck(SQL.is_root, (node_id,)))

    if children:
        child_nodes = state.get('children')
        if not isinstance(child_nodes, (list, tuple)):
            raise ValueError("No valid state provided, can't find list 'children'")
        if not all(has_only_truthy_values(e) for e in child_nodes):
            raise ValueError("No valid state provided, invalid children")

        if type(children) == bool:
            state_checks.append(make_all_children_query(
                [int(c[0]) for c in child_nodes], node_id))
        state_checks.extend(StateCheck(SQL.was_edited, (c[0], c[1], c[1])) for c in child_nodes)
        state_checks.extend(StateCheck(SQL.is_child, (c[0], node_id)) for c in child_nodes)

    if links:
        links = state.get('links')
        if not isinstance(links, (list, tuple)):
            raise ValueError("No valid state provided, can't find list 'links'")
        if not all(has_only_truthy_values(e) for e in links):
            raise ValueError("No valid state provided, invalid links")

        state_checks.append(make_all_links_query(
            [int(link[0]) for link in links], node_id))
        state_checks.extend(StateCheck(SQL.edited('treenode_connector'),
            (link[0], link[1], link[1])) for link in links)

    if c_links:
        c_links = state.get('c_links')
        if not isinstance(c_links, (list, tuple)):
            raise ValueError("No valid state provided, can't find list 'c_links'")
        if not all(has_only_truthy_values(e) for e in c_links):
            raise ValueError("No valid state provided, invalid links")

        state_checks.append(make_all_links_query(
            [int(link[0]) for link in c_links], node_id, True))
        state_checks.extend(StateCheck(SQL.edited('treenode_connector'),
            (link[0], link[1], link[1])) for link in c_links)

    return state_checks

def validate_state(node_ids, state, node=False, is_parent=False,
        parent_edittime=False, children=False, links=False, c_links=False,
        multinode=False, neighborhood=False, lock=True, cursor=None) -> None:
    """Validate a local state relative to a given node. What tests are performed
    depends on the mode flags set.

    Modes are hierarchical: neighborhood implies node, is_parent,
    parent_edittime, children and links. A edge implies is_parent,
    parent_edittime and children. The special mode "multinode" voids others and
    expects a list of two-element-lists containing node IDs and edition times,
    which will be checked.

    Expect state to be a dictionary of of the following form, can be provided
    as a JSON string. Only entry for set flags need to be present:
    {
      parent: (<id>, <edition_time>),
      children: ((<child_id>, <child_edition_time>), ...),
      links: ((<connector_id>, <connector_edition_time>, <relation_id>), ...)
    }
    """
    state = parse_state(state)

    # Make sure input nodes are iterable
    if type(node_ids) not in (list, tuple):
        node_ids = (node_ids,)

    # Neighborhood implies node and parent checks
    node = node or neighborhood
    is_parent = is_parent or neighborhood
    parent_edittime = parent_edittime or neighborhood
    children = children or neighborhood
    links = links or neighborhood

    # Collect state checks and test them, if state checks are not disabled
    if not is_disabled(state):
        cursor = cursor or connection.cursor()
        if multinode:
            node_id_set = set(node_ids)
            unseen = set(node_ids)
            for node_state in state:
                node_id = node_state[0]
                if node_id not in node_id_set:
                    raise ValueError(f"Couldn't find node in state: {node_id}")
                unseen.remove(node_id)
            if len(unseen) > 0:
                raise ValueError("Couldn't find state info on node(s) {}".format(
                    ", ".join(str(n) for n in unseen)))
            state_checks = []
            for node_state in state:
                state_checks.append(StateCheck(SQL.was_edited,
                    (node_state[0], node_state[1], node_state[1])))
            check_state(state, state_checks, cursor)
        else:
            check_sets = [collect_state_checks(n, state, cursor, node=node,
                    is_parent=is_parent, parent_edittime=parent_edittime,
                    multinode=multinode, children=children, links=links,
                    c_links=c_links) for n in node_ids]
            # Flatten list of per-node state checks into one list of checks
            state_checks = reduce(lambda x, y: x + y, check_sets)
            check_state(state, state_checks, cursor)

    # Acquire lock on treenode
    if lock:
        cursor = cursor or connection.cursor()
        lock_nodes(node_ids, cursor)

def lock_node(node_id, cursor) -> None:
    cursor.execute("""
        SELECT id FROM treenode WHERE id=%s FOR UPDATE
    """, (node_id,))
    result = cursor.fetchall()

def lock_nodes(node_ids, cursor) -> None:
    if node_ids:
        node_template = ",".join(("%s",) * len(node_ids))
        cursor.execute(f"""
            SELECT id FROM treenode WHERE id IN ({node_template}) FOR UPDATE
        """, node_ids)
    else:
        raise ValueError("No nodes to lock")

def is_disabled(state) -> bool:
    return state and type(state) == dict and state.get('nocheck') is True

def make_nocheck_state(parsed=False):
    """Get a state representation that causes skipping of actual state checks.

    If "parsed" is True, a parsed representation will be returned, otherwise a
    regular JSON representation is used."""
    state:Union[str, Dict] = {'nocheck': True}
    if not parsed:
        state = json.dumps(state)
    return state

def check_state(state, state_checks, cursor) -> None:
    """Raise an error if state checks can't be passed."""
    # Skip actual tests if state checking is disabled in state
    if is_disabled(state):
        return

    sql_checks = [sc.sql for sc in state_checks]
    args:List = []
    for sc in state_checks:
        args.extend(p for p in sc.params)

    if sql_checks:
        cursor.execute("(" + ") INTERSECT (".join(sql_checks) + ")", args)
    else:
        cursor.execute("SELECT 1")
    state_check_results = cursor.fetchall()

    # Expect results to have a length of the number of checks made and that
    # each result equals one.
    if not state_check_results or 1 != len(state_check_results) or 1 != state_check_results[0][0]:
        raise StateMatchingError("The provided state differs from the database state", state)


import pint
import numpy as np

def belt_drive():
    """
    Cálculo de transmisión a correa.

    Ecuaciones y modelos tomados de
    """
    units = pint.UnitRegistry()

    # Cálculo de torque mínimo de motor
    alpha = np.arctan(1.0/5.0)  # Inclinación máxima de rampas (Ley 24.314)
    Pmax = 8 * units.kgf  # Peso nominal
    D = 9.6 * units.cm  # Diámetro de rueda
    Cmin = Pmax * np.sin(alpha) * D / 2

    # Cálculo de velocidad nominal mínima de motor
    vmin = 1 * units.m / units.s
    Nmin = (vmin / (D / 2)).to(units.rpm)

    # Caracteristicas del motorreductor DC MR08D 24v 24:1
    # (con motor DC Mobuchi RS-555SH-2670)
    N = 266 * units.rpm  # Velocidad nominal
    C = 6.8 * units.kgf * units.cm  # Torque a máxima eficiencia
    assert 2 * C > Cmin
    assert N > Nmin

    sf = 1.5  # Factor de servicio nominal
    C_peak = (C * sf).to(units.N * units.m)
    # Se utiliza correa GT3 3 mm de pitch, 6 mm de ancho
    p = 3 * units.mm  # Pitch de correa
    n1 = n2 = 20  # Dientes por polea
    pd = n1 * p / np.pi  # Delta angular

    d_left = 100 * units.mm
    d_right = 80 * units.mm

    W = 6 * units.mm
    h = 2.41 * units.mm - 1.14 * units.mm
    C_rated = 0.95 * 1.26 * units.N * units.m
    assert C_rated > C_peak  # Verificación de torque

    # Cálculo de tensión de correa
    T = 2 * (
        0.812 * C_peak.to(units.lbf * units.inch) / pd.to(units.inch) +
        0.077 * units.lbf * units.minute**2 / units.ft**2 * ((pd * N).to(units.ft / units.minute)/1000)**2
    ).to(units.N)

    # Cálculo de dientes y ángulo de contacto
    EA = 30000 * units.lbf * (0.82 * W / (1 * units.inch))
    ε = (T / EA).to('dimensionless')

    n_left = np.floor((1 - 2 * ε) * ((n1 + n2)/2 + 2 * d_left/p))
    L_left = n_left * p
    n_right = np.floor((1 - 2 * ε) * ((n1 + n2)/2 + 2 * d_right/p))
    L_right = n_right * p

    print('\n'.join('{} = {}'.format(name, value)
                    for name, value in locals().items()
                    if isinstance(value, units.Quantity)))

if __name__ == '__main__':
    belt_drive()


from observation_insight.app.services.models_BERT import predict_scheme_BERT
from observation_insight.models.predict_model import predict_scheme, get_schemes_names, schemes_to_names
from observation_insight.features.build_features import build_features
from observation_insight.app.core.config import args, BOTTOM_PREDICTION_COUNT, TOP_PREDICTION_COUNT
from observation_insight.models.Models import load_label_encoder, load_classifier, load_encoder
import spacy

class Prediction():

    nlp = spacy.load("da_core_news_sm")

    def __init__(self, encoder_name, model_name):
        self.encoder = encoder_name
        self.model_name = model_name

    def load_all_models(self, args):
        encoder_name = args["encoder_name"]
        encoder = load_encoder(args)
        if encoder_name == 'danishBERT':
            label_encoder = None
            label_encoder_model = None
        else:
            label_encoder,label_encoder_model = load_label_encoder(args)
        classifier = load_classifier(args)
        return encoder, label_encoder, label_encoder_model, classifier

    def preprocessing(self, input_text, encoder, nlp):
        data_features = build_features(args, data=input_text, encoder = encoder, nlp = self.nlp)
        return data_features

    def predict_obs_scheme(self, data_features, model, label_encoder, label_encoder_model):
        """
        # Prediction of observation schemes based on free text documentation

        ## Params
        * data_features (embeddings)
        * model (classifier)
        * label_encoder
        * label_encoder_model

        ## Returns:
        - **results** (str): Predicted observation scheme name
        - **predicted_prob_ordered** (float): Probability of predicted observation scheme
        """
        encoder_name = args["encoder_name"]
        if encoder_name == 'danishBERT':
            results, predicted_prob_ordered = predict_scheme_BERT(model,data_features)
            return results, predicted_prob_ordered
        else:
            predicted_schemes_ordered, predicted_prob_ordered = predict_scheme(args, data_features = data_features, classifier = model)
            results = get_schemes_names(args, predicted_schemes_ordered, label_encoder, label_encoder_model)
            return results, predicted_prob_ordered

    def get_results(self, result_scheme, result_prob):
        res = []
        for i in range(len(result_scheme)):
            res.append({"name": result_scheme[i], "probability": float("{:.6f}".format(result_prob[0][i]))})

        results = {
            'result': res
            }

        return results

    def medicin_fix(self, results):
        """Letting medicinadministration and medicindispensering appear together

        Args:
            results (dict): Dictionary of prediction results

        Returns:
            results_dict (dict): Dictionary of prediction results with the added functionality
        """
        top_location = TOP_PREDICTION_COUNT-1
        bottom_location = BOTTOM_PREDICTION_COUNT-1
        order = results['result']
        schemes = []
        for element in order:
            schemes.append(element['name'])

        disp_pos = schemes.index('Medicindispensering')
        adm_pos = schemes.index('Medicinadministration ')

        if adm_pos < disp_pos:
            if adm_pos == top_location or adm_pos == bottom_location:
                order.insert(adm_pos-1, order.pop(adm_pos))
                adm_pos = adm_pos-1
                order[adm_pos]['probability'] =  order[adm_pos+1]['probability']+0.000001
            order.insert(adm_pos+1, order.pop(disp_pos))
            order[adm_pos+1]['probability'] = order[adm_pos]['probability']
        elif disp_pos < adm_pos:
            if disp_pos == top_location or disp_pos == bottom_location:
                order.insert(disp_pos-1, order.pop(disp_pos))
                disp_pos = disp_pos-1
                order[disp_pos]['probability'] =  order[disp_pos+1]['probability']+0.000001
            order.insert(disp_pos+1, order.pop(adm_pos))
            order[disp_pos+1]['probability'] = order[disp_pos]['probability']

        results_dict = {'result': order}

        return results_dict


# Add some right click options

# stdlib imports
from copy import copy
import math

# Local imports
from ..utils.misc import get_root_node, clone_node
from ..ModelExporter.utils import get_children

# Blender imports
import bmesh
import bpy
from bpy.types import Menu, Operator
from mathutils import Matrix

CONE_ROTATION_MAT = Matrix.Rotation(math.radians(90), 4, 'X')

class AddReferenceNode(Operator):
    """Add a new NMS reference node"""
    bl_idname = "nmsdk.add_reference_node"
    bl_label = "Reference Node"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to add a child node to')
            return {'FINISHED'}
        # Create a new empty reference node.
        for obj in selected_objs:
            empty_mesh = bpy.data.meshes.new('ref')
            empty_obj = bpy.data.objects.new('ref', empty_mesh)
            empty_obj.NMSNode_props.node_types = 'Reference'
            empty_obj.matrix_world = Matrix()
            empty_obj.parent = obj
            empty_obj.rotation_mode = 'QUATERNION'
            bpy.context.scene.collection.objects.link(empty_obj)

        return {'FINISHED'}

class AddLocatorNode(Operator):
    """Add a new NMS reference node"""
    bl_idname = "nmsdk.add_locator_node"
    bl_label = "Locator Node"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to add a child node to')
            return {'FINISHED'}
        # Create a new empty reference node.
        for obj in selected_objs:
            empty_mesh = bpy.data.meshes.new('loc')
            empty_obj = bpy.data.objects.new('loc', empty_mesh)
            empty_obj.NMSNode_props.node_types = 'Locator'
            empty_obj.matrix_world = Matrix()
            empty_obj.parent = obj
            empty_obj.rotation_mode = 'QUATERNION'
            bpy.context.scene.collection.objects.link(empty_obj)

        return {'FINISHED'}

class AddBoxCollisionNode(Operator):
    """Add a new NMS box collision node"""
    bl_idname = "nmsdk.add_box_collision_node"
    bl_label = "Box collision node"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to add a child node to')
            return {'FINISHED'}
        # Create a new empty reference node.
        for obj in selected_objs:
            # Create a new cube for collisions
            mesh = bpy.data.meshes.new('cube')
            bm = bmesh.new()
            bmesh.ops.create_cube(bm, size=1.0)
            bm.to_mesh(mesh)
            bm.free()
            cube = bpy.data.objects.new('cube', mesh)
            cube.NMSNode_props.node_types = 'Collision'
            cube.NMSCollision_props.collision_types = 'Box'
            cube.matrix_world = Matrix()
            cube.parent = obj
            bpy.context.scene.collection.objects.link(cube)
            cube.rotation_mode = 'QUATERNION'

        return {'FINISHED'}

class AddSphereCollisionNode(Operator):
    """Add a new NMS sphere collision node"""
    bl_idname = "nmsdk.add_sphere_collision_node"
    bl_label = "Sphere collision node"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to add a child node to')
            return {'FINISHED'}
        # Create a new empty reference node.
        for obj in selected_objs:
            # Create a new sphere for collisions
            mesh = bpy.data.meshes.new('sphere')
            bm = bmesh.new()
            bmesh.ops.create_icosphere(bm, subdivisions=4, radius=0.5)
            bm.to_mesh(mesh)
            bm.free()
            sphere = bpy.data.objects.new('sphere', mesh)
            sphere.NMSNode_props.node_types = 'Collision'
            sphere.NMSCollision_props.collision_types = 'Sphere'
            sphere.matrix_world = Matrix()
            sphere.parent = obj
            bpy.context.scene.collection.objects.link(sphere)
            sphere.rotation_mode = 'QUATERNION'

        return {'FINISHED'}

class AddCylinderCollisionNode(Operator):
    """Add a new NMS cylinder collision node"""
    bl_idname = "nmsdk.add_cylinder_collision_node"
    bl_label = "Cylinder collision node"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to add a child node to')
            return {'FINISHED'}
        # Create a new empty reference node.
        for obj in selected_objs:
            # Create a new cylinder for collisions
            mesh = bpy.data.meshes.new('cylinder')
            bm = bmesh.new()
            bmesh.ops.create_cone(bm, cap_ends=True, cap_tris=True,
                                  radius1=0.5, radius2=0.5, depth=1.0,
                                  segments=20, matrix=CONE_ROTATION_MAT)
            bm.to_mesh(mesh)
            bm.free()
            cylinder = bpy.data.objects.new('cylinder', mesh)
            cylinder.NMSNode_props.node_types = 'Collision'
            cylinder.NMSCollision_props.collision_types = 'Cylinder'
            cylinder.matrix_world = Matrix()
            cylinder.parent = obj
            bpy.context.scene.collection.objects.link(cylinder)
            cylinder.rotation_mode = 'QUATERNION'

        return {'FINISHED'}

class CloneNodes(Operator):
    """Clone the selcted node(s)"""
    bl_idname = "nmsdk.clone_nodes"
    bl_label = "Clone node(s)"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to clone')
            return {'FINISHED'}
        # Make a copy of each of the selected nodes.
        for obj in selected_objs:
            clone_node(obj)
        return {'FINISHED'}

class CloneNodesRecursively(Operator):
    """Clone the selcted node(s) and all attached children"""
    bl_idname = "nmsdk.clone_nodes_recursively"
    bl_label = "Clone node(s) recursively"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", None)
        if not selected_objs:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select an object to clone')
            return {'FINISHED'}
        # Make a copy of each of the selected nodes.
        for obj in selected_objs:
            clone_node(obj, True)
        return {'FINISHED'}

class NMSDK_OT_move_to_parent(Operator):
    """Set the selected object(s) to be a child node of the selected parent"""
    bl_idname = "nmsdk.add_to_parent"
    bl_label = "Set object(s) as child of selected parent"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        selected_objs = getattr(context, "selected_objects", [])
        parent_obj = getattr(context, "active_object", None)

        # First, make sure we have, a) enough objects selected, and b) the
        # correct set of objects to iterate over so that it doesn't include the
        # parent object.
        if len(selected_objs) < 2:
            self.report({'ERROR_INVALID_INPUT'},
                        'Please select 2 or more objects')
            return {'FINISHED'}
        child_objs = set(selected_objs) - {parent_obj}
        root_obj = get_root_node(parent_obj)
        for obj in child_objs:
            # There are two possibilities for moving an object under a parent.
            # 1. It can already be inside the imported scene, in which case we
            # don't want to apply any kind of transforms, but simply set the
            # parent and local transform.
            # 2. The object exists outside of the scene, so we need to do a few
            # extra things detailed within the code block.
            if get_root_node(obj) == root_obj:
                # Copy the local transform.
                trans = copy(obj.matrix_local)
                # set the parent.
                obj.parent = parent_obj
                # Set the local transform as before
                obj.matrix_local = trans
                del trans
            else:
                trans = copy(obj.matrix_world)
                bpy.ops.object.parent_set()
                obj.matrix_world = trans
                del trans

                # Go over all the children nodes, and if any don't have data
                # then they are empty. We'll set these as Locators.
                # Also check the obj itself while we are here...
                for child in [obj] + get_children(obj):
                    if not child.data:
                        child.NMSNode_props.node_types = 'Locator'

        return {'FINISHED'}

class NMSDK_MT_add_NMS_Scenenodes(Menu):
    bl_label = "Add new Scene Node objects"
    bl_idname = "NMSDK_MT_add_NMS_Scenenodes"

    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row.operator('nmsdk.add_reference_node')
        row = layout.row()
        row.operator('nmsdk.add_locator_node')
        layout.separator()
        row = layout.row()
        row.operator('nmsdk.add_box_collision_node')
        row = layout.row()
        row.operator('nmsdk.add_sphere_collision_node')
        row = layout.row()
        row.operator('nmsdk.add_cylinder_collision_node')

class NMSDK_MT_clone_NMS_Scenenodes(Menu):
    bl_label = "Clone NMS Scenenode objects"
    bl_idname = "NMSDK_MT_clone_NMS_Scenenodes"

    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row.operator('nmsdk.clone_nodes')
        row = layout.row()
        row.operator('nmsdk.clone_nodes_recursively')
        row = layout.row()

# Functions to modify blenders' menu items

def add_empty_root_node(self, context):
    """ Add an option to the 'add' menu in the 3D view to add an empty root
    NMS SceneNode reference for exporting. """
    layout = self.layout
    layout.separator()
    layout.operator('nmsdk.create_root_scene')

def parent_menu_func(self, context):
    layout = self.layout
    layout.separator()
    layout.operator('nmsdk.add_to_parent')

def add_obj_menu_func(self, context):
    """ Add the sub-menu to the context menu. """
    layout = self.layout
    if get_root_node(context.active_object) is not None:
        layout.separator()
        row = layout.row()
        row.menu("NMSDK_MT_add_NMS_Scenenodes")
        row = layout.row()
        row.menu("NMSDK_MT_clone_NMS_Scenenodes")

classes = (NMSDK_OT_move_to_parent,
           AddReferenceNode,
           AddBoxCollisionNode,
           AddSphereCollisionNode,
           AddCylinderCollisionNode,
           AddLocatorNode,
           NMSDK_MT_add_NMS_Scenenodes,
           NMSDK_MT_clone_NMS_Scenenodes,
           CloneNodes,
           CloneNodesRecursively)

class ContextMenus():
    @staticmethod
    def register():
        # Register classes to be used.
        for cls_ in classes:
            bpy.utils.register_class(cls_)
        # Add the functions to the menus they need to be in.
        bpy.types.VIEW3D_MT_add.append(add_empty_root_node)
        bpy.types.VIEW3D_MT_object_parent.append(parent_menu_func)
        bpy.types.VIEW3D_MT_object_context_menu.append(add_obj_menu_func)

    @staticmethod
    def unregister():
        # Unregister classes used.
        for cls_ in classes:
            bpy.utils.unregister_class(cls_)
        # Remove the functions from the menus they were in.
        bpy.types.VIEW3D_MT_add.remove(add_empty_root_node)
        bpy.types.VIEW3D_MT_object_parent.remove(parent_menu_func)
        bpy.types.VIEW3D_MT_object_context_menu.remove(add_obj_menu_func)


# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def sortedListToBST(self, head):
        """
        :type head: ListNode
        :rtype: TreeNode
        """

        li = []
        node = head

        while node:
            li.append(node.val)
            node = node.next

        def operate(left, right):
            if left > right:
                return
            mid = (left + right) / 2
            node = TreeNode()
            node.val = li[mid]
            node.left = operate(left, mid - 1)
            node.right = operate(mid + 1, right)
            return node

        return operate(0, len(li) - 1)

Medium/109. Convert Sorted List to Binary Search Tree.py


#!/usr/bin/python3

import os

class GPIO():

    def __init__(self, gpio_base_path, direction="in", value=0):
        if os.path.exists(gpio_base_path):
            self._base_path = gpio_base_path
            self._gpio_direction_path = self._base_path + str("/direction")
            self._gpio_value_path = self._base_path + str("/value")

            try:
                self.set_direction(direction)
                self.set_value(value)
            except:
                return -1

    def set_direction(self, direction):
        gpio_file = open(self._gpio_direction_path, "w")
        gpio_file.write(direction)
        gpio_file.close()

    def get_direction(self):
        gpio_file = open(self._gpio_direction_path, "r")
        direction = gpio_file.read()
        gpio_file.close()
        return direction

    def set_value(self, value):
        gpio_file = open(self._gpio_value_path, "w")
        gpio_file.write(str(value))
        gpio_file.close()

    def get_value(self):
        gpio_file = open(self._gpio_value_path, "r")
        value = gpio_file.read()
        gpio_file.close()
        return value


from django.conf.urls.defaults import patterns, include, url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.http import HttpResponse

from settings import MEDIA_ROOT

from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',

    (r'^chart/', include('apps.chart.urls', namespace='chart', app_name='chart')),
    (r'^dig/', include('apps.dig.urls', namespace='dig', app_name='dig')),
    (r'^origin/', include('apps.origin.urls', namespace='origin', app_name='origin')),
	(r'^user/', include('apps.user.urls', namespace='user', app_name='user')),
	(r'^business/', include('apps.business.urls', namespace='business', app_name='business')),
    (r'^$', 'django.contrib.auth.views.login', {'template_name':'registration/login.html'}),
    (r'^login/$', 'django.contrib.auth.views.login', {'template_name':'registration/login.html'}),
    (r'^logout/$', 'django.contrib.auth.views.logout_then_login'),
    (r'^static/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT }),
    (r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': MEDIA_ROOT }),
    (r'^robots\.txt$', lambda r: HttpResponse("User-agent: *\nDisallow: /", mimetype="text/plain")),
)

urlpatterns += staticfiles_urlpatterns()


import os
from flask import render_template, Blueprint, current_app, request, send_from_directory, flash, redirect, url_for, abort
from flask_login import login_required, current_user

from ..decorators import confirm_required, permission_required
from ..extensions import db
from ..models import Photo, Comment, Tag
from ..utils import rename_image, resize_image, flash_errors
from ..forms.main import CommentForm, DescriptionForm, TagForm

main_bp = Blueprint('main', __name__)

@main_bp.route('/')
def index():
    return render_template('main/index.html')

@main_bp.route('/explore')
def explore():
    return render_template('main/explore.html')

@main_bp.route('/upload', methods=['GET', 'POST'])
@login_required
@confirm_required
@permission_required('UPLOAD')
def upload():
    if request.method == 'POST' and 'file' in request.files:
        f = request.files.get('file')
        filename = rename_image(f.filename)
        f.save(os.path.join(current_app.config['ALBUMY_UPLOAD_PATH'], filename))
        filename_s = resize_image(f, filename, current_app.config['ALBUMY_PHOTO_SIZE']['small'])
        filename_m = resize_image(f, filename, current_app.config['ALBUMY_PHOTO_SIZE']['medium'])
        photo = Photo(
            filename=filename,
            filename_s=filename_s,
            filename_m=filename_m,
            author=current_user._get_current_object()
        )
        db.session.add(photo)
        db.session.commit()
    return render_template('main/upload.html')

@main_bp.route('/avatars/<path:filename>')
def get_avatar(filename):
    return send_from_directory(current_app.config['AVATARS_SAVE_PATH'], filename)

@main_bp.route('/uploads/<path:filename>')
def get_image(filename):
    return send_from_directory(current_app.config['ALBUMY_UPLOAD_PATH'], filename)

@main_bp.route('/photo/<int:photo_id>')
def show_photo(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    page = request.args.get('page', 1, type=int)
    per_page = current_app.config['ALBUMY_COMMENT_PER_PAGE']
    pagination  = Comment.query.with_parent(photo).order_by(Comment.timestamp.desc()).paginate(page, per_page)
    comments = pagination.items

    comment_form = CommentForm()
    description_form = DescriptionForm()
    tag_form = TagForm()

    description_form.description.data = photo.description
    return render_template('main/photo.html', photo=photo, comment_form=comment_form, description_form=description_form,
                           tag_form=tag_form, pagination=pagination, comments=comments)

@main_bp.route('/photo/n/<int:photo_id>')
def photo_next(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    photo_n = Photo.query.with_parent(photo.author).filter(Photo.id < photo_id).order_by(Photo.id.desc()).first()

    if photo_n is None:
        flash('This is already the last one.', 'info')
        return redirect(url_for('.show_photo', photo_id=photo_id))
    return redirect(url_for('.show_photo', photo_id=photo_n.id))

@main_bp.route('/photo/p/<int:photo_id>')
def photo_previous(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    photo_p = Photo.query.with_parent(photo.author).filter(Photo.id < photo_id).order_by(Photo.id.desc()).frist()

    if photo_p is None:
        flash('This is already the first one.', 'info')
        return redirect(url_for('.show_photo', photo_id=photo_id))
    return redirect(url_for('.show_photo', photo_id=photo_p.id))

@main_bp.route('/report/comment/<int:comment_id>', methods=['POST'])
@login_required
@confirm_required
def report_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    comment.flag += 1
    db.session.commit()
    flash('Comment reported.', 'info')
    return redirect(url_for('.show_photo', photo_id=comment.photo_id))

@main_bp.route('/report/photo/<int:photo_id>', methods=['POST'])
@login_required
@confirm_required
def report_photo(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    photo.flag += 1
    db.session.commit()
    flash('Photo reported.', 'info')
    return redirect(url_for('.show_photo', photo_id=photo_id))

@main_bp.route('/photo/<int:photo_id>/description', methods=['POST'])
@login_required
def edit_description(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    if current_user != photo.author:
        abort(403)

    form = DescriptionForm()
    if form.validate_on_submit():
        photo.description = form.description.data
        db.session.commit()
        flash('Description updated.', 'success')

    flash_errors(form)
    return redirect(url_for('.show_photo', photo_id=photo_id))

@main_bp.route('/photo/<int:photo_id>/comment/new', methods=['POST'])
@login_required
@permission_required('COMMENT')
def new_comment(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    page = request.args.get('page', 1, type=int)
    form = CommentForm()
    if form.validate_on_submit():
        body = form.body.data
        author = current_user._get_current_object()
        comment = Comment(body=body, author=author, photo=photo)

        replied_id = request.args.get('reply')
        if replied_id:
            comment.replied = Comment.query.get_or_404(replied_id)
        db.session.add(comment)
        db.session.commit()
        flash('Comment published.', 'success')

    flash_errors(form)
    return redirect(url_for('.show_photo', photo_id=photo_id, page=page))

@main_bp.route('/photo/<int:photo_id>/tag/new', methods=['POST'])
@login_required
def new_tag(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    if current_user != photo.author:
        abort(403)

    form = TagForm()
    if form.validate_on_submit():
        for name in form.tag.data.split():
            tag = Tag.query.filter_by(name=name).first()
            if tag is None:
                tag = Tag(name=tag)
                db.session.add(tag)
            if tag not in photo.tags:
                photo.tags.append(tag)
                db.session.commit()
        flash('Tag added', 'success')
    flash_errors(form)
    return redirect(url_for('.show_photo', photo_id=photo_id))

@main_bp.route('/set-comment/<int:photo_id>', methods=['POST'])
@login_required
def set_comment(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    if current_user != photo.author:
        abort(403)

    if photo.can_comment:
        photo.can_comment = False
        flash('Comment disabled.', 'info')
    else:
        photo.can_comment = True
        flash('Comment enabled.', 'info')
    db.session.commit()
    return redirect(url_for('.show_photo', photo_id=photo_id))

@main_bp.route('/reply/comment/<int:comment_id>')
@login_required
@permission_required('COMMENT')
def reply_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    return redirect(url_for('.show_photo', photo_id=comment.photo_id, reply=comment_id, author=comment.author.name)
                    + '#comment-form')

@main_bp.route('/delete/photo/<int:photo_id>', methods=['POST'])
@login_required
def delete_photo(photo_id):
    photo = Photo.query.get_or_404(photo_id)
    if current_user != photo.author:
        abort(403)

    db.session.delete(photo)
    db.session.commit()
    flash('Photo deleted.', 'info')

    photo_n = Photo.query.with_parent(photo.author).filter(Photo.id > photo.id).first()
    if photo_n is None:
        photo_p = Photo.query.with_parent(photo.author).filter(Photo.id < photo.id).first()
        if photo_p is None:
            return redirect(url_for('user.index', username=photo.author.username))
        return redirect(url_for('.show_photo', photo_id=photo_p.id))
    return redirect(url_for('.show_photo', photo_id=photo_n.id))

@main_bp.route('/delete/comment/<int:comment_id>', methods=['POST'])
@login_required
def delete_comment(comment_id):
    comment = Comment.query.get_or_404(comment_id)
    if current_user != comment.author:
        abort(403)

    db.session.delete(comment)
    db.session.commit()
    flash('Comment deleted.', 'info')
    return redirect(url_for('.show_photo', photo_id=comment.photo_id))  # comment is a python object

@main_bp.route('/tag/<int:tag_id>', defaults={'order': 'by_time'})
@main_bp.route('/tag/<int:tag_id>/<order>')
def show_tag(tag_id, order):
    tag = Tag.query.get_or_404(tag_id)
    page = request.args.get('page', 1, type=int)
    per_page = current_app.config['ALBUMY_PHOTO_PER_PAGE']
    order_rule = 'time'
    pagination = Photo.query.with_parent(tag).order_by(Photo.timestamp.desc()).paginate(page, per_page)
    photos = pagination.items

    if order == 'by_collects':
        photos.sort(key=lambda x: len(x.collectors), reverse=True)
        order_rule = 'collects'
    return render_template('main/tag.html', tag=tag, pagination=pagination, photos=photos, order_rule=order_rule)

@main_bp.route('/delete/tag/<int:photo_id>/<int:tag_id>', methods=['POST'])
@login_required
def delete_tag(photo_id, tag_id):
    tag = Tag.query.get_or_404(tag_id)
    photo = Photo.query.get_or_404(photo_id)
    if current_user != photo.author:
        abort(403)

    photo.tags.remove(tag)
    db.session.commit()

    if not tag.photos:
        db.session.delete(tag)
        db.session.commit()
    flash('Tag deleted.', 'info')
    return redirect(url_for('.show_photo', photo_id=photo_id))


#!/usr/bin/env python3
from setuptools import setup, find_packages
import sys
from pathlib import Path

MODULE_NAME = 'bathroom_checker'
APP_NAME = 'bathroom-checker'

desktop_script = """
[Desktop Entry]
Name={APP_NAME}
Type=Application
Exec=/usr/local/bin/{APP_NAME}
StartupNotify=false
Terminal=false
""".format(**vars())

if sys.platform == "linux":
    try:
        with open("/usr/share/applications/{APP_NAME}.desktop".format(**vars()), "w") as f:
            f.write(desktop_script)
    except Exception:
        print("Enable to create .desktop script. ¿Are you root?")

setup(
    python_requires='>=3.4',
    name=APP_NAME,
    description="Tray icon for mrmilu's bathroom-monitor",
    author='Alex Left',
    author_email='aizquierdo@mrmilu.com',
    url='',
    version='0.2',
    entry_points = {
        'console_scripts': ['{APP_NAME}={MODULE_NAME}.{MODULE_NAME}:main'.format(**vars())],
    },
    packages=[MODULE_NAME],
    package_data={MODULE_NAME: ['images/*']},
    include_package_data= True,
    license='GPL-v3',
    long_description=open('README.md').read(),
    install_requires=["pystray", "requests", "pillow", "notify2"]
)


#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Additional hypothesis-testing routines to supplement
``scipy.stats`` and ``statsmodels``.
"""
from __future__ import absolute_import
from __future__ import with_statement
from __future__ import division
from __future__ import nested_scopes
from __future__ import generators
from __future__ import unicode_literals
from __future__ import print_function

import statsmodels
import numpy as np
from numpy import random
from neurotools.util.array import find
import scipy.stats
from typing import NamedTuple

from neurotools.stats.information import betapr
def two_tailed_sampled_test(delta):
    '''
    Directly test whether a population ``delta``
    is above or below zero more than chance.
    This is for use
    with bootstrap/shuffle tests when stronger assumptions
    may be inaccurate or risk false-positives.
    '''
    delta = np.float32(delta).ravel()
    # This is very under-powered?
    if len(delta)<100:
        raise RuntimeError(
            'You probably don\'t want to use this '
            'with fewer than 100 samples.')
    k0 = sum(delta<0)
    k1 = sum(delta>0)
    if max(k0,k1)<5:
        raise RuntimeError(
            'There probably aren\'t enough samples')
    pr1gtr0 = betapr(k0,len(delta))
    pr0gtr1 = betapr(sum(delta>0),len(delta))
    pvalue  = 1-(1-min(pr1gtr0, pr0gtr1))**2
    return pvalue

class ZTestResult(NamedTuple):
    z: float
    pvalue: float
def ztest_from_moments(μ1,s1,μ2,s2):
    '''
    Calculate z-test given moments from two samples.
    '''
    Δ = μ1 - μ2
    S = np.sqrt(s1**2 + s2**2)
    z = Δ/S
    pvalue = 2*scipy.stats.norm.sf(abs(z))
    return ZTestResult(z,pvalue)

import neurotools.stats
class WeightedTtestResult(NamedTuple):
    t:float
    pvalue:float
    dof:float
    alternative:str
    sem:float
    mu:float
    s:float
def weighted_ttest_1samp(
    x,
    w,
    alternative='two-sided'):
    '''
    Test if mean of independent samples ``x``
    with weights ``w`` is different from zero using
    a to-tailed one-sample t-test.
    '''
    n   = np.sum(w) # Effective sample size
    dof = n-1       # Degrees of freedom
    μ,σ = neurotools.stats.weighted_avg_and_std(x,w)
    s   = σ*np.sqrt(n/(n-1)) # sample s.d.
    sem = s/np.sqrt(n)       # standard error
    t = μ/sem                # score
    if alternative=='two-sided':
        pvalue = scipy.stats.t(dof).sf(np.abs(t))*2
    if alternative=='greater':
        pvalue = scipy.stats.t(dof).sf(t)
    if alternative=='less':
        pvalue = scipy.stats.t(dof).cdf(t)
    return WeightedTtestResult(t,pvalue,dof,alternative,sem,μ,s)

def beta_propotion_test(
    a1,b1,
    a2,b2,
    npts = 1000,
    eps  = 0.5):
    '''
    (experimental)

    Use a Beta distribution model to determine whether
    two propotions are significantly different.

    Parameters
    ----------
    a1: positive int
        Number of items in category 0, group 1
    b1: positive int
        Number of items in category 1, group 1
    a2: positive int
        Number of items in category 0, group 2
    b2: positive int
        Number of items in category 1, group 2

    Returns
    -------
    mudelta: float
        When positive: The probability of being in the
        second category is larger for the second group,
        compared to the first.
    p: float
        Two-tailed p-value for significant diffierence
        in rates between the groups.
    '''
    from scipy.stats import beta

    # Model count data using beta distribution
    d1 = beta(eps+a1, eps+b1)
    d2 = beta(eps+a2, eps+b2)

    # Integrate to get distr. of differences
    ll = np.linspace(0,1,npts+1)
    pdelta = np.convolve(d1.pdf(ll), d2.pdf(ll)[::-1])
    pdelta /= np.sum(pdelta)

    # average difference
    l2 = np.linspace(-1,1,2*npts+1)
    mudelta = l2@pdelta

    # P value
    if mudelta>0:
        p = np.sum(pdelta[l2<0])*2
    else:
        p = np.sum(pdelta[l2>0])*2
    return mudelta,p

def cohen_d(x,y):
    '''
    Calculate Cohen's d effect-size summary for independent
    samples from two unpaired populations.
    '''
    nx = len(x)
    ny = len(y)
    dof = nx + ny - 2
    vx = np.nanvar(x, ddof=1)
    vy = np.nanvar(y, ddof=1)
    v  = ((nx-1)*vx + (ny-1)*vy) / dof
    d = (np.nanmean(x) - np.nanmean(y)) / np.sqrt(v)
    # Cohen, Jacob (1988).
    # Sawilowsky, S (2009)
    bins = [-np.inf,0.01,0.20,0.50,0.80,1.20,2.0,np.inf]
    sizenames = ['Very small',
        'Small',
        'Medium',
        'Large',
        'Very large',
        'Huge']
    i = np.digitize(abs(d),bins,right=True)-1
    return d, sizenames[i]


import pygame
from random import randrange

# O pygame usa o sistema RGB (Red, Green, Blue) para definição de cores e utiliza tupla.
branco = (255, 255, 255)
preto = (0, 0, 0,)
vermelho = (255, 0, 0)
verde = (0, 255, 0)
azul = (0, 0, 255)

try:
    pygame.init()
except:
    print('O módulo pygame não foi inicializado com sucesso!')

largura = 320
altura = 280
tamanho = 10
placar = 40
velocidade_x = 0
velocidade_y = 0

# Variável que recebe parte do comando limitador de frames
relogio = pygame.time.Clock()

# Define o tamanho da tela, sendo a larguar e o altura apresentado em uma tupla
fundo = pygame.display.set_mode((largura, altura))
# Define o nome da janela
pygame.display.set_caption('Snake')

def texto(msg, cor, tam, x, y):
    font = pygame.font.SysFont(None, tam)
    texto1 = font.render(msg, True, cor)
    fundo.blit(texto1, [x, y])

def cobra(CobraXY):
    for XY in CobraXY:
        pygame.draw.rect(fundo, preto, [XY[0], XY[1], tamanho, tamanho])

def maca(pos_x, pos_y):
    pygame.draw.rect(fundo, vermelho, [pos_x, pos_y, tamanho, tamanho])

def lerArquivo(nome):
    try:
        a = open(nome, 'rt')
    except:
        print('ERRO ao ler o arquivo!')
    else:
        c = 0
        for linha in a:
            dado = linha.split(';')
            dado[1] = dado[1].replace('\n', '')
            print(f'{dado[0]:<30}{dado[1]:>5} pontos')
            texto(dado[0], preto, 20, 10, 50 + (20 * c))
            texto(dado[1], preto, 20, 200, 50 + (20 * c))
            c += 1
    finally:
        a.close()

def cadastrar(arq, nome='desconhecido', pontuação=0):
    try:
        a = open(arq, 'at')
    except:
        print('Houve um ERRO na abertura do arquivo!')
    else:
        try:
            a.write(f'{nome};{pontuação}\n')
        except:
            print('Houve um ERRO na hora de escrever os dados!')
        else:
            print(f'Novo cadastro de {nome} com {pontuação} foi adicionado.')

def ranking():
    rankon = True
    while rankon:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
        fundo.fill(branco)
        texto('Nome', preto, 40, 10, 10)
        texto('Pontuação', preto, 40, 150, 10)
        lerArquivo('Ranking.txt')
        pygame.display.update()

def menu():
    menu_on = True
    sairranking = False
    while menu_on:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                quit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                x = pygame.mouse.get_pos()[0]
                y = pygame.mouse.get_pos()[1]
                if 90 <= x <= 230 and 100 <= y <= 150:
                    menu_on = False
                    return True
                if 90 <= x <= 230 and 160 <= y <= 210:
                    sairranking = ranking()
                    if not sairranking:
                        menu_on = False
                        return False
                if 90 <= x <= 230 and 220 <= y <= 270:
                    return False
        fundo.fill(branco)
        texto('SNAKE', preto, 80, 60, 30)
        pygame.draw.rect(fundo, preto, [90, 100, 140, 50])
        texto('Jogar', branco, 40, 120, 110)
        pygame.draw.rect(fundo, preto, [90, 160, 140, 50])
        texto('Ranking', branco, 40, 102, 170)
        pygame.draw.rect(fundo, preto, [90, 220, 140, 50])
        texto('Sair', branco, 40, 130, 232)
        pygame.display.update()

def jogo(naosair):
    sair = naosair
    fimdejogo = False
    pos_x = randrange(0, largura - tamanho, 10)
    pos_y = randrange(0, altura - tamanho - placar, 10)
    maca_x = randrange(0, largura - tamanho, 10)
    maca_y = randrange(0, altura - tamanho - placar, 10)
    velocidade_x = 0
    velocidade_y = 0
    CobraXY = []
    CobraComp = 1
    pontos = 0
    while sair:
        while fimdejogo:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    sair = False
                    fimdejogo = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_c:
                        sair = True
                        fimdejogo = False
                        pos_x = randrange(0, largura - tamanho, 10)
                        pos_y = randrange(0, altura - tamanho - placar, 10)
                        maca_x = randrange(0, largura - tamanho, 10)
                        maca_y = randrange(0, altura - tamanho - placar, 10)
                        velocidade_x = 0
                        velocidade_y = 0
                        CobraXY = []
                        CobraComp = 1
                        pontos = 0
                    if event.key == pygame.K_s:
                        sair = False
                        fimdejogo = False
                if event.type == pygame.MOUSEBUTTONDOWN:
                    x = pygame.mouse.get_pos()[0]
                    y = pygame.mouse.get_pos()[1]
                    if 45 < x < 180  and 120 < y < 147:
                        sair = True
                        fimdejogo = False
                        pos_x = randrange(0, largura - tamanho, 10)
                        pos_y = randrange(0, altura - tamanho - placar, 10)
                        maca_x = randrange(0, largura - tamanho, 10)
                        maca_y = randrange(0, altura - tamanho - placar, 10)
                        velocidade_x = 0
                        velocidade_y = 0
                        CobraXY = []
                        CobraComp = 1
                        pontos = 0
                    elif 190 < x < 265 and 120 < y < 147:
                        sair = False
                        fimdejogo = False
            fundo.fill(branco)
            texto('Fim de jogo', vermelho, 50, 65, 30)
            texto('Pontuação Final: ' + str(pontos), preto, 30, 70, 80)
            pygame.draw.rect(fundo, preto, [45, 120, 135, 27])
            texto('Continuar(C)', branco, 30, 50, 125)
            pygame.draw.rect(fundo, preto, [190, 120, 75, 27])
            texto('Sair(S)', branco, 30, 195, 125)
            pygame.display.update()
        # Identifica um comando do teclado
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sair = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT and velocidade_x != tamanho:
                    velocidade_x = -tamanho
                    velocidade_y = 0
                if event.key == pygame.K_RIGHT and velocidade_x != -tamanho:
                    velocidade_x = tamanho
                    velocidade_y = 0
                if event.key == pygame.K_UP and velocidade_y != tamanho:
                    velocidade_x = 0
                    velocidade_y = -tamanho
                if event.key == pygame.K_DOWN and velocidade_y != -tamanho:
                    velocidade_x = 0
                    velocidade_y = tamanho
        if sair:
            # Estabelece como branco o fundo
            fundo.fill(branco)
            for c in range(0, largura, 10):
                pygame.draw.rect(fundo, preto, [c, 0, 1, altura])
            for c in range(0, altura, 10):
                pygame.draw.rect(fundo, preto, [0, c, largura, 1])
            # Faz um forma quadrilátera usando a tela, cor do objeto e a posição XY e a extenção de cada eixo.
            pos_x += velocidade_x
            pos_y += velocidade_y
            if pos_x == maca_x and pos_y == maca_y:
                maca_x = randrange(0, largura - tamanho - placar, 10)
                maca_y = randrange(0, altura - tamanho - placar, 10)
                CobraComp += 1
                pontos += 1
            if pos_x + tamanho > largura:
                pos_x = 0
            if pos_x < 0:
                pos_x = largura - tamanho
            if pos_y + 10 > altura - placar:
                pos_y = 0
            if pos_y < 0:
                pos_y = altura - tamanho - placar
            # Finaliza o jogo se o usuário atingir uma das paredes
            #if pos_x + tamanho > largura:
            #    fimdejogo = True
            #if pos_x < 0:
            #    fimdejogo = True
            #if pos_y + tamanho > altura:
            #    fimdejogo = True
            #if pos_y < 0:
            #    fimdejogo = True
            CobraInicio = []
            CobraInicio.append(pos_x)
            CobraInicio.append(pos_y)
            CobraXY.append(CobraInicio)
            if len(CobraXY) > CobraComp:
                del CobraXY[0]
            for bloco in CobraXY:
                c = CobraXY.count(bloco)
                if c == 2:
                    fimdejogo = True
            pygame.draw.rect(fundo, preto, [0, altura - placar, largura, placar])
            texto('Pontuação: ' + str(pontos), branco, 20, 10, altura - 30)
            cobra(CobraXY)
            maca(maca_x, maca_y)
            # Atualiza a tela em exibição
            pygame.display.update()
            # LIMITA o loop a 15 frames por segundo
            relogio.tick(15)

escolha = menu()
jogo(escolha)
# Encerra o sistema
pygame.quit()


from collections import Counter
from typing import List

class Solution:
    def numPairsDivisibleBy60(self, time: List[int]) -> int:
        counter = Counter()
        answer = 0
        for i in range(len(time)):
            time[i] %= 60

            if 60 - time[i] in counter:
                answer += counter[60 - time[i]]

            counter[time[i]] += 1

        return answer

# Runtime: 252 ms, faster than 37.99% of Python3 online submissions for Pairs of Songs With Total Durations Divisible by 60.
# Memory Usage: 17.4 MB, less than 39.64% of Python3 online submissions for Pairs of Songs With Total Durations Divisible by 60.