from collections import namedtuple
from enum import Enum
from functools import wraps
from math import pi
from typing import Callable, Iterable, Optional, Tuple
from typing import SupportsInt
from typing import Union

from PySide2.QtCore import QTimerEvent
from PySide2.QtCore import Qt, QPoint, Signal
from PySide2.QtGui import (QBrush, QColor, QKeyEvent, QMouseEvent, QPainter, QPaintEvent,
                           QPalette, QPen, QPixmap, QPolygon, QTransform)
from PySide2.QtWidgets import QHBoxLayout, QWidget

from ._colours import get_colour
from ._constants import NO_MARGINS, Point, Size
from ._fonts import get_font, FontSpec
from ._image import Image, get_pixmap

ObjectHolder = namedtuple('ObjectHolder', ['obj_type', 'args'])

def radians_to_qpainter_angle(rads):
    # type: (float) -> int
    """Converts an angle in radians to one in 1/16ths of degrees as used by QPainter.

    :param rads: angle to covert in radians
    :return: an angle in 1/16:sup:`ths` of a degree
    """
    return int(360 * 16 * rads / (2 * pi))

def point_list_to_polygon(point_list):
    # type: (Iterable[Point]) -> QPolygon
    """Converts an iterable of (x, y) points coordinates to a QPolygon suitable for rendering.

    :param point_list: ordered iterable of points that make up the polygon
    :return: a polygon with the given points
    """
    polygon = QPolygon()
    for point in point_list:
        polygon.push_back(QPoint(*point))
    return polygon

def set_painter_line_width_and_colour(painter, line_width, line_colour):
    # type: (QPainter, int, str) -> None
    """Sets up QPainter for drawing lines.

    :param painter: the painter to be modified
    :param line_width: the new line width to set
    :param line_colour: the new line colour to set
    """
    pen = QPen(QBrush(get_colour(line_colour)), line_width)
    painter.setPen(pen)

def set_painter_fill_colour(painter, fill_colour):
    # type: (QPainter, str) -> None
    """Sets fill colour for drawing solid shapes.

    :param painter: the painter to be modified
    :param fill_colour: the new fill colour to set
    """
    painter.setBrush(QBrush(get_colour(fill_colour)))

def set_painter_lines_and_fill(painter, line_width, line_colour, fill_colour=None):
    # type: (QPainter, int, str, Optional[str]) -> None
    """Fully sets up painter for drawing.

    :param painter: the painter to be modified
    :param line_width: the new line width to set
    :param line_colour: the new line colour to set
    :param fill_colour: the new fill colour to set
    """
    set_painter_line_width_and_colour(painter, line_width, line_colour)
    if fill_colour is not None:
        set_painter_fill_colour(painter, fill_colour)

def render_line(painter, start, end, line_width, line_colour):
    # type: (QPainter, Point, Point, int, str) -> None
    """Renders a line on the canvas.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param start: the coordinate of the start point of the line
    :param end: the coordinate of the end point of the line
    :param line_width: the width of the line to draw
    :param line_colour: the colour of the line to draw
    """
    set_painter_line_width_and_colour(painter, line_width, line_colour)
    painter.drawLine(*start, *end)

def get_circle_rect(center_point, radius):
    # type: (Point, int) -> Tuple[int, int, int, int]
    """Returns the rectangle containing a circle with given centre and radius.

    :param center_point: the center of the circle
    :param radius: the radius of the circle
    :return: tuple containing (`top_left_x`, `top_left_y`, `width`, `height`) of the rectangle.
    """
    center_x, center_y = center_point
    return center_x - radius, center_y - radius, radius * 2, radius * 2

def render_arc(painter, center_point, radius, start_angle, end_angle, line_width, line_colour, fill_colour=None):
    # type: (QPainter, Point, int, int, int, int, str, Optional[str]) -> None
    """Renders an arc (filled or not) on the canvas.

    Angles are given in radians, with 0.0 at the 3 o'clock position, and values increasing clockwise.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param center_point: the center of the arc
    :param radius: the radius of the arc
    :param start_angle: the start angle of the arc
    :param end_angle: the end angle of the arc
    :param line_width: the line width to draw with
    :param line_colour: the line colour to draw with
    :param fill_colour: the colour to fill the arc with (optional, defaults to transparent)
    """
    arc_len = end_angle - start_angle
    arc_len = radians_to_qpainter_angle(arc_len)
    start_angle = radians_to_qpainter_angle(start_angle)

    rect = get_circle_rect(center_point, radius)
    set_painter_lines_and_fill(painter, line_width, line_colour, fill_colour)
    if fill_colour is None:
        painter.drawArc(*rect, start_angle, -arc_len)
    else:
        painter.drawPie(*rect, start_angle, -arc_len)

def render_circle(painter, center_point, radius, line_width, line_colour, fill_colour=None):
    # type: (QPainter, Point, int, int, str, Optional[str]) -> None
    """Renders a circle (filled or not) on the canvas.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param center_point: the center of the circle
    :param radius: the radius of the circle
    :param line_width: the line width to draw with
    :param line_colour: the line colour to draw with
    :param fill_colour: the colour to fill the circle with, optional, defaults to transparent
    """
    rect = get_circle_rect(center_point, radius)
    set_painter_lines_and_fill(painter, line_width, line_colour, fill_colour)
    painter.drawEllipse(*rect)

def render_point(painter, point, colour):
    # type: (QPainter, Point, str) -> None
    """Renders a point on the canvas.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param point: the coordinates of the point
    :param colour: the colour to draw the point
    """
    set_painter_line_width_and_colour(painter, 1, colour)
    painter.drawPoint(*point)

def render_polyline(painter, point_list, line_width, line_colour):
    # type: (QPainter, Iterable[Point], int, str) -> None
    """Renders a polyline on the canvas.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param point_list: the coordinates of the line's segment start/finish points, in order
    :param line_width: the line width to draw with
    :param line_colour: the line colour to draw with
    """
    set_painter_line_width_and_colour(painter, line_width, line_colour)
    polygon = point_list_to_polygon(point_list)
    painter.drawPolyline(polygon)

def render_polygon(painter, point_list, line_width, line_colour, fill_colour=None):
    # type: (QPainter, Iterable[Point], int, str, Optional[str]) -> None
    """Renders an optionally-filled polygon on the canvas.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param point_list: the coordinates of the polygon's vertices (the final point is always joined to the first point)
    :param line_width: the line width to draw with
    :param line_colour: the line colour to draw with
    :param fill_colour: the colour to fill the polygon with, optional, defaults to transparent
    """
    set_painter_lines_and_fill(painter, line_width, line_colour, fill_colour)
    polygon = point_list_to_polygon(point_list)
    painter.drawPolygon(polygon)

def render_text(painter, text, point, font_size, font_colour, font_face='serif'):
    # type: (QPainter, str, Point, int, str, str) -> None
    """Renders text on the canvas, positioned with its bottom-left corner at the given point.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param text: the text to draw
    :param point: the point to draw at
    :param font_size: the font size of the text
    :param font_colour: the colour of the text
    :param font_face: the font face of the text (one of `serif`, `sans-serif` or `monospace`)
    """
    set_painter_line_width_and_colour(painter, 1, font_colour)
    font = get_font(FontSpec(font_size, font_face))
    painter.setFont(font)
    painter.drawText(*point, text)

def render_image(painter, image, source_centre, source_window, canvas_center, canvas_size, rotation=0.0):
    # type: (QPainter, Image, Point, Size, Point, Size, float) -> None
    """Renders an image or portion of it on the canvas with optional rotation and scaling.

    :param painter: the painter to draw with (which knows the canvas to draw on)
    :param image: the :class:`~simplequi._image.Image` to render
    :param source_centre: the center point of the portion of the original image to render
    :param source_window: the width and height of the portion of the original image to render
    :param canvas_center: the location to render the image on the canvas
    :param canvas_size: the size to render the image on the canvas
    :param rotation: amount in radians to rotate the image by, with 0.0 at the 3 o'clock position, increasing clockwise
    """
    pixmap = get_pixmap(image, source_centre, source_window, canvas_size)
    if pixmap is None:
        # Image not loaded yet, shouldn't get here but just in case...
        return

    if rotation != 0.0:
        transform = QTransform()
        transform = transform.translate(*canvas_center)
        transform = transform.rotateRadians(rotation)
        painter.save()
        painter.setTransform(transform)
        canvas_center = 0, 0
    x, y = canvas_center
    x -= canvas_size[0] / 2.
    y -= canvas_size[1] / 2.
    painter.drawPixmap(x, y, pixmap)
    if rotation != 0.0:
        painter.restore()

class ObjectTypes(Enum):
    """Used to tell the canvas which renderer to use for an object"""

    Text = 0
    Line = 1
    Polyline = 2
    Polygon = 3
    Circle = 4
    Arc = 5
    Point = 6
    Image = 7

#: Converts an object type to render to the appropriate rendering function.
OBJECT_RENDERERS = {
    ObjectTypes.Line: render_line,
    ObjectTypes.Arc: render_arc,
    ObjectTypes.Circle: render_circle,
    ObjectTypes.Point: render_point,
    ObjectTypes.Polyline: render_polyline,
    ObjectTypes.Polygon: render_polygon,
    ObjectTypes.Text: render_text,
    ObjectTypes.Image: render_image,
}

def check_started(func):
    """Decorator that only runs enclosed the method if the object has 'started'.

    Checks whether the attribute ``started`` on the ``self`` object is `True`, then runs the method.

    :param func: function to decorate
    :return: wrapped ``func``
    """

    @wraps(func)
    def inner(inner_self, *args, **kwargs):
        if not inner_self.started:
            return
        return func(inner_self, *args, **kwargs)

    return inner

class DrawingArea(QWidget):
    """The widget that actually renders the desired canvas and handles events on it.

    :param parent: the parent QWidget of this object
    :param width: how wide to make the canvas, in pixels
    :param height: how high to make the canvas, in pixels
    """

    # Signals emitted when events handled
    mouseclick = Signal(tuple)  #: emitted when canvas is clicked, with coordinates of click
    mousedrag = Signal(tuple)  #: emitted when canvas is clicked and dragged, with coordinates of current drag end
    keydown = Signal(int)  #: emitted when canvas has focus and key is pressed, with int key value
    keyup = Signal(int)  #: emitted when canvas has focus and key is released, with int key value

    def __init__(self, parent, width, height):
        # type: (QWidget, int, int) -> None
        """Initialises a canvas with set width and height."""
        super().__init__(parent)

        # General layout
        self.__canvas_width = width
        self.__canvas_height = height
        self.setContentsMargins(NO_MARGINS)
        self.setFixedSize(width, height)

        # Background colour setup
        self.__palette = QPalette()
        self.__palette.setColor(QPalette.Base, get_colour('black'))
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.Base)
        self.setPalette(self.__palette)
        self.__background_colour = get_colour('black')
        self.__reset_pixmap()

        # Drawing stuff
        self.__canvas = Canvas(self)
        self.__objects = []
        self.__new_objects = []
        self.__draw_handler = None
        self.__draw_timer_id = None

        # Event stuff
        self.__started = False
        self.__keydown_handler = None
        self.__keyup_handler = None
        self.__mouseclick_handler = None
        self.__mousedrag_handler = None

        # Allow focus for event handling
        self.setFocusPolicy(Qt.StrongFocus)

    # Drawing
    def set_draw_handler(self, draw_handler):
        # type: (Callable[[Canvas], None]) -> None
        """Sets the draw handler and begins the rendering loop.

        :param draw_handler: function to call every 1/60:sup:`th` of a second (actually 17ms), which gets a
            :class:`Canvas` object as an argument
        """
        if self.__draw_handler is not None and self.__draw_timer_id is not None:
            self.killTimer(self.__draw_timer_id)

        self.__draw_handler = draw_handler
        if self.__started:
            self.__draw_timer_id = self.startTimer(17)  # Roughly 60FPS

    def timerEvent(self, event):
        # type: (QTimerEvent) -> None
        """Draws if the event comes from the draw timer, otherwise ignores it.

        :param event: timer event to process
        """
        if event.timerId() != self.__draw_timer_id:
            return super().timerEvent(event)
        self.__draw()

    def __reset_pixmap(self):
        """Sets new pixmap filled with the current background colour."""
        self.__pixmap = QPixmap(self.__canvas_width, self.__canvas_height)
        self.__pixmap.fill(self.__background_colour)

    @check_started
    def __draw(self):
        """Calls the draw handler and re-renders the canvas if necessary"""
        if self.__draw_handler is None:
            return

        self.__new_objects = []
        self.__draw_handler(self.__canvas)

        if self.__new_objects != self.__objects:
            self.__objects = self.__new_objects
            self.__render()

    def __render(self):
        """Actually renders the canvas"""
        self.__reset_pixmap()
        painter = QPainter(self.__pixmap)
        painter.setRenderHint(
            QPainter.RenderHint(QPainter.Antialiasing | QPainter.TextAntialiasing | QPainter.SmoothPixmapTransform))
        for obj in self.__objects:
            painter.save()
            OBJECT_RENDERERS[obj.obj_type](painter, *obj.args)
            painter.restore()
        self.update()

    def add_object(self, primitive):
        # type: (ObjectHolder) -> None
        """Adds a primitive to the draw queue.

        :param primitive: primitive described by an object holder specifying the object type and arguments
        """
        self.__new_objects.append(primitive)

    def set_background_colour(self, colour):
        # type: (QColor) -> None
        """Changes the canvas background.

        :param colour: the new background colour to use
        """
        self.__palette.setColor(QPalette.Base, colour)
        self.setPalette(self.__palette)
        self.__background_colour = colour
        self.__render()

    @check_started
    def paintEvent(self, _event):
        # type: (QPaintEvent) -> None
        """Draws cached pixmap on the canvas - :meth:`__render` takes care of creating it in the first place

        :param _event: the paint event passed in by Qt, not actually used
        """
        painter = QPainter(self)
        painter.drawPixmap(self.rect(), self.__pixmap)

    # Events
    def start(self):
        """Commences event handling and drawing"""
        self.__started = True

        # Start the draw timer if a handler exists
        if self.__draw_handler is not None:
            self.set_draw_handler(self.__draw_handler)

    @property
    def started(self):
        """Whether the frame has started drawing an event handling"""
        return self.__started

    @staticmethod
    def __connect_handlers(signal, *handlers):
        # type: (Signal, Iterable[Callable]) -> None
        """Hooks up signal to handlers.

        :param signal: the signal to connect to
        :param handlers: the functions to connect to `signal`, which should have the same argument types as the signal
        """
        try:
            signal.disconnect()
        except RuntimeError:
            # No connections
            pass
        for handler in handlers:
            signal.connect(handler)

    def set_keydown_handler(self, key_handler, controls_area_slot):
        # type: (Callable[[int], None], Callable[[int], None]) -> None
        """Adds a keyboard event handler waiting for a keydown event.

        :param key_handler: the function to call on keydown
        :param controls_area_slot: the slot to display the key pressed in the controls area
        """
        self.__connect_handlers(self.keydown, key_handler, controls_area_slot)
        self.__keydown_handler = key_handler

    def set_keyup_handler(self, key_handler, controls_area_slot):
        # type: (Callable[[int], None], Callable[[int], None]) -> None
        """Adds a keyboard event handler waiting for a keyup event.

        :param key_handler: the function to call on keyup
        :param controls_area_slot: the slot to display the key released in the controls area
        """
        self.__connect_handlers(self.keyup, key_handler, controls_area_slot)
        self.__keyup_handler = key_handler

    def set_mouseclick_handler(self, mouse_handler, controls_area_slot):
        # type: (Callable[[tuple], None], Callable[[Tuple[int, int]], None]) -> None
        """Adds a mouse event handler waiting for a mouseclick event.

        :param mouse_handler: the function to call on mouseclick
        :param controls_area_slot: the slot to display the click location in the controls area
        """
        self.__connect_handlers(self.mouseclick, mouse_handler, controls_area_slot)
        self.__mouseclick_handler = mouse_handler

    def set_mousedrag_handler(self, mouse_handler, controls_area_slot):
        # type: (Callable[[Tuple[int, int]], None], Callable[[Tuple[int, int]], None]) -> None
        """Adds a mouse event handler waiting for a mousedrag event.

        :param mouse_handler: the function to call on mousedrag
        :param controls_area_slot: the slot to display the drag location in the controls area
        """
        self.__connect_handlers(self.mousedrag, mouse_handler, controls_area_slot)
        self.__mousedrag_handler = mouse_handler

    @check_started
    def keyPressEvent(self, event):
        # type: (QKeyEvent) -> None
        self.keydown.emit(int(event.key()))
        event.accept()

    @check_started
    def keyReleaseEvent(self, event):
        # type: (QKeyEvent) -> None
        self.keyup.emit(int(event.key()))
        event.accept()

    @check_started
    def mouseReleaseEvent(self, event):
        # type: (QMouseEvent) -> None
        self.mouseclick.emit(event.pos().toTuple())
        event.accept()

    @check_started
    def mouseMoveEvent(self, event):
        # type: (QMouseEvent) -> None
        self.mousedrag.emit(event.pos().toTuple())
        event.accept()

class DrawingAreaContainer(QWidget):
    """Creates and draws a border around the actual drawing area/canvas.

    :param parent: the parent QWidget of this object
    :param width: how wide to make the canvas, in pixels
    :param height: how high to make the canvas, in pixels
    """

    def __init__(self, parent, width, height):
        # type: (QWidget, int, int) -> None
        """Initialises a canvas and border with set width and height and calculated border width"""
        super().__init__(parent)
        # Set up colouring - default is a grey border around a black background
        self.setAutoFillBackground(True)
        palette = self.palette()
        self.__background_colour = get_colour('black')
        palette.setColor(QPalette.Dark, get_colour('darkgrey'))
        palette.setColor(QPalette.Shadow, get_colour('black'))
        self.setPalette(palette)
        self.setBackgroundRole(QPalette.Dark)

        # Variable width border for widget: 1/100 * average dimension, width between 1 and 3
        border_width = min(max((width + height) // 200, 1), 3)
        self.setFixedSize(width + border_width * 2, height + border_width * 2)

        # Layout
        self.__drawing_area = DrawingArea(self, width, height)
        layout = QHBoxLayout(self)
        layout.setContentsMargins(NO_MARGINS)
        layout.addWidget(self.__drawing_area, alignment=Qt.AlignCenter)
        self.setLayout(layout)

        self.setFocusProxy(self.__drawing_area)

    @property
    def canvas(self):
        """The actual drawing area"""
        return self.__drawing_area

    def set_background_colour(self, colour):
        # type: (str) -> None
        """Changes the canvas background colour and triggers redraw of the canvas if required.

        Also calculates the border colour to draw. By default, the border uses the built-in colour defined for the
        ``QPalette.Shadow`` role (which is usually black), unless the requested background colour is black, in which
        case it uses ``QPalette.Dark``.

        :param colour: the new background colour to set
        """
        colour = get_colour(colour)
        if colour == self.__background_colour:
            return

        self.__background_colour = colour
        self.__drawing_area.set_background_colour(colour)
        border = QPalette.Dark if colour is get_colour('black') else QPalette.Shadow
        self.setBackgroundRole(border)

class Canvas:
    """Wrapper for the drawing area, implementing the codeskulptor canvas API.

    This class is passed to the ``draw_handler`` set by :meth:`~simplequi._frame.Frame.set_draw_handler` to allow calls
    to draw on the canvas to be made in draw events.  These events occur roughly 60 times per second. Note that users
    should not create instances of this class, creation is handled internally.

    :param drawing_area: the actual widget that will be drawn on
    """

    def __init__(self, drawing_area):
        # type: (DrawingArea) -> None
        """Initialises the wrapper"""
        self.__drawing_area = drawing_area

    @staticmethod
    def __ensure_int_coordinates(*coordinates):
        # type: (Union[Iterable[Point], Point]) -> Union[Iterable[Point], Point]
        """Casts point/rectangle coordinates to int.

        This is to ensure Py2 compatibility with scripts that have problems with floor division -> true division on Py3.
        If a single point is passed, returns a single point.  If more than one is passed, returns a list of points.

        :param coordinates: arbitrary number of coordinate pairs (x, y)
        :return: single point or list of points, depending on number of ``coordinates`` passed
        """
        res = []
        for coord in coordinates:
            new_coord = (int(coord[0]), int(coord[1]))
            res.append(new_coord)
        return res[0] if len(res) == 1 else res

    @staticmethod
    def __ensure_int_values(*values):
        # type: (Union[Iterable[SupportsInt], SupportsInt]) -> (Union[Iterable[SupportsInt], SupportsInt])
        """Casts values to int.

        This is to ensure Py2 compatibility with scripts that have problems with floor division -> true division on Py3.
        If a single value is passed, returns a single value.  If more than one is passed, returns a list of values.

        :param values: arbitrary number of values that can be cast to int
        :return: single value or list of values, depending on number of ``values`` passed
        """
        res = []
        for val in values:
            res.append(int(val))
        return res[0] if len(res) == 1 else res

    def draw_text(self, text, point, font_size, font_color, font_face='serif'):
        # type: (str, Point, int, str, str) -> None
        """Writes the given text string in the given font size, color, and font face.

        The point is a 2-element tuple or list of screen coordinates representing the lower-left-hand corner of where to
        write the text.  The supported font faces are 'serif' (the default), 'sans-serif', and 'monospace'.

        :param text: the text to draw
        :param point: the point to draw at
        :param font_size: the font size of the text
        :param font_color: the colour of the text
        :param font_face: the font face of the text (one of `serif`, `sans-serif` or `monospace`)
        """
        point = self.__ensure_int_coordinates(point)
        font_size = self.__ensure_int_values(font_size)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Text, (text, point, font_size, font_color, font_face)))

    def draw_line(self, point1, point2, line_width, line_color):
        # type: (Point, Point, int, str) -> None
        """Draws a line segment between the two points, each of which is a 2-element tuple or list of screen
        coordinates. The line's width is given in pixels and must be positive.

        :param point1: the coordinate of the start point of the line
        :param point2: the coordinate of the end point of the line
        :param line_width: the width of the line to draw
        :param line_color: the colour of the line to draw
        """
        point1, point2 = self.__ensure_int_coordinates(point1, point2)
        line_width = self.__ensure_int_values(line_width)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Line, (point1, point2, line_width, line_color)))

    def draw_polyline(self, point_list, line_width, line_color):
        # type: (Iterable[Point], int, str) -> None
        """Draws a sequence of line segments between each adjacent pair of points in the non-empty list.

        It is an error for the list of points to be empty. Each point is a 2-element tuple or list of screen
        coordinates. The line's width is given in pixels and must be positive.

        :param point_list: the coordinates of the line's segment start/finish points, in order
        :param line_width: the line width to draw with
        :param line_color: the line colour to draw with
        """
        point_list = self.__ensure_int_coordinates(*point_list)
        line_width = self.__ensure_int_values(line_width)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Polyline, (point_list, line_width, line_color)))

    def draw_polygon(self, point_list, line_width, line_color, fill_color=None):
        # type: (Iterable[Point], int, str, Optional[str]) -> None
        """Draws a sequence of line segments between each adjacent pair of points in the non-empty list, plus a line
        segment between the first and last points.

        It is an error for the list of points to be empty. Each point is a 2-element tuple or list of screen
        coordinates. The line's width is given in pixels, and must be positive. The fill color defaults to None. If the
        fill color is specified, then the interior of the polygon is colored.

        :param point_list: the coordinates of the polygon's vertices (the final point is always joined to the first)
        :param line_width: the line width to draw with
        :param line_color: the line colour to draw with
        :param fill_color: the colour to fill the polygon with, optional, defaults to transparent
        """
        point_list = self.__ensure_int_coordinates(*point_list)
        line_width = self.__ensure_int_values(line_width)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Polygon,
                                                    (point_list, line_width, line_color, fill_color)))

    def draw_circle(self, center_point, radius, line_width, line_color, fill_color=None):
        # type: (Point, int, int, str, Optional[str]) -> None
        """Draws a circle at the given center point having the given radius.

        The point is a 2-element tuple or list of screen coordinates. The line's width is given in pixels and must be
        positive. The fill color defaults to None. If the fill color is specified, then the interior of the circle is
        colored.

        :param center_point: the center of the circle
        :param radius: the radius of the circle
        :param line_width: the line width to draw with
        :param line_color: the line colour to draw with
        :param fill_color: the colour to fill the circle with, optional, defaults to transparent
        """
        center_point = self.__ensure_int_coordinates(center_point)
        radius, line_width = self.__ensure_int_values(radius, line_width)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Circle,
                                                    (center_point, radius, line_width, line_color, fill_color)))

    def draw_arc(self, center_point, radius, start_angle, end_angle, line_width, line_color, fill_color=None):
        # type: (Point, int, float, float, int, str, Optional[str]) -> None
        """Draws an arc at the given center point having the given radius.

        The point is a 2-element tuple or list of screen coordinates. The starting and ending angles indicate which part
        of a circle should be drawn. Angles are given in radians, clockwise starting with a zero angle at the 3 o'clock
        position. The line's width is given in pixels and must be positive. The fill color defaults to None. If the fill
        color is specified, then the interior of the circle is colored.

        :param center_point: the center of the arc
        :param radius: the radius of the arc
        :param start_angle: the start angle of the arc
        :param end_angle: the end angle of the arc
        :param line_width: the line width to draw with
        :param line_color: the line colour to draw with
        :param fill_color: the colour to fill the arc with (optional, defaults to transparent)
        """
        center_point = self.__ensure_int_coordinates(center_point)
        radius, line_width = self.__ensure_int_values(radius, line_width)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Arc,
                                                    (center_point, radius, start_angle, end_angle,
                                                     line_width, line_color, fill_color)))

    def draw_point(self, point, color):
        # type: (Point, str) -> None
        """Draws a 1×1 rectangle at the given point in the given color. The point is a 2-element tuple or list of screen
        coordinates.

        :param point: the coordinates of the point
        :param color: the colour to draw the point
        """
        point = self.__ensure_int_coordinates(point)
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Point, (point, color)))

    def draw_image(self, image, center_source, width_height_source, center_dest, width_height_dest, rotation=0.0):
        # type: (Image, Point, Size, Point, Size, float) -> None
        """Draw an image that was previously loaded by simplequi.load_image.

        center_source is a pair of coordinates giving the position of the center of the image, while center_dest is a
        pair of screen coordinates specifying where the center of the image should be drawn on the canvas.
        width_height_source is a pair of integers giving the size of the original image, while width_height_dest is a
        pair of integers giving the size of how the images should be drawn. The image can be rotated clockwise by
        rotation radians.

        You can draw the whole image file or just part of it. The source information (center_source and
        width_height_source) specifies which pixels to display. If it attempts to use any pixels outside of the actual
        file size, then no image will be drawn.

        Specifying a different width or height in the destination than in the source will rescale the image.

        :param image: the :class:`~simplequi._image.Image` to render
        :param center_source: the center point of the portion of the original image to render
        :param width_height_source: the width and height of the portion of the original image to render
        :param center_dest: the location to render the image on the canvas
        :param width_height_dest: the size to render the image on the canvas
        :param rotation: amount in radians to rotate the image, with 0.0 at the 3 o'clock position, increasing clockwise
        """
        # First check image is loaded - if not, stop here, to prevent caching of blank images
        if not image.get_height() or not image.get_width():
            return

        center_source, width_height_source, center_dest, width_height_dest = self.__ensure_int_coordinates(
            center_source, width_height_source, center_dest, width_height_dest
        )
        self.__drawing_area.add_object(ObjectHolder(ObjectTypes.Image,
                                                    (image, center_source, width_height_source,
                                                     center_dest, width_height_dest, rotation)))


import tensorflow as tf
import numpy as np
import math
from transformer import model as TransformerModel, shape_list, positional_encoding

kMaxSeqLen = 200
kMaxSubToken = 3
kMaxTarget = 3
kVocabSize = 50003

def parse_tfrecord_function(example_proto):
    features = {
        "targets":
        tf.FixedLenSequenceFeature(
            [], tf.int64, allow_missing=True, default_value=0),
        "sentence":
        tf.FixedLenSequenceFeature(
            [], tf.int64, allow_missing=True, default_value=0),
        "length":
        tf.FixedLenFeature([], tf.int64, default_value=0),
    }
    parsed_features = tf.parse_single_example(example_proto, features)
    sentence = parsed_features["sentence"]
    sentence.set_shape([kMaxSeqLen-1])
    targets = parsed_features["targets"]
    targets.set_shape([kMaxSeqLen-1])
    length = parsed_features["length"]+1
    return sentence, targets, length

def layer_norm(input_tensor, name=None):
  """Run layer normalization on the last dimension of the tensor."""
  return tf.contrib.layers.layer_norm(
      inputs=input_tensor, begin_norm_axis=-1, begin_params_axis=-1, scope=name)

def layer_norm_and_dropout(input_tensor, dropout_prob, name=None):
  """Runs layer normalization followed by dropout."""
  output_tensor = layer_norm(input_tensor, name)
  output_tensor = tf.nn.dropout(output_tensor, 1-dropout_prob)
  return output_tensor

class Model(object):
    def __init__(self,
                 embeddingSize,
                 numTags,
                 lastHiddenSize=200,
                 positionEmbeddingSize=18,
                 segEmbeddingSize=2):
        self.max_token_per_sentence = kMaxSeqLen-1
        self.pos_embedding_size = positionEmbeddingSize
        self.seg_embedding_size = segEmbeddingSize
        self.sentence_placeholder = tf.placeholder(
            tf.int32,
            shape=[None, self.max_token_per_sentence],
            name="sentence")
        self.targets = tf.placeholder(
            tf.int32, shape=[None, self.max_token_per_sentence])
        self.a_length = tf.placeholder(tf.int32, shape=[None],name="alength")
        self.totalLength = self.a_length
        self.words = tf.get_variable(
            "words", [kVocabSize, embeddingSize], initializer=tf.truncated_normal_initializer(stddev=0.02))
        self.embedding_size = embeddingSize + \
            self.pos_embedding_size + self.seg_embedding_size
        self.cls = tf.get_variable(
            "CLS", [embeddingSize], initializer=tf.truncated_normal_initializer(stddev=0.02))
        self.segs = tf.get_variable(
            "ABN", [3, self.seg_embedding_size], initializer=tf.truncated_normal_initializer(stddev=0.02))
        self.last_hidden_size = lastHiddenSize
        self.learning_rate_h = tf.placeholder(tf.float32, shape=[])
        self.dropout_h = tf.placeholder(tf.float32, shape=[], name="dropout")
        self.num_tags = numTags
        self.title_out_weight = tf.get_variable(
            "title_out_weight",
            shape=[self.embedding_size, self.num_tags],
            initializer=tf.contrib.layers.xavier_initializer())
        self.title_out_bias = tf.get_variable(
            "title_out_bias", shape=[self.num_tags])

    def length(self, data, axis=1):
        used = tf.sign(tf.abs(data))
        length = tf.reduce_sum(used, reduction_indices=axis)
        length = tf.cast(length, tf.int32)
        return length

    def inference(self, name=None):
        with tf.variable_scope("tf_inference"):
            X = self.sentence_placeholder
            seqEmds = tf.nn.embedding_lookup(self.words, X)
            shapeS = tf.shape(seqEmds)

            clsH = tf.tile(self.cls, [shapeS[0]])
            clsH = tf.reshape(clsH, [
                              shapeS[0], self.embedding_size-self.pos_embedding_size-self.seg_embedding_size])
            clsH = tf.expand_dims(clsH, axis=1)

            # now X is [Batch, kMaxSeqLen, kMaxSubToken, embedding]
            X = tf.concat([clsH, seqEmds], axis=1)
            xs = tf.zeros([shapeS[0], kMaxSeqLen])
            #[Batch, kMaxSeqLen, embedding2]
            Xpos = positional_encoding(xs, self.pos_embedding_size)

            amask = tf.sequence_mask(
                self.a_length, self.max_token_per_sentence+1, dtype=tf.int32)
            abmask = tf.sequence_mask(
                self.totalLength, self.max_token_per_sentence+1, dtype=tf.int32)
            totalmask = amask + abmask
            segE = tf.nn.embedding_lookup(self.segs, totalmask)

            X = tf.concat([X, Xpos, segE], axis=2)
            X = layer_norm_and_dropout(X, self.dropout_h)
            print("now X is: %r" % (X))
            X = TransformerModel(8, 6, X, self.dropout_h, mask=abmask)
        X = X[:,1:,:]
        output = tf.reshape(X, shape=[-1, self.embedding_size])
        output = tf.nn.xw_plus_b(
                output, self.title_out_weight, self.title_out_bias)
        unary_scores = tf.reshape(
                output, [-1, self.max_token_per_sentence, self.num_tags],
                name="finalInference")
        return unary_scores, self.totalLength-1

    def loss(self,logits,pLen):
        log_likelihood, self.transition_params = tf.contrib.crf.crf_log_likelihood(
            logits , tf.cast(self.targets, tf.int32), pLen)
        loss = tf.reduce_mean(-log_likelihood)
        return loss


from flask import Flask,jsonify,request
from flask_restx import Resource, Api, fields, reqparse
from flask_sqlalchemy import SQLAlchemy
from models import Book, db, Comment

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///book.db'
db.init_app(app)

with app.app_context():
    db.create_all()
    db.session.commit()

api = Api(app, version='1.0', title='Books API',
    description='A simple Books API',
)
ns_books = api.namespace('books', description='Books operations')

books_id_arguments = reqparse.RequestParser()
books_id_arguments.add_argument('id', type=int, required=True)

comments_id_arguments = reqparse.RequestParser()
comments_id_arguments.add_argument('id', type=int, required=True)

book_definition = api.model('Book Informations', {
    'name': fields.String(required=True),
    'author': fields.String(required=True),
    'comment_id': fields.Integer(),
    'comment': fields.String()
})

comment_definition = api.model('Comment Informations', {
    'comment': fields.String(required=True)
})

def row2dict(row):
    d = {}
    for column in row.__table__.columns:
        d[column.name] = str(getattr(row, column.name))

    return d

@ns_books.route("/")
class books(Resource):

    def get(self):
        books = Book.query.all()
        return jsonify([row2dict(book) for book in books])

    @api.expect(book_definition)
    def post(self):
        data = request.get_json()
        book = Book(data.get('name'), data.get('author'))
        db.session.add(book)
        db.session.commit()
        resp = jsonify(success=True)
        return resp

    @api.expect(books_id_arguments)
    def delete(self):
        data = books_id_arguments.parse_args(request)
        book = Book.query.get(data.get('id'))
        db.session.delete(book)
        db.session.commit()
        resp = jsonify(success=True)
        return resp

    @api.expect(books_id_arguments,book_definition)
    def put(self):
        id = books_id_arguments.parse_args(request)
        data = request.get_json()
        book = Book.query.get(id)
        book.title = data.get('name')
        book.author = data.get('author')
        db.session.commit()
        resp = jsonify(success=True)
        return resp

@ns_books.route("/comments")
class comments(Resource):

    def get(self):
        comments = Comment.query.all()
        return jsonify([row2dict(comment) for comment in comments])

    @api.expect(comment_definition)
    def post(self):
        data = request.get_json()
        comment = Comment(data.get('comment'))
        db.session.add(comment)
        db.session.commit()
        resp = jsonify(success=True)
        return resp

    @api.expect(comments_id_arguments)
    def delete(self):
        data = comments_id_arguments.parse_args(request)
        comment = Comment.query.get(data.get('id'))
        db.session.delete(comment)
        db.session.commit()
        resp = jsonify(success=True)
        return resp

    @api.expect(comments_id_arguments,comment_definition)
    def put(self):
        id = comments_id_arguments.parse_args(request)
        data = request.get_json()
        comment = Comment.query.get(id)
        comment.comment = data.get('comment')
        db.session.commit()
        resp = jsonify(success=True)
        return resp

app.run()


# Utils : mouse jiggler
# A mouse jiggler is a tool which, when plugged into a target computer, prevents it from falling asleep by moving the mouse to simulate human input.
# https://www.youtube.com/watch?v=aZ8u56I3J3I

import mouse
import time
import random

m1 = 1
m2 = 15
j = 10
base = time.monotonic()

def getRandomTime():
    return (random.randint(m1, m2))

timeInterval = getRandomTime()

while True:
    if (time.monotonic() - base) > timeInterval:
        print('Boop')
        mouse.move(j,j,absolute=False,duration=0.2)
        mouse.move(-abs(j),-abs(j),absolute=False,duration=0.2)
        base = time.monotonic()
        timeInterval = getRandomTime()
        print(f'going again in {timeInterval}')


import torch
from mmcv.runner import force_fp32, auto_fp16
from mmdet.models import DETECTORS
from mmdet3d.core import bbox3d2result
from mmdet3d.models.detectors.mvx_two_stage import MVXTwoStageDetector
from projects.mmdet3d_plugin.models.utils.grid_mask import GridMask
from projects.mmdet3d_plugin.models.utils.misc import locations

@DETECTORS.register_module()
class Petr3D(MVXTwoStageDetector):
    """Petr3D."""

    def __init__(self,
                 use_grid_mask=False,
                 pts_voxel_layer=None,
                 pts_voxel_encoder=None,
                 pts_middle_encoder=None,
                 pts_fusion_layer=None,
                 img_backbone=None,
                 pts_backbone=None,
                 img_neck=None,
                 pts_neck=None,
                 pts_bbox_head=None,
                 img_roi_head=None,
                 img_rpn_head=None,
                 train_cfg=None,
                 test_cfg=None,
                 num_frame_head_grads=2,
                 num_frame_backbone_grads=2,
                 num_frame_losses=2,
                 stride=16,
                 position_level=0,
                 aux_2d_only=True,
                 single_test=False,
                 pretrained=None):
        super(Petr3D, self).__init__(pts_voxel_layer, pts_voxel_encoder,
                             pts_middle_encoder, pts_fusion_layer,
                             img_backbone, pts_backbone, img_neck, pts_neck,
                             pts_bbox_head, img_roi_head, img_rpn_head,
                             train_cfg, test_cfg, pretrained)
        self.grid_mask = GridMask(True, True, rotate=1, offset=False, ratio=0.5, mode=1, prob=0.7)
        self.use_grid_mask = use_grid_mask
        self.prev_scene_token = None
        self.num_frame_head_grads = num_frame_head_grads
        self.num_frame_backbone_grads = num_frame_backbone_grads
        self.num_frame_losses = num_frame_losses
        self.single_test = single_test
        self.stride = stride
        self.position_level = position_level
        self.aux_2d_only = aux_2d_only

    def extract_img_feat(self, img, len_queue=1, training_mode=False):
        """Extract features of images."""
        B = img.size(0)  # batchsize 2

        if img is not None:
            if img.dim() == 6: # True
                img = img.flatten(1, 2)   # img -> (2, 6, 3, 256, 704)
            if img.dim() == 5 and img.size(0) == 1:
                img.squeeze_()
            elif img.dim() == 5 and img.size(0) > 1: # True
                B, N, C, H, W = img.size()
                img = img.reshape(B * N, C, H, W) # img -> (2*6, 3, 256, 704)
            if self.use_grid_mask: # True GridMask数据增强
                img = self.grid_mask(img)

            img_feats = self.img_backbone(img) # self.img_backbone = ResNet-50, out_stride=[16, 32]
            # img_feat -> ((12, 1024, 16, 44), (12, 2048, 8, 22))

            if isinstance(img_feats, dict):
                img_feats = list(img_feats.values())
        else:
            return None
        if self.with_img_neck: # True
            img_feats = self.img_neck(img_feats) # self.img_neck=CPFPN  projects/mmdet3d_plugin/models/necks/cp_fpn.py
            # img_feat -> ((12, 256, 16, 44), (12, 256, 8, 22))

        BN, C, H, W = img_feats[self.position_level].size() # self.position_level = 0
        if self.training or training_mode: # True
            img_feats_reshaped = img_feats[self.position_level].view(B, len_queue, int(BN/B / len_queue), C, H, W)
            # img_feats_reshaped -> (2, 1, 6, 256, 16, 44)
        else:
            img_feats_reshaped = img_feats[self.position_level].view(B, int(BN/B/len_queue), C, H, W)

        return img_feats_reshaped

    @auto_fp16(apply_to=('img'), out_fp32=True)
    def extract_feat(self, img, T, training_mode=False):
        """Extract features from images and points."""
        img_feats = self.extract_img_feat(img, T, training_mode)
        return img_feats

    def obtain_history_memory(self,
                            gt_bboxes_3d=None,
                            gt_labels_3d=None,
                            gt_bboxes=None,
                            gt_labels=None,
                            img_metas=None,
                            centers2d=None,
                            depths=None,
                            gt_bboxes_ignore=None,
                            **data):
        losses = dict()
        T = data['img'].size(1) # 1
        num_nograd_frames = T - self.num_frame_head_grads # 1-1=0
        num_grad_losses = T - self.num_frame_losses # 1-1=0
        for i in range(T):
            requires_grad = False
            return_losses = False
            data_t = dict()
            for key in data:
                data_t[key] = data[key][:, i]

            data_t['img_feats'] = data_t['img_feats']
            # 当前frame时，需要计算loss
            # num_nograd_frames来决定历史帧是否要计算loss，详细看self.forward_pts_train
            if i >= num_nograd_frames:
                requires_grad = True
            if i >= num_grad_losses:
                return_losses = True
            loss = self.forward_pts_train(gt_bboxes_3d[i],
                                        gt_labels_3d[i], gt_bboxes[i],
                                        gt_labels[i], img_metas[i], centers2d[i], depths[i], requires_grad=requires_grad,return_losses=return_losses,**data_t)
            if loss is not None:
                for key, value in loss.items():
                    losses['frame_'+str(i)+"_"+key] = value  # 记录历史帧里的loss
        return losses

    def prepare_location(self, img_metas, **data):
        pad_h, pad_w, _ = img_metas[0]['pad_shape'][0]
        bs, n = data['img_feats'].shape[:2]
        x = data['img_feats'].flatten(0, 1)
        location = locations(x, self.stride, pad_h, pad_w)[None].repeat(bs*n, 1, 1, 1)
        return location

    def forward_roi_head(self, location, **data):
        if (self.aux_2d_only and not self.training) or not self.with_img_roi_head:
            return {'topk_indexes':None}
        else:
            outs_roi = self.img_roi_head(location, **data)
            return outs_roi

    def forward_pts_train(self,
                          gt_bboxes_3d,
                          gt_labels_3d,
                          gt_bboxes,
                          gt_labels,
                          img_metas,
                          centers2d,
                          depths,
                          requires_grad=True,
                          return_losses=False,
                          **data):
        """Forward function for point cloud branch.
        Args:
            pts_feats (list[torch.Tensor]): Features of point cloud branch
            gt_bboxes_3d (list[:obj:`BaseInstance3DBoxes`]): Ground truth
                boxes for each sample.
            gt_labels_3d (list[torch.Tensor]): Ground truth labels for
                boxes of each sampole
            img_metas (list[dict]): Meta information of samples.
            gt_bboxes_ignore (list[torch.Tensor], optional): Ground truth
                boxes to be ignored. Defaults to None.
        Returns:
            dict: Losses of each branch.
        """
        location = self.prepare_location(img_metas, **data)
        # location -> (2*6, 16, 44, 2) img_feature特征图的每个位置对应的图片位置

        # 当前帧，requires_grad = True， return_losses = True
        # 历史帧，requires_grad = False， return_losses = False
        # 如果历史帧
        if not requires_grad:
            self.eval()
            with torch.no_grad():
                outs = self.pts_bbox_head(location, img_metas, None, **data)
            self.train()
        # 如果当前帧
        else:
            ################### 先用roi挑选一些感兴趣的区域
            outs_roi = self.forward_roi_head(location, **data)  # self.forward_roi_head = FocalHead  projects/mmdet3d_plugin/models/dense_heads/focal_head.py
            # feature_map = (16*44) -> 704 tokens
            # outs_roi['enc_cls_scores'] -> (2*6, 704, 10)
            # outs_roi['enc_bbox_preds'] -> (2*6, 704, 4)
            # outs_roi['pred_centers2d'] -> (2*6, 704, 2)
            # outs_roi['centerness']     -> (2*6, 704, 1)
            # outs_roi['topk_indexes']   -> (2, 6*704, 1)
            topk_indexes = outs_roi['topk_indexes']
            # 用roi中的信息来得到最后的输出
            outs = self.pts_bbox_head(location, img_metas, topk_indexes, **data)  # self.pts_bbox_head = StreamPETRHead  projects/mmdet3d_plugin/models/dense_heads/streampetr_head.py
            # len(outs) = 2,
            # outs[0]['all_cls_scores'] -> [6, 2, 900, 10]
            # outs[0]['all_bbox_preds'] -> [6, 2, 900, 10]
            # outs[0]['dn_mask_dict']   -> dict

        # 如果当前帧
        if return_losses:
            loss_inputs = [gt_bboxes_3d, gt_labels_3d, outs] # gt_bboxes_3d LiDAR-3Dbox
            losses = self.pts_bbox_head.loss(*loss_inputs) # losses = [loss_cls, loss_bbox]
            if self.with_img_roi_head: # True
                loss2d_inputs = [gt_bboxes, gt_labels, centers2d, depths, outs_roi, img_metas]
                losses2d = self.img_roi_head.loss(*loss2d_inputs)
                losses.update(losses2d)

            return losses
        # 历史帧
        else:
            return None

    # 主forward程序
    @force_fp32(apply_to=('img'))
    def forward(self, return_loss=True, **data):
        """Calls either forward_train or forward_test depending on whether
        return_loss=True.
        Note this setting will change the expected inputs. When
        `return_loss=True`, img and img_metas are single-nested (i.e.
        torch.Tensor and list[dict]), and when `resturn_loss=False`, img and
        img_metas should be double nested (i.e.  list[torch.Tensor],
        list[list[dict]]), with the outer list indicating test time
        augmentations.
        """
        if return_loss: # True
            for key in ['gt_bboxes_3d', 'gt_labels_3d', 'gt_bboxes', 'gt_labels', 'centers2d', 'depths', 'img_metas']:
                data[key] = list(zip(*data[key]))
        # LiDARInstance3DBoxes 9-dim
        #
            return self.forward_train(**data)
        else:
            return self.forward_test(**data)

    def forward_train(self,
                      img_metas=None,
                      gt_bboxes_3d=None,
                      gt_labels_3d=None,
                      gt_labels=None,
                      gt_bboxes=None,
                      gt_bboxes_ignore=None,
                      depths=None,
                      centers2d=None,
                      **data):
        """Forward training function.
        Args:
            points (list[torch.Tensor], optional): Points of each sample.
                Defaults to None.
            img_metas (list[dict], optional): Meta information of each sample.
                Defaults to None.
            gt_bboxes_3d (list[:obj:`BaseInstance3DBoxes`], optional):
                Ground truth 3D boxes. Defaults to None.
            gt_labels_3d (list[torch.Tensor], optional): Ground truth labels
                of 3D boxes. Defaults to None.
            gt_labels (list[torch.Tensor], optional): Ground truth labels
                of 2D boxes in images. Defaults to None.
            gt_bboxes (list[torch.Tensor], optional): Ground truth 2D boxes in
                images. Defaults to None.
            img (torch.Tensor optional): Images of each sample with shape
                (N, C, H, W). Defaults to None.
            proposals ([list[torch.Tensor], optional): Predicted proposals
                used for training Fast RCNN. Defaults to None.
            gt_bboxes_ignore (list[torch.Tensor], optional): Ground truth
                2D boxes in images to be ignored. Defaults to None.
        Returns:
            dict: Losses of different branches.
        """
        # data['img'] = (batch_size, 1, camera_num, channel, h, w) = (2, 1, 6, 3, 256, 704)
        T = data['img'].size(1) # 1

        prev_img = data['img'][:, :-self.num_frame_backbone_grads]   # (2, 0, 6, 3, 256, 704)
        rec_img = data['img'][:, -self.num_frame_backbone_grads:]  # (2, 1, 6, 3, 256, 704)
        rec_img_feats = self.extract_feat(rec_img, self.num_frame_backbone_grads)   # (2, 1, 6, 256, 16, 44)  stride=16
        # rec_img_feats -> (2, 1, 6, 256, 16, 44)

        ############################################ 这一步对于历史的信息进行了保存(这里不确定，应该不是这样)
        # 如果存在历史帧，就对历史帧的信息进行叠加
        if T-self.num_frame_backbone_grads > 0:  # T表示data['img']中储存了多少帧的信息，self.num_frame_backbone_grads表示需要用来损失计算的帧数（也就是当前帧）
            self.eval()
            with torch.no_grad():
                prev_img_feats = self.extract_feat(prev_img, T-self.num_frame_backbone_grads, True)
                # prev_img_feats -> ()
            self.train()
            data['img_feats'] = torch.cat([prev_img_feats, rec_img_feats], dim=1)
        # 如果只有当前帧
        else:
            data['img_feats'] = rec_img_feats

        losses = self.obtain_history_memory(gt_bboxes_3d,
                        gt_labels_3d, gt_bboxes,
                        gt_labels, img_metas, centers2d, depths, gt_bboxes_ignore, **data)

        return losses

    def forward_test(self, img_metas, rescale, **data):
        for var, name in [(img_metas, 'img_metas')]:
            if not isinstance(var, list):
                raise TypeError('{} must be a list, but got {}'.format(
                    name, type(var)))
        for key in data:
            if key != 'img':
                data[key] = data[key][0][0].unsqueeze(0)
            else:
                data[key] = data[key][0]
        return self.simple_test(img_metas[0], **data)

    def simple_test_pts(self, img_metas, **data):
        """Test function of point cloud branch."""
        location = self.prepare_location(img_metas, **data)
        outs_roi = self.forward_roi_head(location, **data)
        topk_indexes = outs_roi['topk_indexes']

        if img_metas[0]['scene_token'] != self.prev_scene_token:
            self.prev_scene_token = img_metas[0]['scene_token']
            data['prev_exists'] = data['img'].new_zeros(1)
            self.pts_bbox_head.reset_memory()
        else:
            data['prev_exists'] = data['img'].new_ones(1)

        outs = self.pts_bbox_head(location, img_metas, topk_indexes, **data)
        bbox_list = self.pts_bbox_head.get_bboxes(
            outs, img_metas)
        bbox_results = [
            bbox3d2result(bboxes, scores, labels)
            for bboxes, scores, labels in bbox_list
        ]
        return bbox_results

    def simple_test(self, img_metas, **data):
        """Test function without augmentaiton."""
        data['img_feats'] = self.extract_img_feat(data['img'], 1)

        bbox_list = [dict() for i in range(len(img_metas))]
        bbox_pts = self.simple_test_pts(
            img_metas, **data)
        for result_dict, pts_bbox in zip(bbox_list, bbox_pts):
            result_dict['pts_bbox'] = pts_bbox
        return bbox_list


import time
from contextlib import contextmanager
from dataclasses import dataclass, field
from typing import Iterator

@dataclass()
class Result:
    ns: int | None = field(default=None)

    @property
    def formatted(self) -> str:
        assert self.ns is not None
        ms = self.ns / 1e6
        return f"{ms:.2f} ms"

@contextmanager
def with_measurement() -> Iterator[Result]:
    result = Result()
    try:
        start = time.perf_counter_ns()
        yield result
    finally:
        stop = time.perf_counter_ns()
        result.ns = stop - start


# coding: utf-8
# Adapted based on https://github.com/yaoxufeng/PCL-Proxy-based-Contrastive-Learning-for-Domain-Generalization
# and https://github.com/facebookresearch/DomainBed
'''
custom loss function
'''

import math
import numpy as np

import torch
import torch.nn as nn

import torch.nn.functional as F

# Proposed losses

torch.manual_seed(0)

class ClsCLLoss(nn.Module):
    def __init__(self, num_classes, scale):
        super(ClsCLLoss, self).__init__()
        self.label = torch.LongTensor([i for i in range(num_classes)])
        self.scale = scale
        self.criterion = torch.nn.TripletMarginLoss()

    def forward(self, feature, gt_label, proxy):
        feature = F.normalize(feature, p=2, dim=1)
        proxy = F.normalize(proxy, p=2, dim=1)
        pos_pred = proxy[gt_label]
        perm = torch.randperm(pos_pred.size(0)).to(feature.device)
        neg_pred = pos_pred[perm]
        loss = self.criterion(feature, pos_pred, neg_pred)
        return loss

# # =========================  proxy Contrastive loss (baseline) ==========================
class ProxyLoss(nn.Module):
    '''
    pass
    '''

    def __init__(self, scale=1, thres=0.1):
        super(ProxyLoss, self).__init__()
        self.scale = scale
        self.thres = thres

    def forward(self, feature, pred, target):
        feature = F.normalize(feature, p=2, dim=1)  # normalize
        feature = torch.matmul(feature, feature.transpose(1, 0))  # (B, B)
        label_matrix = target.unsqueeze(1) == target.unsqueeze(0)
        feature = feature * ~label_matrix  # get negative matrix
        feature = feature.masked_fill(feature < self.thres, -np.inf)
        pred = torch.cat([pred, feature], dim=1)  # (N, C+N)

        loss = F.nll_loss(F.log_softmax(self.scale * pred, dim=1),
                          target)

        return loss

class ProxyPLoss(nn.Module):
    '''
    pass
    '''

    def __init__(self, num_classes, scale):
        super(ProxyPLoss, self).__init__()
        self.soft_plus = nn.Softplus()
        self.label = torch.LongTensor([i for i in range(num_classes)])
        self.scale = scale

    def forward(self, feature, target, proxy):
        feature = F.normalize(feature, p=2, dim=1)
        pred = F.linear(feature, F.normalize(
            proxy, p=2, dim=1))  # proxy class # (N, C)
        label = (self.label.unsqueeze(1).to(feature.device)
                 == target.unsqueeze(0))  # (C, N)
        pred = torch.masked_select(pred.transpose(1, 0), label)  # N,

        pred = pred.unsqueeze(1)  # (N, 1)

        feature = torch.matmul(feature, feature.transpose(1, 0))  # (N, N)
        label_matrix = target.unsqueeze(1) == target.unsqueeze(0)  # (N, N)

        feature = feature * ~label_matrix  # get negative matrix
        feature = feature.masked_fill(feature < 1e-6, -np.inf)  # (N, N)

        logits = torch.cat([pred, feature], dim=1)  # (N, 1+N)
        label = torch.zeros(logits.size(
            0), dtype=torch.long).to(feature.device)
        loss = F.nll_loss(F.log_softmax(self.scale * logits, dim=1), label)

        return loss


#%%
from pathlib import Path

import polars as pl

# -----------------------------------------------------------------------------
# load data sources
# -----------------------------------------------------------------------------

df_dim = pl.scan_csv(Path('./input/ee_dim_input.csv'))
df_monthly = pl.scan_csv(Path('./input/ee_monthly_input.csv'))

# -----------------------------------------------------------------------------
# helper function
# -----------------------------------------------------------------------------

def clean_ids(df: pl.DataFrame | pl.LazyFrame) -> pl.DataFrame | pl.LazyFrame:
    '''
    making sure the ids columns are non-nullable by coalescing
    the columns using a lookup table
    '''

    df = (
            df.join(df_lookup, on='guid', how='left', suffix='_right')
                  .with_columns(pl.coalesce(['employee_id_right', 'employee_id']).alias('emp_id'))
                  .drop(['employee_id_right', 'employee_id'])
                  .join(df_lookup, left_on='emp_id', right_on='employee_id', how='inner')
                  .with_columns(pl.coalesce(['guid', 'guid_right']).alias('guid'))
                  .drop('guid_right')
         )

    return df

# -----------------------------------------------------------------------------
# creating the transformations
# -----------------------------------------------------------------------------

# creating a lookup table for unique rows of emp_id and guid
df_lookup = ( pl.concat([
                            df_dim.groupby(['employee_id', 'guid']).count(),
                            df_monthly.groupby(['employee_id', 'guid']).count()
                        ])
                .filter(pl.col('employee_id').is_not_null() & pl.col('guid').is_not_null())
                .select(['employee_id', 'guid'])
                # deduplicate rows since some employees appear in both data sets
                .groupby(['employee_id', 'guid']).count()
                .drop('count')
            )

# adjusting the employee dimension table
df_dim_clean = df_dim.pipe(clean_ids)

# adjusting the monthly table
df_monthly_clean = df_monthly.pipe(clean_ids)

# -----------------------------------------------------------------------------
# output the clean outputs
# -----------------------------------------------------------------------------

df_monthly_clean.collect().write_csv(f"{Path('./output')}/monthly_clean_output_py.csv")
df_dim_clean.collect().write_csv(f"{Path('./output')}/dim_clean_output_py.csv")


def user_interface():
    first_num = int(input('Enter the first number: '))
    second_num = int(input('Enter second number: '))
    operator_choice = select_operator()
    calculator(first_num, second_num, operator_choice)

def select_operator():
    list_of_operators = ['add', 'subtract', 'multiply', 'devide']
    for i in range(4):
        print(f'{i+1}. {list_of_operators[i]}')
    operator = int(input('Select operation: '))
    if operator >= 1 and operator<= 4:
        return operator
    else:
        print('Invalid Choice!!')
        user_interface()

def calculator(num1, num2, the_operator):
    if the_operator == 1:
        print(f'{num1} + {num2} = {num1+num2}')
    elif the_operator == 2:
        print(f'{num1} - {num2} = {num1-num2}')
    elif the_operator == 3:
        print(f'{num1} * {num2} = {num1*num2}')
    else:
        print(f'{num1} / {num2} = {num1/num2}')

print('++~SIMPLE CALCULATOR~++')
user_interface()


import pyautogui
import pyttsx3 as p
import speech_recognition as sr
import randfacts
import datetime
import pywhatkit

# text to Speech
from YouTube_auto import *
from gettingIpDetails import ip_address_info
from selenium_web import *
from News import *
from Jokes import *
from Weather import *
from gettingRunningTask import *

engine = p.init()

rate = engine.getProperty('rate')
engine.setProperty('rate', 180)
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[1].id)

# engine.say("Hello world. I am being born. My name is X.... ")
# engine.say('Hi! .... WHat are you doing?.... I am Listening')
# engine.runAndWait()

def Wish_me():
    hour = int(datetime.datetime.now().hour)
    if 0 >= hour < 12:
        return "Morning !"
    elif 12 >= hour < 16:
        return "Afternoon !"
    else:
        return "Evening !"

today_date = datetime.datetime.now()

def speak(text):
    engine.say(text)
    engine.runAndWait()

def listen():
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print("I Am Listening")
        speak("Listening")
        r.energy_threshold = 10000
        r.adjust_for_ambient_noise(source, 1.2)
        audio = r.listen(source)
        try:
            print("Recognizing")
            speak("Recognizing")
            query = r.recognize_google(audio, language='en-in')
            print(query)
        except Exception as e:
            print(e)
            speak("Say that again Sir")
            return "None"
        return query

# print("Hello Sir! " + "Good " + Wish_me() + "I am your Voice Assistant....X...How Are You?")
# speak("Hello Sir! " + "Good " + Wish_me() + "I am your Voice Assistant....X...How Are You?")

def Hello():
    print("Hello Sir! " + "Good " + Wish_me() + "I am your Voice Assistant....SAM...How Are You?")
    speak("Hello Sir! " + "Good " + Wish_me() + "I am your Voice Assistant....SAM...How Are You?")
    speak("Tell me How can I help You Sir")
    # speak("Tell me How can I help You")

# if "what" and "about" and "you" in listen():
# print("I am also having a great day sir")
# speak("I am also having a great day sir")

# print("what Can I Do for You?")
# speak("what Can I Do for You?")

# with sr.Microphone() as source:
# r.energy_threshold = 10000
# r.adjust_for_ambient_noise(source, 1.2)
# print("Listening...")
# audio = r.listen(source)
# text2 = r.recognize_google(audio)

# text2 = listen()

def Take_query():
    Hello()
    # if "what" and "about" and "you" in listen():
    # print("I am also having a great day sir")
    # speak("I am also having a great day sir")
    # speak("Tell me How can I help You Sir ")
    while True:

        text2 = listen()

        # wikipedia_search
        if "information" in text2:
            speak("You need information related to which topic?")
            # with sr.Microphone() as source:
            # r.energy_threshold = 10000
            # r.adjust_for_ambient_noise(source, 1.2)
            # print("Listening...")
            # audio = r.listen(source)
            # inform = r.recognize_google(audio)
            inform = listen()
            print("Searching {} in wikipedia".format(inform))
            speak("Searching {} in wikipedia".format(inform))
            assist = infow()
            assist.get_info(inform)

        # Youtube_search
        elif "play" and "video" in text2:
            print("Sure Sir! You want  me to play which video?")
            speak("Sure Sir! You want  me to play which video?")
            # with sr.Microphone() as source:
            # r.energy_threshold = 10000
            # r.adjust_for_ambient_noise(source, 1.2)
            # print("Listening...")
            # audio = r.listen(source)
            # YInfo = r.recognize_google(audio)
            info = listen()
            print("playing {} in youtube".format(info))
            speak("playing {} in youtube".format(info))
            assist = music()
            assist.play(info)

        # news_Update
        elif "latest" and "news" in text2:
            print("sure Sir. I will read some latest news for you")
            speak("sure Sir. I will read some latest news for you")
            news_update = news()
            for i in range(len(news_update)):
                print(news_update[i])
                speak(news_update[i])

        # random_facts
        elif "fact" in text2:
            print("Sure Sir, Anything for You")
            speak("Sure Sir, Anything for You")
            fact = randfacts.getFact()
            print(fact)
            speak("Did you know that, " + fact)

        # random_jokes
        elif "joke" in text2:
            print("Sure Sir, get ready for some chuckles! ")
            speak("Sure Sir, get ready for some chuckles! ")
            jk = joke()
            print(jk[0])
            speak(jk[0])
            print(jk[1])
            speak(jk[1])

        elif "temperature" in text2:
            print("Sure Sir!")
            speak("sure Sir!")
            print(
                "Temperature in lucknow today is " + str(temperature()) + " degree Celsius with " + str(description()))
            speak(
                "Temperature in lucknow today is " + str(temperature()) + " degree Celsius with " + str(description()))

        elif "capture" and "screenshot" in text2:
            speak("sure sir! capturing the screenshot of your screen")
            screenshot = pyautogui.screenshot()
            screenshot.save('my_screenshot.png')

        # elif "flip" and "a" and "coin" in text2:
        # speak("Sir sir! Flipping a coin")
        # moves = ["head", "tails"]
        # coin_move = random.moves
        # print(" It's " + coin_move)
        # speak(" It's " + coin_move)

        elif "date" in text2:
            speak("Sure Sir!")
            print("Today is " + today_date.strftime("%d") + " of " + today_date.strftime(
                "%B") + " and It's Currently is " + (
                      today_date.strftime("%I")) + ":" + (today_date.strftime("%M")) + " " + (
                      today_date.strftime("%p")))
            speak("Today is " + today_date.strftime("%d") + " of " + today_date.strftime(
                "%B") + " and It's Currently is " + (
                      today_date.strftime("%I")) + " : " + (today_date.strftime("%M")) + " " + (
                      today_date.strftime("%p")))

        elif "message" in text2:
            print("Sure Sir!")
            speak("Sure Sir!")
            speak("please state the number to which you want to send message!")
            phone_no = "+91" + listen()
            speak("Got it Sir")
            speak("Sir what message you want me to send !")
            message = listen()
            speak("Got it Sir")
            speak("Sending Msg Now")
            pywhatkit.sendwhatmsg(phone_no, message, 17, 31)
            print("Sent Successfully")
            speak("Message has been sent")

        elif "task list" in text2 or "task" in text2:
            print('Sure Sir! Showing All Running Tasks!')
            speak('Sure Sir! Showing All Running Tasks!')
            system_info()

        elif "my ip" in text2:
            print("Sure Sir! showing IP Details")
            speak("Sure Sir! showing IP Details")
            ip_address_info()

        elif "bye" in text2:
            print("Bye Bye Sir For Now... I am just one Call Away")
            speak("Bye Bye Sir For Now... I am just one Call Away")
            exit()
        else:
            speak("please Sir Be gentle to Me ")

if __name__ == '__main__':
    # main method for executing
    # the functions
    Take_query()


import numpy as np
import pandas as pd

disArray = pd.read_csv(r'C:\Users\jyang71\Desktop\LCM_Pattern_Cluster\distance.csv')
disArray = np.array(disArray)[:,1:]
kpArray = pd.read_csv(r'C:\Users\jyang71\Desktop\LCM_Pattern_Cluster\kappa.csv')
kpArray = np.array(kpArray)[:,1:]

def similarity(idx1,idx2,w):
    NormDis = disArray[idx1,idx2]/np.max(disArray)
    NormKp = kpArray[idx1,idx2]
    cost = w * NormDis - (1-w) * NormKp
    return cost


import csv
import os

from resumeScrubber import ResumeScrubber, Resume
import uicomponents as ui

class CsvResumeScrubber(ResumeScrubber):
    def __init__(self, file):
        self.file = file
        with open(file, 'rb') as csvfile:
            self.csvData = list(csv.reader(csvfile))

        with open(file, 'wb') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerows(self.csvData)

        print('which is the resume?')
        self.resumeCol = ui.getChoice(self.csvData[0], range(len(self.csvData[0])))

        print('where do labels go?')
        labelCol = ui.getChoice(self.csvData[0] + ['[ new ]'], range(len(self.csvData[0]) + 1))
        self.labelCol = labelCol

        print('is there a header row?')
        hasHeader = ui.getChoice(['yes', 'no'], [True, False])
        if(hasHeader):
            if(not self.csvData[0][labelCol]):
                self.csvData[0][labelCol] = 'label'

        resumes = [
                ResumeFromCsv(
                    row,
                    self.resumeCol,
                    labelCol,
                    self.save
                ) for row in self.csvData[hasHeader:]
                    if(labelCol >= len(row) or not row[labelCol])
            ]
        super(CsvResumeScrubber, self).__init__(resumes)

    def save(self):
        with open(self.file, 'wb') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerows(self.csvData)

class ResumeFromCsv(Resume):
    def __init__(self, row, fileInd, labelInd, save):
        self.row = row
        self.save = save
        self.labelCol = labelInd
        super(ResumeFromCsv, self).__init__(row[fileInd])

    def label(self, label):
        while(self.labelCol >= len(self.row)):
            self.row.append('')
        self.row[self.labelCol] = label
        self.save()


import cv2
imagen = cv2.imread('contorno.jpg')
#umbral = blanco y negro
grises = cv2.cvtColor(imagen,cv2.COLOR_BGR2GRAY)

u,umbral = cv2.threshold(grises,100,255,cv2.THRESH_BINARY)

contorno,jerarquia = cv2.findContours(umbral,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

cv2.drawContours(imagen,contorno,-1,(251,60,50),3)

#mostrar
cv2.imshow('imagen',imagen)
#cv2.imshow('imagen',umbral)
print(u)
#deja ver la imagen sin que se cierre
cv2.waitKey(0)
#cv2.destroyAllWindows()


import numpy as np
import scipy as sc

from scipy import integrate
from scipy.integrate import quad
from scipy.interpolate import splev, splrep
import matplotlib.pyplot as plt

from constants import rho_c, vc_min, e_sf_II, e_sf_III, chemfeedback, kboltz, mprot, yrbysec, nu_HII, nu_HeII, nu_HeIII, hPlanck, Mpcbycm
from cosmological_functions import Cosmology

def vcmin_pop3_neut():
	return vc_min

def vcmin_pop2_neut():
	return vc_min

def vcmin_pop3_ion(T, X):
	ans=np.sqrt(2.0*kboltz*T*X/mprot)*1e-05
	if (ans < vcmin_pop3_neut()):
		return vcmin_pop3_neut()
	else:
		return ans

def vcmin_pop2_ion(T, X):
	ans=np.sqrt(2.0*kboltz*T*X/mprot)*1e-05
	if (ans < vcmin_pop2_neut()):
		return vcmin_pop2_neut()
	else:
		return ans

def vc_max():
	return 1e09

class PopII_PopIII(Cosmology):

	def __init__(self, MCMC_H0, MCMC_ombh2, MCMC_omch2, sigma_8,  MCMC_ns, **kwargs):

		if kwargs['call_for_first_time']:
			pass
		else:
			self.zcoll=kwargs['zcoll']
			self.delta_c=kwargs['delta_c']
			self.logM=kwargs['logM']
			self.logsig=kwargs['logsig']
			self.vcmax=kwargs['vcmax']
			self.zmass=self.zcoll
			self.dtimedz=kwargs['dtimedz']
			self.tdyn=kwargs['tdyn']

		super(PopII_PopIII, self).__init__(MCMC_H0, MCMC_ombh2, MCMC_omch2, sigma_8, MCMC_ns, **kwargs)

	def get_sfr(self, z, QH_Q_prev_z, escfrac, dnphotdm, lumfun_integral_qso, temp_HII, electron_denisty_HII):

		dtimedz=self.dtimedz
		tdyn=self.tdyn

		MinimumVcPop2Ion, MinimumVcPop2Neut= vcmin_pop2_ion(temp_HII,electron_denisty_HII), vcmin_pop2_neut()
		MinimumVcPop3Ion, MinimumVcPop3Neut= vcmin_pop3_ion(temp_HII,electron_denisty_HII), vcmin_pop3_neut()

		dnphotdz_ion=np.zeros((3,3))
		dnphotdz_neut=np.zeros((3,3))

		prefactor=super().omega_z(z)**0.6

		dfcolldt_pop2_ion=self.mass_integral_pop2(MinimumVcPop2Ion)*(prefactor/tdyn)

		dfcolldt_pop2_neut=self.mass_integral_pop2(MinimumVcPop2Neut)*(prefactor/tdyn)

		dfcolldt_pop3_ion=self.mass_integral_pop3(MinimumVcPop3Ion)*(prefactor/tdyn)
		dfcolldt_pop3_neut=self.mass_integral_pop3(MinimumVcPop3Neut)*(prefactor/tdyn)

		mass_integral_pop2_neut=e_sf_II*dfcolldt_pop2_neut
		mass_integral_pop2_ion=e_sf_II*dfcolldt_pop2_ion
		mass_integral_pop3_neut=e_sf_III*dfcolldt_pop3_neut
		mass_integral_pop3_ion=e_sf_III*dfcolldt_pop3_ion

		sfrfactor=self.rho_b

		dnphotdz_ion[0]=[QH_Q_prev_z*dnphotdm['PopII']['HII']*mass_integral_pop2_ion*sfrfactor*escfrac['PopII']['HII']*dtimedz/yrbysec,QH_Q_prev_z*dnphotdm['PopII']['HeII']*mass_integral_pop2_ion*sfrfactor*escfrac['PopII']['HeII']*dtimedz/yrbysec,QH_Q_prev_z*dnphotdm['PopII']['HeIII']*mass_integral_pop2_ion*sfrfactor*escfrac['PopII']['HeIII']*dtimedz/yrbysec]

		dnphotdz_ion[1]=[QH_Q_prev_z*dnphotdm['PopIII']['HII']*mass_integral_pop3_ion*sfrfactor*escfrac['PopIII']['HII']*dtimedz/yrbysec, QH_Q_prev_z*dnphotdm['PopIII']['HeII']*mass_integral_pop3_ion*sfrfactor*escfrac['PopIII']['HeII']*dtimedz/yrbysec, QH_Q_prev_z*dnphotdm['PopIII']['HeIII']*mass_integral_pop3_ion*sfrfactor*escfrac['PopIII']['HeIII']*dtimedz/yrbysec]

		dnphotdz_ion[2]=[(1.0-QH_Q_prev_z)*yrbysec*10.0**18.05*(1.0-(nu_HII/nu_HeII)**1.570)*lumfun_integral_qso/(1.570*hPlanck)*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*yrbysec*10.0**18.05*((nu_HII/(nu_HeII))**1.57-(nu_HII/(nu_HeIII))**1.57)*lumfun_integral_qso/(1.57*hPlanck)*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*yrbysec*10.0**18.05*(nu_HII/(nu_HeIII))**1.57*lumfun_integral_qso/(1.57*hPlanck)*dtimedz/yrbysec]

		dnphotdz_neut[0]=[(1.0-QH_Q_prev_z)*dnphotdm['PopII']['HII']*mass_integral_pop2_neut*sfrfactor*escfrac['PopII']['HII']*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*dnphotdm['PopII']['HeII']*mass_integral_pop2_neut*sfrfactor*escfrac['PopII']['HeII']*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*dnphotdm['PopII']['HeIII']*mass_integral_pop2_neut*sfrfactor*escfrac['PopII']['HeIII']*dtimedz/yrbysec]

		dnphotdz_neut[1]=[(1.0-QH_Q_prev_z)*dnphotdm['PopIII']['HII']*mass_integral_pop3_neut*sfrfactor*escfrac['PopIII']['HII']*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*dnphotdm['PopIII']['HeII']*mass_integral_pop3_neut*sfrfactor*escfrac['PopIII']['HeII']*dtimedz/yrbysec,(1.0-QH_Q_prev_z)*dnphotdm['PopIII']['HeIII']*mass_integral_pop3_neut*sfrfactor*escfrac['PopIII']['HeIII']*dtimedz/yrbysec]

		dnphotdz_neut[2]=[QH_Q_prev_z*yrbysec*10.0**18.05*(1.0-(nu_HII/nu_HeII)**1.570)*lumfun_integral_qso/(1.570*hPlanck)*dtimedz/yrbysec, QH_Q_prev_z*yrbysec*10.0**18.05*((nu_HII/(nu_HeII))**1.57-(nu_HII/(nu_HeIII))**1.57)*lumfun_integral_qso/(1.57*hPlanck)*dtimedz/yrbysec,QH_Q_prev_z*yrbysec*10.0**18.05*(nu_HII/(nu_HeIII))**1.570*lumfun_integral_qso/(1.57*hPlanck)*dtimedz/yrbysec]

		sfr_pop2_ion=-QH_Q_prev_z*mass_integral_pop2_ion*sfrfactor

		sfr_pop2_neut=-(1.0-QH_Q_prev_z)*mass_integral_pop2_neut*sfrfactor # These values would be needed when we use 21-cm signal along with the reionization

		sfr_pop3_ion=-QH_Q_prev_z*mass_integral_pop3_ion*sfrfactor

		sfr_pop3_neut=-(1.0-QH_Q_prev_z)*mass_integral_pop3_neut*sfrfactor

		dnphotdz_H=(dnphotdz_neut[0][0]+dnphotdz_neut[1][0]+dnphotdz_neut[2][0]+dnphotdz_neut[0][1]+dnphotdz_neut[1][1]+dnphotdz_neut[2][1])+\
		(dnphotdz_ion[0][0]+dnphotdz_ion[1][0]+dnphotdz_ion[2][0]+dnphotdz_ion[0][1]+dnphotdz_ion[1][1]+dnphotdz_ion[2][1])

		dnphotdz_He=(dnphotdz_neut[0][2]+dnphotdz_neut[1][2]+dnphotdz_neut[2][2])+(dnphotdz_ion[0][2]+dnphotdz_ion[1][2]+dnphotdz_ion[2][2])

		return  dnphotdz_neut, dnphotdz_ion, dnphotdz_H, dnphotdz_He, sfr_pop2_ion, sfr_pop2_neut, sfr_pop3_ion, sfr_pop3_neut

	def get_ionflux(self, k, lambda_0, z, HII_0_prev_temp, HII_0_prev_elect, HeIII_0_prev_temp, HeIII_0_prev_elect, QH, QHe, sigma_PI, sigma_PH, dnphotdz_neut, dnphotdz_ion):

		dtimedz=self.dtimedz

		Gamma_PI=np.zeros((3,3))
		Gamma_PH=np.zeros((3,3))

		lambda_0_H=lambda_0*np.sqrt(super().xbsq(HII_0_prev_temp, z, 1.0/HII_0_prev_elect))/(1.0+z)
		lambda_0_He=lambda_0*np.sqrt(super().xbsq(HeIII_0_prev_temp,z,1.0/HeIII_0_prev_elect))/(1.0+z)

		if (1.0-QH['F_V'][k-1]>= 0.0):
			QH['mfp'][k]=QH['Q'][k-1]**(1.0/3.0)*lambda_0_H*Mpcbycm/(1.0-QH['F_V'][k-1])**(2.0/3.0)
		else:
			QH['mfp'][k]=QH['mfp'][k-1]

		if (1.0-QHe['F_V'][k-1] >= 0.0):
			QHe['mfp'][k]=QHe['Q'][k-1]**(1.0/3.0)*lambda_0_He*Mpcbycm/(1.0-QHe['F_V'][k-1])**(2.0/3.0)
		else:
			QHe['mfp'][k]=QHe['mfp'][k-1]

		Gamma_PI[0]=[QH['mfp'][k]*sigma_PI['PopII']['HII']*(1.0+z)**3*(dnphotdz_neut[0][0]+dnphotdz_ion[0][0])/(dtimedz*Mpcbycm**3), QH['mfp'][k]*sigma_PI['PopII']['HeII']*(1.0+z)**3*(dnphotdz_neut[0][1]+dnphotdz_ion[0][1])/(dtimedz*Mpcbycm**3),QHe['mfp'][k]*sigma_PI['PopII']['HeIII']*(1.0+z)**3*(dnphotdz_neut[0][2]+dnphotdz_ion[0][2])/(dtimedz*Mpcbycm**3)]

		Gamma_PI[1]=[QH['mfp'][k]*sigma_PI['PopIII']['HII']*(1.0+z)**3*(dnphotdz_neut[1][0]+dnphotdz_ion[1][0])/(dtimedz*Mpcbycm**3), QH['mfp'][k]*sigma_PI['PopIII']['HeII']*(1.0+z)**3*(dnphotdz_neut[1][1]+dnphotdz_ion[1][1])/(dtimedz*Mpcbycm**3),QHe['mfp'][k]*sigma_PI['PopIII']['HeIII']*(1.0+z)**3*(dnphotdz_neut[1][2]+dnphotdz_ion[1][2])/(dtimedz*Mpcbycm**3)]

		Gamma_PI[2]=[QH['mfp'][k]*sigma_PI['QSO']['HII']*(1.0+z)**3*(dnphotdz_neut[2][0]+dnphotdz_ion[2][0])/(dtimedz*Mpcbycm**3), QH['mfp'][k]*sigma_PI['QSO']['HeII']*(1.0+z)**3*(dnphotdz_neut[2][1]+dnphotdz_ion[2][1])/(dtimedz*Mpcbycm**3),QHe['mfp'][k]*sigma_PI['QSO']['HeIII']*(1.0+z)**3*(dnphotdz_neut[2][2]+dnphotdz_ion[2][2])/(dtimedz*Mpcbycm**3)]

		Gamma_PH[0]=[hPlanck*nu_HII*QH['mfp'][k]*sigma_PH['PopII']['HII']*(1.0+z)**3*(dnphotdz_neut[0][0]+dnphotdz_ion[0][0])/(dtimedz*Mpcbycm**3), hPlanck*nu_HeII*QH['mfp'][k]*sigma_PH['PopII']['HeII']*(1.0+z)**3*(dnphotdz_neut[0][1]+dnphotdz_ion[0][1])/(dtimedz*Mpcbycm**3), hPlanck*nu_HeIII*QHe['mfp'][k]*sigma_PH['PopII']['HeIII']*(1.0+z)**3*(dnphotdz_neut[0][2]+dnphotdz_ion[0][2])/(dtimedz*Mpcbycm**3)]

		Gamma_PH[1]=[hPlanck*nu_HII*QH['mfp'][k]*sigma_PH['PopIII']['HII']*(1.0+z)**3*(dnphotdz_neut[1][0]+dnphotdz_ion[1][0])/(dtimedz*Mpcbycm**3),hPlanck*nu_HeII*QH['mfp'][k]*sigma_PH['PopIII']['HeII']*(1.0+z)**3*(dnphotdz_neut[1][1]+dnphotdz_ion[1][1])/(dtimedz*Mpcbycm**3), hPlanck*nu_HeIII*QHe['mfp'][k]*sigma_PH['PopIII']['HeIII']*(1.0+z)**3*(dnphotdz_neut[1][2]+dnphotdz_ion[1][2])/(dtimedz*Mpcbycm**3)]

		Gamma_PH[2]=[hPlanck*nu_HII*QH['mfp'][k]*sigma_PH['QSO']['HII']*(1.0+z)**3*(dnphotdz_neut[2][0]+dnphotdz_ion[2][0])/(dtimedz*Mpcbycm**3),hPlanck*nu_HeII*QH['mfp'][k]*sigma_PH['QSO']['HeII']*(1.0+z)**3*(dnphotdz_neut[2][1]+dnphotdz_ion[2][1])/(dtimedz*Mpcbycm**3), hPlanck*nu_HeIII*QHe['mfp'][k]*sigma_PH['QSO']['HeIII']*(1.0+z)**3*(dnphotdz_neut[2][2]+dnphotdz_ion[2][2])/(dtimedz*Mpcbycm**3)]

		return Gamma_PI,Gamma_PH, QH['mfp'][k], QHe['mfp'][k]

	def mass_integral_pop2(self, vcmin):
		zmass=self.zcoll
		if (abs(vcmin-self.vcmax) < 1e-08):
			return 0.0
		elif (vcmin>self.vcmax):
			return 0.0

		mmin=self.mass_func(vcmin,self.zcoll)/self.h
		numin=self.nu_parameter(mmin)

		nu_array=np.linspace(numin,10.0,2000)
		dnu=nu_array[1]-nu_array[0]
		if (self.vcmax > 1e05):
			ans=sc.integrate.simps(self.mass_integrand_pop2(nu_array,zmass),dx=dnu)
			return ans
		else:
			mmax=self.mass_func(self.vcmax,self.zcoll)/self.h
			numax=self.nu_parameter(mmax)
			ans,err=quad(self.mass_integrand_pop2,numin,numax,args=(zmass),epsabs=1.49e-03, epsrel=1.49e-03)
			return ans

	def mass_integral_pop3(self, vcmin):
		zmass=self.zcoll
		if (abs(vcmin-self.vcmax) < 1e-08):
			return 0.0
		elif (vcmin>self.vcmax):
			return 0.0

		mmin=self.mass_func(vcmin, zmass)/self.h
		numin=self.nu_parameter(mmin)
		nu_array=np.linspace(numin,10.0,2000)
		dnu=nu_array[1]-nu_array[0]
		if (self.vcmax > 1e05):
			ans=sc.integrate.simps(self.mass_integrand_pop3(nu_array,zmass),dx=dnu)

			if ans<0.0:
				ans=0.0
		else:
			mmax=self.mass_func(self)/h
			numax=nu_parameter(self,mmax)
			ans,err=quad(self.mass_integrand_pop3,numin,numax,args=(self,zmass),epsabs=1.49e-03, epsrel=1.49e-03)
			if ans<0.0:
				ans=0.0
		return ans

	def fbbym(self,m, e_sf_III, chemfeedback ):
		if ((feedback == 0) or (feedback == 1)):
			return 1.0

	def fpop3(self, m):
		if (e_sf_III < 1e-06):
			return 0.0
		if (chemfeedback== 0):
			return 1.0
		mass_m=10.0**m
		mass_min=self.mass_func(vcmin_pop3_neut(), self.zcoll)/self.h

		factor=1e06
		mass_form=mass_m-factor
		m_form=np.log10(mass_form)
		m_min=np.log10(mass_min)
		spl=splrep(self.logM, self.logsig)
		sigma_m=splev(m,spl)
		sigma_m=10.0**sigma_m

		sigma_min=splev(m_min,spl)
		sigma_min=10.0**sigma_min

		sigma_form=splev(m_form,spl)
		sigma_form=10.0**sigma_form

		arg=np.sqrt(abs((sigma_form-sigma_m)/(sigma_min-sigma_form)))
		arg=np.where(arg<1e-05,arg,2.0*np.arctan(arg)/np.pi)
		#if self.use_21_cm:
			#return arg*(0.5+0.5*np.tanh((zcoll-z_trans)/dt_to_dz_tanh(z_trans)))
		#else:
		return arg

	def mass_integrand_pop2(self, nu, zmass):
		sig=self.delta_c/(nu*self.d(zmass))
		spl=splrep(np.sort(self.logsig),self.logM[np.argsort(self.logsig)])
		logM_new=splev(np.log10(sig),spl)
		mass_integrand_pop2_ans=self.probdist(nu)*(nu*nu-1.0)
		return mass_integrand_pop2_ans*(1.0-self.fpop3(logM_new))

	def mass_integrand_pop3(self, nu,zmass):
		sig=self.delta_c/(nu*self.d(zmass))
		spl=splrep(np.sort(self.logsig),self.logM[np.argsort(self.logsig)])
		logM_new=splev(np.log10(sig),spl)
		mass_integrand_pop3_ans=self.probdist(nu)*(nu*nu-1.0)
		return mass_integrand_pop3_ans*self.fpop3(logM_new)

	def nu_parameter(self,mass):
		rho_0=rho_c*self.omega_m*self.h**2
		x=(3.0*mass/(4.0*np.pi*rho_0))**(1.0/3.0)
		return self.delta_c/(self.sigma_sourav(x)*self.d(self.zcoll))

	def delvir(self):
		x=self.omega_z(self.zcoll)-1.0
		return 18.0*np.pi**2+82.0*x-39.0*x*x

	def coeff_vc_mass(self):
		hsq=1.0/(self.Hubble_dist(self.zcoll))**2
		return (3.504/(1e08**(1.0/3.0)))*(0.50*hsq*self.delvir())**(1.0/6.0)*(1.0 - (2.0*self.omega_l)/(3.0*hsq*self.delvir()))

	def mass_func(self,vc, zcoll):
		return (vc/self.coeff_vc_mass())**3

	def vc(self,mass,zcoll):
		return self.coeff_vc_mass()*mass**(1.0/3.0)