"""Edit Colors dialog."""

from typing import Any, Callable

from rich.segment import Segment
from rich.style import Style
from textual import events
from textual.color import Color as Color
from textual.containers import Container, Horizontal, Vertical
from textual.css.query import NoMatches
from textual.geometry import Offset
from textual.message import Message
from textual.reactive import reactive, var
from textual.strip import Strip
from textual.widget import Widget
from textual.widgets import Button, Input, Label

from textual_paint.args import args
from textual_paint.localization.i18n import get as _
from textual_paint.windows import DialogWindow

# https://github.com/kouzhudong/win2k/blob/ce6323f76d5cd7d136b74427dad8f94ee4c389d2/trunk/private/shell/win16/comdlg/color.c#L38-L43
# These are a fallback in case colors are not received from some driver.
# const default_basic_colors = [
# 	"#8080FF", "#80FFFF", "#80FF80", "#80FF00", "#FFFF80", "#FF8000", "#C080FF", "#FF80FF",
# 	"#0000FF", "#00FFFF", "#00FF80", "#40FF00", "#FFFF00", "#C08000", "#C08080", "#FF00FF",
# 	"#404080", "#4080FF", "#00FF00", "#808000", "#804000", "#FF8080", "#400080", "#8000FF",
# 	"#000080", "#0080FF", "#008000", "#408000", "#FF0000", "#A00000", "#800080", "#FF0080",
# 	"#000040", "#004080", "#004000", "#404000", "#800000", "#400000", "#400040", "#800040",
# 	"#000000", "#008080", "#408080", "#808080", "#808040", "#C0C0C0", "#400040", "#FFFFFF",
# ];
# Grabbed with Color Cop from the screen with Windows 98 SE running in VMWare
basic_colors = [
	"#FF8080", "#FFFF80", "#80FF80", "#00FF80", "#80FFFF", "#0080FF", "#FF80C0", "#FF80FF",
	"#FF0000", "#FFFF00", "#80FF00", "#00FF40", "#00FFFF", "#0080C0", "#8080C0", "#FF00FF",
	"#804040", "#FF8040", "#00FF00", "#008080", "#004080", "#8080FF", "#800040", "#FF0080",
	"#800000", "#FF8000", "#008000", "#008040", "#0000FF", "#0000A0", "#800080", "#8000FF",
	"#400000", "#804000", "#004000", "#004040", "#000080", "#000040", "#400040", "#400080",
	"#000000", "#808000", "#808040", "#808080", "#408080", "#C0C0C0", "#400040", "#FFFFFF",
]
custom_colors = [
	"#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF",
	"#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF", "#FFFFFF",
]

num_colors_per_row = 8

class ColorGrid(Container):
    """A grid of colors."""

    class Changed(Message):
        """A message that is sent when the selected color changes."""

        def __init__(self, color: str, color_grid: "ColorGrid", index: int) -> None:
            """Initialize the message."""
            super().__init__()
            self.color = color
            self.color_grid = color_grid
            self.index = index

    color_list: var[list[str]] = var(list[str], init=False)
    """The list of colors to display. NOT TO BE CONFUSED WITH `colors` defined by `Widget`."""

    def __init__(self, color_list: list[str], selected_color: str, **kwargs: Any) -> None:
        """Initialize the ColorGrid."""
        super().__init__(**kwargs)
        self.selected_color: str = selected_color
        self._color_by_button: dict[Button, str] = {}
        self.color_list = color_list  # This immediately calls `watch_color_list`.
        self.can_focus = True

    def on_mount(self) -> None:
        """Called when the window is mounted."""
        found_match = False
        for button, color in self._color_by_button.items():
            matches = Color.parse(color) == Color.parse(self.selected_color)
            if matches and not found_match:
                button.add_class("focused")
                found_match = True
        self._select_focused_color()

    def watch_color_list(self, color_list: list[str]) -> None:
        """Called when the color list changes."""
        self._color_by_button = {}
        for button in self.query(Button):
            button.remove()
        for color in self.color_list:
            button = Button("", classes="color_button color_well")
            button.styles.background = color
            button.can_focus = False  # using fake focus for now
            self._color_by_button[button] = color
            self.mount(button)

    def on_key(self, event: events.Key) -> None:
        """Called when a key is pressed."""
        if event.key == "up":
            self._navigate_relative(-num_colors_per_row)
        elif event.key == "down":
            self._navigate_relative(+num_colors_per_row)
        elif event.key == "left":
            self._navigate_relative(-1)
        elif event.key == "right":
            self._navigate_relative(+1)
        elif event.key == "home":
            self._navigate_absolute(0)
        elif event.key == "end":
            self._navigate_absolute(len(self.color_list) - 1)
        elif event.key in ("space", "enter"):
            self._select_focused_color()

    def _select_focused_color(self) -> None:
        try:
            focused = self.query_one(".focused", Button)
        except NoMatches:
            return
        for selected in self.query(".selected"):
            selected.remove_class("selected")
        focused.add_class("selected")
        self.selected_color = self._color_by_button[focused]
        index = list(self._color_by_button.keys()).index(focused)
        self.post_message(self.Changed(self.selected_color, self, index))

    def _navigate_relative(self, delta: int) -> None:
        """Navigate to a color relative to the currently focused color."""
        try:
            focused = self.query_one(".focused", Button)
        except NoMatches:
            return
        # index = self._colors.index(self._color_by_button[focused]) # doesn't work because there can be duplicates
        index = list(self._color_by_button.keys()).index(focused)
        # print(delta, (index % num_colors_per_row), num_colors_per_row)
        if delta == -1 and (index % num_colors_per_row) == 0:
            return
        if delta == +1 and (index % num_colors_per_row) == num_colors_per_row - 1:
            return
        self._navigate_absolute(index + delta)

    def _navigate_absolute(self, index: int) -> None:
        """Navigate to the color at the given index."""
        if index < 0 or index >= len(self.color_list):
            return
        target_button = list(self._color_by_button.keys())[index]
        target_button.add_class("focused")
        for button in self._color_by_button:
            button.remove_class("focused")
        target_button.add_class("focused")

    def on_button_pressed(self, event: Button.Pressed) -> None:
        """Called when a button is clicked or activated with the keyboard."""
        self.selected_color = self._color_by_button[event.button]
        for button in self._color_by_button:
            button.remove_class("focused")
        event.button.add_class("focused")
        self._select_focused_color()
        self.focus()

    # I want MouseDown rather than Pressed in order to implement double-clicking.
    # However, event.control is None for mouse events, so this doesn't work:
    # def on_mouse_down(self, event: events.MouseDown) -> None:
    #     """Called when the mouse is pressed down."""
    #     if event.button == 1:
    #         self.selected_color = self._color_by_button[event.control]
    #         self.refresh()
    # @on(events.MouseDown, ".color_button")
    # def handle_color_button(self, event: events.MouseDown) -> None:
    #     """Called when a color button is clicked."""
    #     self.selected_color = self._color_by_button[event.control]

class LuminosityRamp(Widget):
    """A vertical slider to select a luminosity, previewing the color at each luminosity with a gradient."""

    class Changed(Message):
        """A message that is sent when the luminosity changes."""

        def __init__(self, luminosity: float) -> None:
            """Initialize the Changed message."""
            super().__init__()
            self.luminosity = luminosity

    hue = reactive(0.0)
    saturation = reactive(0.0)
    luminosity = reactive(0.0)

    def __init__(self, hue: float, saturation: float, luminosity: float, **kwargs: Any) -> None:
        """Initialize the LuminosityRamp."""
        super().__init__(**kwargs)
        self.luminosity = luminosity
        self.hue = hue
        self.saturation = saturation
        self._mouse_down = False

    def render_line(self, y: int) -> Strip:
        """Render a line of the widget."""
        marker = "<" if args.ascii_only else "◀" # ◀ (bigger/clearer) or 🢐 (closer shape but smaller)
        # lum = y / self.size.height # bottom isn't quite white
        lum = 1 - y / (self.size.height - 1)
        color = Color.from_hsl(self.hue, self.saturation, lum)
        style = Style(bgcolor=color.rich_color)
        segments = [Segment(" " * (self.size.width - 1), style, None)]
        if y == round((self.size.height - 1) * (1 - self.luminosity)):
            segments.append(Segment(marker, Style(color="black"), None))
        return Strip(segments)

    def on_mouse_down(self, event: events.MouseDown) -> None:
        """Called when the mouse is pressed down."""
        self._update_color(event.y)
        self._mouse_down = True
        self.capture_mouse()

    def on_mouse_up(self, event: events.MouseUp) -> None:
        """Called when the mouse is released."""
        self.release_mouse()
        self._mouse_down = False

    def on_mouse_move(self, event: events.MouseMove) -> None:
        """Called when the mouse is moved."""
        if self._mouse_down:
            self._update_color(event.y)

    def _update_color(self, y: int) -> None:
        """Update the color based on the given y coordinate."""
        self.luminosity = max(0, min(1, 1 - y / (self.size.height - 1)))
        self.post_message(self.Changed(luminosity=self.luminosity))
        # self.refresh()

class ColorField(Widget):
    """A field of hue and saturation, where you can pick a color by clicking."""

    class Changed(Message):
        """A message that is sent when the color changes."""

        def __init__(self, hue: float, saturation: float) -> None:
            """Initialize the Changed message."""
            super().__init__()
            self.hue = hue
            self.saturation = saturation

    hue = reactive(0.0)
    saturation = reactive(0.0)

    def __init__(self, hue: float, saturation: float, **kwargs: Any) -> None:
        """Initialize the ColorField."""
        super().__init__(**kwargs)
        self.hue = hue
        self.saturation = saturation
        self._mouse_down = False

    def render_line(self, y: int) -> Strip:
        """Render a line of the widget."""
        segments: list[Segment] = []
        crosshair = "+" if args.ascii_only else "✜" # Options: ┼+✜✛⊹✚╋╬⁘⁛⌖⯐ or
        #  ╻
        # ╺ ╸
        #  ╹
        for x in range(self.size.width):
            crosshair_here = (
                x == round(self.hue * (self.size.width - 1)) and
                y == round((self.size.height - 1) * (1 - self.saturation))
            )
            color = Color.from_hsl(x / (self.size.width - 1), 1 - y / (self.size.height - 1), 0.5)
            char = crosshair if crosshair_here else " "
            segments.append(Segment(char, Style(color="black", bgcolor=color.rich_color), None))
        return Strip(segments)

    def on_mouse_down(self, event: events.MouseDown) -> None:
        """Called when the mouse is pressed down."""
        self._update_color(event.offset)
        self._mouse_down = True
        self.capture_mouse()

    def on_mouse_up(self, event: events.MouseUp) -> None:
        """Called when the mouse is released."""
        self.release_mouse()
        self._mouse_down = False

    def on_mouse_move(self, event: events.MouseMove) -> None:
        """Called when the mouse is moved."""
        if self._mouse_down:
            self._update_color(event.offset)

    def _update_color(self, offset: Offset) -> None:
        """Update the color based on the given offset."""
        x, y = offset
        self.hue = max(0, min(1, x / (self.size.width - 1)))
        self.saturation = max(0, min(1, 1 - y / (self.size.height - 1)))
        self.post_message(self.Changed(hue=self.hue, saturation=self.saturation))
        # self.refresh()

class ColorPreview(Widget):
    """A preview of the selected color. This doesn't really need to be a special widget..."""

    color: reactive[str] = reactive("black")

    def __init__(self, color: str, **kwargs: Any) -> None:
        """Initialize the ColorPreview."""
        super().__init__(**kwargs)
        self.color = color

    def render_line(self, y: int) -> Strip:
        """Render a line of the widget."""
        return Strip([Segment(" " * self.size.width, Style(bgcolor=self.color), None)])

class IntegerInput(Input):
    """An input that only accepts integers."""

    def __init__(self, min: int, max: int, **kwargs: Any) -> None:
        """Initialize the IntegerInput."""
        super().__init__(**kwargs)
        self.min = min
        self.max = max
        self.last_valid_int = 0

    def _track_valid_int(self, value: str) -> int:
        try:
            value_as_int = int(value)
        except ValueError:
            return self.last_valid_int
        value_as_int = max(self.min, min(self.max, value_as_int))
        self.last_valid_int = value_as_int
        return value_as_int

    def validate_value(self, value: str) -> str:
        """Validate the given value."""
        if not value:
            # Allow empty string
            return value
        return str(self._track_valid_int(value))

    def on_blur(self, event: events.Blur) -> None:
        """Called when the input loses focus. Resets the input if empty."""
        if not self.value:
            self.value = str(self.last_valid_int)

class EditColorsDialogWindow(DialogWindow):
    """A dialog window that lets the user select a color."""

    def __init__(self, *children: Widget, title: str = _("Edit Colors"), selected_color: str|None, handle_selected_color: Callable[[str], None], **kwargs: Any) -> None:
        """Initialize the Edit Colors dialog."""
        super().__init__(handle_button=self.handle_button, *children, title=title, **kwargs)
        self.hue_degrees = 0.0
        self.sat_percent = 0.0
        self.lum_percent = 0.0
        # self._initial_color = selected_color
        if selected_color:
            self._current_color = selected_color
        self._color_by_button: dict[Button, str] = {}
        self._inputs_by_letter: dict[str, IntegerInput] = {}
        self._custom_colors_index = 0
        self.handle_selected_color = handle_selected_color

    def handle_button(self, button: Button) -> None:
        """Called when a button is clicked or activated with the keyboard."""
        if button.has_class("cancel"):
            self.request_close()
        elif button.has_class("ok"):
            self.handle_selected_color(self._current_color.hex)
        elif button.has_class("add_to_custom_colors"):
            global custom_colors
            custom_colors = custom_colors[:]  # copy so that watch_color_list gets called
            # (it uses reference equality; an alternative would be to set always_update=True)
            custom_colors[self._custom_colors_index] = self._current_color.hex
            self.custom_colors_grid.color_list = custom_colors
            self._custom_colors_index = (self._custom_colors_index + 1) % len(custom_colors)

    def on_mount(self) -> None:
        """Called when the window is mounted."""
        self.basic_colors_grid = ColorGrid(basic_colors, self._current_color.hex, classes="autofocus")
        self.custom_colors_grid = ColorGrid(custom_colors, self._current_color.hex)
        verticals_for_inputs: list[Vertical] = []
        for color_model in ["hsl", "rgb"]:
            input_containers: list[Container] = []
            for component_letter in color_model:
                text_with_hotkey: str = {
                    "h": _("Hu&e:"),
                    "s": _("&Sat:"),
                    "l": _("&Lum:"),
                    "r": _("&Red:"),
                    "g": _("&Green:"),
                    "b": _("Bl&ue:"),
                }[component_letter]
                text_without_hotkey = text_with_hotkey.replace("&", "")
                max_value: int = {
                    "h": 360,
                    "s": 100,
                    "l": 100,
                    "r": 255,
                    "g": 255,
                    "b": 255,
                }[component_letter]
                min_value: int = 0
                input = IntegerInput(min_value, max_value, name=component_letter)
                label = Label(text_without_hotkey)
                container = Container(label, input, classes="input_container")
                input_containers.append(container)
                self._inputs_by_letter[component_letter] = input
            verticals_for_inputs.append(Vertical(*input_containers))

        self.content.mount(
            Horizontal(
                Vertical(
                    Label(_("Basic Colors")),
                    self.basic_colors_grid,
                    Label(_("Custom Colors")),
                    self.custom_colors_grid,
                ),
                Vertical(
                    Horizontal(
                        ColorField(self.hue_degrees / 360, self.sat_percent / 100),
                        LuminosityRamp(self.hue_degrees / 360, self.sat_percent / 100, self.lum_percent / 100),
                    ),
                    Horizontal(
                        Vertical(
                            ColorPreview("black"),
                            Label(_("Color")),
                            classes="color_preview_area",
                        ),
                        *verticals_for_inputs,
                    ),
                    Button(_("Add to Custom Colors"), classes="add_to_custom_colors"),
                ),
            ),
            Container(
                Button(_("OK"), classes="ok submit", variant="primary"),
                Button(_("Cancel"), classes="cancel"),
                classes="buttons",
            )
        )

        self._update_inputs("hslrgb")

    def on_color_grid_changed(self, event: ColorGrid.Changed) -> None:
        """Called when the user selects a color from the grid."""
        self._current_color = event.color
        self._update_inputs("hslrgb")
        self._update_color_preview()
        if event.color_grid is self.custom_colors_grid:
            self._custom_colors_index = event.index
        # This is a little awkward, removing .selected for other grids here, but
        # for the clicked grid in the ColorGrid widget itself.
        for color_grid in self.query(ColorGrid):
            if event.color_grid is not color_grid:
                for button in color_grid.query(Button):
                    button.remove_class("selected")

    def on_luminosity_ramp_changed(self, event: LuminosityRamp.Changed) -> None:
        """Called when dragging the luminosity slider."""
        self.lum_percent = event.luminosity * 100
        self._update_inputs("lrgb")
        self._update_color_preview()

    def on_color_field_changed(self, event: ColorField.Changed) -> None:
        """Called when dragging the color field."""
        self.hue_degrees = event.hue * 360
        self.sat_percent = event.saturation * 100
        self._update_inputs("hsrgb")
        self._update_color_preview()

    def on_input_changed(self, event: Input.Changed) -> None:
        """Called when an input changes."""
        component_letter = event.input.name
        if component_letter is None:
            return
        assert isinstance(event.input, IntegerInput)
        value = event.input.last_valid_int
        if component_letter in "hsl":
            if component_letter == "h":
                self.hue_degrees = value
            elif component_letter == "s":
                self.sat_percent = value
            else:
                self.lum_percent = value
            self._update_inputs("rgb")
        else:
            rgb = list(self._current_color.rgb)
            rgb["rgb".index(component_letter)] = value
            self._current_color = Color(*rgb)
            self._update_inputs("hsl")
        self._update_color_preview()

    @property
    def _current_color(self) -> Color:
        """Get the current color."""
        return Color.from_hsl(self.hue_degrees / 360, self.sat_percent / 100, self.lum_percent / 100)

    @_current_color.setter
    def _current_color(self, color: Color | str) -> None:
        """Set the color values from the given textual.color.Color object or string."""
        if isinstance(color, str):
            color = Color.parse(color)
        hue, sat, lum = color.hsl
        self.hue_degrees = hue * 360
        self.sat_percent = sat * 100
        self.lum_percent = lum * 100

    def _update_inputs(self, component_letters: str) -> None:
        """Update the inputs for the given component letters."""
        with self.prevent(Input.Changed):
            for component_letter in component_letters:
                input = self._inputs_by_letter[component_letter]
                input.value = str(int({
                    "h": self.hue_degrees,
                    "s": self.sat_percent,
                    "l": self.lum_percent,
                    "r": self._current_color.rgb[0],
                    "g": self._current_color.rgb[1],
                    "b": self._current_color.rgb[2],
                }[component_letter]))

    def _update_color_preview(self) -> None:
        """Update the color preview."""
        self.query_one(ColorPreview).color = self._current_color.hex
        luminosity_ramp = self.query_one(LuminosityRamp)
        color_field = self.query_one(ColorField)
        luminosity_ramp.luminosity = self.lum_percent / 100
        luminosity_ramp.hue = self.hue_degrees / 360
        luminosity_ramp.saturation = self.sat_percent / 100
        color_field.hue = self.hue_degrees / 360
        color_field.saturation = self.sat_percent / 100


"""
    Query Set for Resource Object
"""
import hashlib
from sqlalchemy import or_, and_
from sqlalchemy.orm import Session
from sqlalchemy.sql.sqltypes import Boolean
from api.db import models
from api.db.models import Variation, Workbook, User
from api.db.helpers import clone

def row2dict(row):
    """
        Convert object row from database into python dict

        parameters:
        ----
            row: row of query result
    """
    data = {}
    for column in row.__table__.columns:
        data[column.name] = str(getattr(row, column.name))
    return data

def get_entity(database: Session, input_id: int, table):
    """
        Get Resource from table by ID
    """
    return database.query(table).filter(table.id == input_id).first()

def get_entities_by_name(database: Session, name: str, technology: str, table, user: models.User):
    """
        Get multiple Resource from table by name
    """
    return database.query(table) \
        .filter(and_(table.technology == technology, table.name == name, or_(table.is_public, table.author_id.is_(None), table.author_id == user.id))) \
        .all()

def get_entity_by_name(database: Session, name: str, technology: str, table, user: models.User):
    """
        Get first object from table by name
    """
    return database.query(table) \
        .filter(and_(table.technology == technology, table.name.like(name), or_(table.is_public, table.author_id.is_(None), table.author_id == user.id))) \
        .first()

def all_entities_by_technology(database: Session, table, technology: str, user: models.User):
    """
        Get all object from table that is default data (author_id == none),
        owned by user or has been set to true set by technology.
    """
    return database.query(table) \
        .filter(and_(table.technology == technology, or_(table.is_public, table.author_id.is_(None), table.author_id == user.id))) \
        .all()

def all_entities(database: Session, table, user: models.User):
    """
        Get all object from table that is default data (author_id == none),
        owned by user or has been set to true.
    """
    return database.query(table) \
        .filter(or_(table.is_public, table.author_id.is_(None), table.author_id == user.id)) \
        .all()

def all_entity_paths(database: Session, _, table):
    """
        Get all objects path from table
    """
    return list(map(lambda r: r.path, database.query(table).all()))

def clone_variation(database: Session, input_id: int):
    """
        Get Variation object from database and create local clone
    """
    cloned = clone(database, database.query(Variation).filter(Variation.id == input_id))
    return cloned

def save_variation(database: Session, variation: Variation):
    """
        Save Variation object to database
    """
    database.add(variation)
    database.commit()
    database.refresh(variation)
    return variation

def save_entity(database: Session, name: str, technology: str, obj, table, user: User = None):
    """
        Save object to database. When data is saved by a user, it will
        generate a hashed name to prevent same name issues

        parameters:
        ---
            database: Session
                database session
            name: str
                primary name key
            obj: dict
                modified data
            table: Models
                object model
            ref_name: str
                randomized name for reference to prevent multiple result
                when querying resource
    """
    ref_name = None

    if user:
        ref_name = hashlib.md5((str(user.id) + name).encode('utf-8')).hexdigest()

    db_obj = table(
        name=name,
        technology=technology,
        ref_name=ref_name,
        author=user,
        data=obj)

    database.add(db_obj)
    database.commit()
    database.refresh(db_obj)
    return db_obj

def publish_entity(database: Session, table, input_id: int, user: User, is_public: Boolean):
    """
        publish an entity to make it publically accessable

        parameters:
        ---
            database: Session
                database session
            table: Models
                object model
            input_id: int
                primary id key
            user: User
                logged in user
            is_public: bool
                set to publish/unpublish entity
    """
    entity = database.query(table).filter(table.id == input_id, table.author_id == user.id).one()
    entity.is_public = is_public
    database.commit()

def delete_unused_variations(database: Session):
    """
        Delete unused Variation objects (NOT FUNCTIONAL)
    """
    # would be nice to do in the db but this doesn't work because path is a hybrid property
    # unused_vars = db.query(Variation).join(Workbook, Workbook.variations.contains([Variation.path])).all()

    all_workbooks = database.query(Workbook).all()
    all_variations = database.query(Variation).all()
    for variation in all_variations:
        found = False
        for workbook in all_workbooks:
            if variation.path in workbook.variations:
                found = True
                if not found:
                    database.delete(variation)
    database.commit()

def clear_all_tables(database: Session):
    """
        clear all data in the database
    """
    for model in [
            models.VMA,
            models.TAM,
            models.AdoptionData,
            models.CustomAdoptionPDS,
            models.CustomAdoptionRef,
            models.Scenario,
            models.Reference,
            models.Variation,
            models.Workbook,
            models.VMA_CSV
        ]:
        database.query(model).delete()
    database.commit()


with open("cv-A.txt") as f:
    contents = f.readlines()

contents = [x.strip() for x in contents]
for content in contents:
    tmp = content.split(' --- ')
    last_name = tmp[0].split(' ')[0]
    first_name = tmp[0].split(' ')[1]
    start_year = ''
    end_year = ''
    if tmp[1]:
        year_tmp = tmp[1].split('-')
        start_year = year_tmp[0]
        if len(year_tmp) > 1 and year_tmp[1].isdigit():
            end_year = year_tmp[1]
    title = tmp[2]
    info = []
    info_id = []
    location = []
    location_id = []
    for pair in tmp[3:]:
        pair_split = pair.split(':')
        if pair_split[0][0] == '9':
            location_id.append(pair_split[0])
            location.append(pair_split[1])
        else:
            info_id.append(pair_split[0])
            info.append(pair_split[1])
    # tmp_final += id
    tmp_final = ''
    tmp_final = "'" + last_name + "','" + first_name + "','" + start_year + "','" + end_year + "','" + title + "','"
    for i, i_id in zip(info, info_id):
        tmp_final += i + "','" + i_id + "','"
    for loc, loc_id in zip(location, location_id):
        tmp_final += loc + "','" + loc_id + "','"
    if tmp_final[-2:] == ",'":
        tmp_final = tmp_final[:-2]
    tmp_final = '''"""''' + tmp_final + '''"""'''
    print(tmp_final)


from langsplit import splitter

from django.db import models
from django.contrib.postgres.fields import ArrayField
import django.db.models.options as options

options.DEFAULT_NAMES = options.DEFAULT_NAMES + ('translation_fields',)

class GenericManager(models.Manager):
    pass

class GenericModel(models.Model):
    created_date = models.DateTimeField(auto_now_add=True)
    updated_date = models.DateTimeField(auto_now=True)

    objects = GenericManager()

    class Meta:
        abstract = True

class GenericTranslationModel(GenericModel):
    languages = ArrayField(models.CharField(max_length=2), blank=True)

    def save(self, *args, **kwargs):
        translation_fields = getattr(self._meta, 'translation_fields', ())
        lang_keys = []
        for translation_field, translation_title in translation_fields:
            try:
                field_data = splitter.split(
                    getattr(self, translation_field),
                    title=translation_title
                )
                if field_data:
                    setattr(
                        self,
                        translation_field,
                        splitter.convert(
                            field_data,
                            title=translation_title
                        ).strip()
                    )
                    lang_keys.append(field_data.keys())
            except Exception:
                pass

        if not lang_keys:
            self.languages = []
        else:
            self.languages = list(
                frozenset.intersection(
                    *[frozenset(key_pair) for key_pair in lang_keys]
                )
            )
        return super().save(*args, **kwargs)

    class Meta:
        abstract = True


from .base import GnuRecipe
from ..urls import Urls

class GperfRecipe(GnuRecipe):
    def __init__(self, *args, **kwargs):
        super(GperfRecipe, self).__init__(*args, **kwargs)
        self.sha256 = '588546b945bba4b70b6a3a616e80b4ab' \
                      '466e3f33024a352fc2198112cdbb3ae2'

        self.name = 'gperf'
        self.version = '3.1'
        self.url = Urls.gnu_template(self.name, self.version)


# Plot DMN and Rew networks on glass brain

# use python >= 3.8

from nilearn import plotting
from os import path
import pandas as pd

def main():

    OUT_DIR = "/data/pt_02161/Analysis/Project2_resting_state/seed-based/Second_level /code_and_manuscript/report/fig"
    INFO_FILE = "/data/pt_02161/Results/Project2_resting_state/connectivity/Analysis/noExclFD/result_report.txt"

    # dataframe with filenames, path list, cluster threshold
    df = pd.read_table(INFO_FILE, header=None)
    df[1] = df[1].str.replace('txt', 'nii') # root_paths  = df[1].tolist()

    for i in range(len(df[1])):
        IN_FILE = df[1][i]
        OUT_FILE = path.join(OUT_DIR, (df[0][i] + ".pdf"))

        # do for each result
        #print(f'Processing:\n- in:  {IN_FILE}\n- out: {OUT_FILE}')
        plot(IN_FILE, OUT_FILE)

    print('done!')

def plot(infile: str, outfile: str):
    plotting.plot_glass_brain(
        stat_map_img=infile,
        output_file=outfile,
        display_mode='ortho',
        colorbar=True,
        figure=None,
        axes=None,
        title=None,
        threshold='auto',
        annotate=True,
        black_bg=False,
        cmap='plasma',
        alpha=0.7,
        vmin=None,
        vmax=None,
        plot_abs=True,
        symmetric_cbar='auto',
        resampling_interpolation='continuous')

if __name__ == '__main__':
    main()


from SearchProblems.utils import *
from SearchProblems.uninformed_search import breadth_first_graph_search
from SearchProblems.informed_search import astar_search

class Explorer(Problem):

    def __init__(self, initial, goal=None):
        super().__init__(initial, goal)

    def successor(self, state):
        successors = dict()
        x = state[0]
        y = state[1]
        obstacle1 = list(state[2])
        obstacle2 = list(state[3])

        gridX = 8
        gridY =6

        #da ja pridvizime 1 precka # 0 == nadolu
        if obstacle1[2] == 0:
            if obstacle1[1] == 0:
                obstacle1[2] = 1
                obstacle1[1] +=1
            else:
                obstacle1[1] -=1
        else:
            if obstacle1[1] == gridY-1:
                obstacle1[2] = 0
                obstacle1[1] -=1
            else:
                obstacle1[1] +=1

        #da ja pridvizime 2 precka # 0 == nadolu
        if obstacle2[2] == 0:
            if obstacle2[1] == 0:
                obstacle2[2] = 1
                obstacle2[1] +=1
            else:
                obstacle2[1] -=1
        else:
            if obstacle2[1] == gridY-1:
                obstacle2[2] = 0
                obstacle2[1] -=1
            else:
                obstacle2[1] +=1

        if x + 1 < 8 and (x+1,y) not in [obstacle1, obstacle2]:
            successors['Right'] = (x+1, y, tuple(obstacle1), tuple(obstacle2))

        if x - 1 > 0 and (x-1,y) not in [obstacle1, obstacle2]:
            successors['Left'] = (x-1, y, tuple(obstacle1), tuple(obstacle2))

        if y + 1 < 6 and (x, y+1) not in [obstacle1,obstacle2]:
            successors['Up'] = (x, y+1, tuple(obstacle1), tuple(obstacle2))

        if y - 1 > 0 and (x, y-1) not in [obstacle1, obstacle2]:
            successors['Down'] = (x, y-1, tuple(obstacle1), tuple(obstacle2))

        return successors

    def actions(self, state):
        return self.successor(state).keys()

    def result(self, state, action):
        return self.successor(state)[action]

    def goal_test(self, state):
        return state[0] == goal[0] and state[1] == goal[1]

    def h(self, node):
        x = node.state[0]
        y = node.state[1]
        h1 = self.goal[0]
        h2 = self.goal[1]

        return abs(x - h1) + abs(y - h2)

if __name__ == '__main__':
    man_x = 0
    man_y = 2
    obstacle1 = (2,5, 0)
    obstacle2 = (5,0, 1)
    goal = (7,4)

    expl = Explorer((man_x, man_y, obstacle1, obstacle2), goal)

    rez1 = breadth_first_graph_search(expl)
    print(rez1.solution())

    rez2 = astar_search(expl)
    print(rez2.solution())


#!/usr/bin/python
from gurobipy import *
from gurobifun import *

N = 128 # the words size.

# !SECTION! Modelling tinyJambu constraints
# =======================

def stateUptLin(mod, stin, stout, Ands, r):
    """ Add the simple model onstraints of a single round of TinyJambu with keeping tracks of Ands chains.
        Parameters:
        mod     - the Model
        stin    - the variables of the input state
        stout   - the variables of the output state
        And     - the And chains
        r       - the current round number
    """
    objectiveFun = []

    objectiveFun += [stin[0]]
    mod.addConstr(stout[127] == stin[0])
    j = r%15
    approx = [get_new_var(mod) for _ in xrange(2)]
    Ands[j] += [approx + [stin[0]]]
    for i in xrange(2):
        mod.addConstr(approx[i] <= stin[0]) #Fix approx to 0 when not needed.
    for i in xrange(N-1):
        if(i == 84 or i == 69):
            addGenConstrXor(mod, [stout[i], stin[i+1], approx[ 0 if i == 69 else 1 ]], False)
        elif(i == 90 or i == 46):
            addGenConstrXor(mod, [stin[0], stin[i+1], stout[i]], False)
        else:
            mod.addConstr(stout[i] == stin[i+1])
    return objectiveFun

def chainedAndConstr(mod, Ands):
    """ Add constraints of the refined model on top of the simple model
        using the Ands chains.
    """
    L = len(Ands)
    negativeFun = []

    temp = get_new_var(mod)
    mod.addGenConstrAnd(temp, [Ands[0][2], Ands[1][2]])
    mod.addConstr(Ands[0][0] <= Ands[1][1] + 1 - temp)
    mod.addConstr(Ands[0][0] >= Ands[1][1] - 1 + temp)
    negativeFun += [temp] #we have correlation so we deduce 1.

    for i in xrange(1,L-1):
        temp = get_new_var(mod)
        mod.addGenConstrAnd(temp, [invert_variable(mod,negativeFun[-1]), Ands[i][2], Ands[i+1][2]])
        mod.addConstr(Ands[i][0] <= Ands[i+1][1] + 1 - temp)
        mod.addConstr(Ands[i][0] >= Ands[i+1][1] - 1 + temp)

        negativeFun += [temp] #we have correlation so we deduce 1.

    return negativeFun

def printSols():
    """ Print all solutions (set by mod.Params.PoolSolutions in main).
    """
    print( str(int(quicksum(objFuns).getValue())) + " active gates - " + str(int(quicksum(negFuns).getValue())) + " correlated")
    nSol = mod.SolCount
    for sol in range(nSol):
        mod.Params.SolutionNumber = sol
        print( "Solution number "+str(sol+1)+ ", " + str(int(mod.PoolObjVal)) + " score.")
        for r in range(0,len(states)-1,128) + [len(states)-1]:
            print( "m"+ str(r) +"  = 0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x}".format(variables_block_to_int(states[r][96:128]), variables_block_to_int(states[r][64:96]), variables_block_to_int(states[r][32:64]), variables_block_to_int(states[r][0:32])))
        print( "========")
    mod.Params.SolutionNumber = 0

# !SECTION! Main
# =======================

if __name__ == "__main__":
    # building MILP model
    mod = Model("test")
    nbrounds = 256  # number of rounds
    mod.Params.PoolSearchMode = 2 #def: 0, sol first: 1, best sols: 2
    mod.Params.Threads = 4
    mod.Params.PoolSolutions = 50

    # Create state variables for every rounds.
    states = [get_new_variables_block(mod, N) for r in xrange(nbrounds+1)]
    # Initialize the variable lists for the sum to be optimized.
    objFuns = []
    # Initialize empty And chains.
    Ands = [[] for _ in xrange(15)]

    # Add the simple model constraints and objective function.
    for r in xrange(nbrounds):
        objFuns += stateUptLin(mod, states[r], states[r+1], Ands, r)

    # Add refined model constraints and create a negative objective function (number of correlations).
    negFuns = []
    for i in xrange(15):
        negFuns += chainedAndConstr(mod, Ands[i])

    # Add constraints on the relevant linear trail (Type 1, 2, 3, 4).
    not_all_zeros_constraint(mod, states[0])

    for i in range(0,N-64) + range(N-32,N):
        mod.addConstr(states[0][i] == 0)    # set some input mask to 0.
        mod.addConstr(states[-1][i] == 0)   # set some output mask to 0.

    #dstart = 0x02400004081220010040102040000080
    #dend   = 0x00000000000000010000000000000000
    #for i in xrange(N):
    #    mod.addConstr(states[0][i] == (dstart >> i)&1)
    #    mod.addConstr(states[-1][i] == (dend >> i)&1)

    # Set objecive functions as sum of active AND gates minus correlations.
    mod.setObjective(quicksum(objFuns)- quicksum(negFuns))

    # Optimize.
    mod.optimize()
    printSols()


#!/usr/bin/python3

#spawner che genera casualmente un solo blocco (usato per il testing con mock_node) scritto da Carlo
from gazebo_msgs.srv import SpawnModel
from geometry_msgs.msg import *
import rospy
import random
import os
home_path = os.environ["HOME"]

#Array containing all lego blocks names
# blocks  = ["X1-Y2-Z1", "X2-Y2-Z2", "X1-Y3-Z2", "X1-Y2-Z2", "X1-Y2-Z2-CHAMFER", "X1-Y4-Z2", "X1-Y1-Z2", "X1-Y2-Z2-TWINFILLET", "X1-Y3-Z2-FILLET", "X1-Y4-Z1", "X2-Y2-Z2-FILLET"];
blocks = [
    "X1-Y1-Z2",
    "X1-Y2-Z1",
    "X1-Y2-Z2",
    "X1-Y2-Z2-CHAMFER",
    "X1-Y2-Z2-TWINFILLET",
    "X1-Y3-Z2",
    "X1-Y3-Z2-FILLET",
    "X1-Y4-Z1",
    "X1-Y4-Z2",
    "X2-Y2-Z2",
    "X2-Y2-Z2-FILLET"]

#Generate random position
pos = Pose(Point(random.uniform(-0.33, 0.33), random.uniform(-0.25, -0.73),0.775), Quaternion(0,0,random.uniform(-3.14, 3.14), random.uniform(-1.57, 1.57)))
#Get a random lego block from all legos
brickNumber = random.randint(0,10)
brick=blocks[brickNumber]
print(pos)
print(brick)
print(brickNumber)
#Call rospy spawn function to spawn objects in gazebo
#spawna direttamente in gazebo !
spawn_model_client = rospy.ServiceProxy('/gazebo/spawn_sdf_model', SpawnModel)
spawn_model_client(model_name=''+str(brick),
    model_xml=open(home_path+'/progetto_ws/src/ultimate_gazebo/models/'+brick+'/model1.sdf', 'r').read(),
    robot_namespace='/foo',
    initial_pose=pos,
    reference_frame='world')


import json
from datetime import datetime

from io import BytesIO

import scrapy
import pandas as pd

import os
import sys

sys.path.append(
    os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))))

from global_settings import TOP_N_STOCKS, SAVE_EACH_N_ITEMS, PROJECT_ROOT

import logging
logger = logging.root

class MarketBeatDashboardSpider(scrapy.Spider):
    name = "marketbeat_dashboard"
    timestamp = datetime.now().strftime("%d-%m-%y")
    TOP_N_COMPANIES = TOP_N_STOCKS
    SAVE_EACH = SAVE_EACH_N_ITEMS

    def __init__(self, name=None, **kwargs):
        super().__init__(name, **kwargs)
        self.data = []

    def start_requests(self):

        nasdaq_stocks = pd.read_csv(os.path.join(PROJECT_ROOT, "nasdaq.csv"))
        nasdaq_stocks["exchange"] = 'NASDAQ'
        nyse_stocks = pd.read_csv(os.path.join(PROJECT_ROOT, "nyse.csv"))
        nyse_stocks["exchange"] = "NYSE"
        stocks = pd.concat([nasdaq_stocks, nyse_stocks])
        stocks = stocks.sort_values("Market Cap", ascending=False, inplace=False)
        stocks = list(zip(stocks["Symbol"], stocks["exchange"]))

        urls = [
            "https://www.marketbeat.com/stocks/{stock_exchange}/{symbol}/".format(stock_exchange=stock[1],
                                                                                symbol=stock[0]) for stock in stocks
        ]

        for url in urls[:self.TOP_N_COMPANIES]:
            yield scrapy.Request(url, self.parse)

    def parse(self, response, **kwargs):

        stock_profile = {}
        stock_profile["symbol"] = response.url.split("/")[-2]

        overall_rank = response.xpath("//p[@class='bold m-0']/text()").get()
        insider_rank = response.xpath("//div[@class='carousel-item rankInsider']/h3/span/text()").get()
        community_rank = response.xpath("//div[@class='carousel-item rankCommunity']/h3/span/text()").get()
        valuation_rank = response.xpath("//div[@class='carousel-item rankValuation']/h3/span/text()").get()
        dividend_rank = response.xpath("//div[@class='carousel-item rankDividend']/h3/span/text()").get()
        analyst_rank = response.xpath("//div[@class='carousel-item active rankAnalyst']/h3/span/text()").get()

        stock_profile["overall_rank"] = overall_rank
        stock_profile["insider_rank"] = insider_rank
        stock_profile["community_rank"] = community_rank
        stock_profile["valuation_rank"] = valuation_rank
        stock_profile["dividend_rank"] = dividend_rank
        stock_profile["analyst_rank"] = analyst_rank

        self.data.append(stock_profile)

        if len(self.data) % self.SAVE_EACH == 0:
            logger.info(f"Scraped: {len(self.data)} stocks. Saving...")
            with open(f"marketbeat_dashboard_{self.timestamp}.json", "w") as f:
                json.dump(self.data, f)

        if len(self.data) == self.TOP_N_COMPANIES:
            logger.info(f"Scraped: {len(self.data)} stocks. Saving...")
            with open(f"marketbeat_dashboard_{self.timestamp}.json", "w") as f:
                json.dump(self.data, f)


# Vstupní proměnné
oddelovac = "=" * 62
uzivatele = {"tomas", "petr", "marek"}
sluzby = ("dostupne filmy", "detaily filmu", "reziseri")
film_1 = {
    "JMENO": "Shawshank Redemption",
    "HODNOCENI": "93/100",
    "ROK": 1994,
    "REZISER": "Frank Darabont",
    "STOPAZ": 144,
    "HRAJI": ("Tim Robbins", "Morgan Freeman", "Bob Gunton", "William Sadler",
      "Clancy Brown", "Gil Bellows", "Mark Rolston", "James Whitmore",
      "Jeffrey DeMunn", "Larry Brandenburg"
    )
}

film_2 = {
    "JMENO": "The Godfather",
    "HODNOCENI": "92/100",
    "ROK": 1972,
    "REZISER": "Francis Ford Coppola",
    "STOPAZ": 175,
    "HRAJI": ("Marlon Brando", "Al Pacino", "James Caan",
      "Richard S. Castellano", "Robert Duvall", "Sterling Hayden",
      "John Marley", "Richard Conte"
    )
}

film_3 = {
    "JMENO": "The Dark Knight",
    "HODNOCENI": "90/100",
    "ROK": 2008,
    "REZISER": "Christopher Nolan",
    "STOPAZ": 152,
    "HRAJI": ("Christian Bale", "Heath Ledger", "Aaron Eckhart",
      "Michael Caine", "Maggie Gyllenhaal", "Gary Oldman", "Morgan Freeman",
      "Monique Gabriela", "Ron Dean", "Cillian Murphy"
    )
}

film_4 = {
    "JMENO": "The Prestige",
    "HODNOCENI": "85/100",
    "ROK": 2006,
    "REZISER": "Christopher Nolan",
    "STOPAZ": 130,
    "HRAJI": ("Hugh Jackman", "Christian Bale", "Michael Caine",
      "Piper Perabo", "Rebecca Hall", "Scarlett Johansson", "Samantha Mahurin",
      "David Bowie"
    )
}

# Společný slovník 'filmy'

ceny = {"jablko":10}

filmy = {
    film_1["JMENO"]: film_1,
    film_2["JMENO"]: film_2,
    film_3["JMENO"]: film_3,
    film_4["JMENO"]: film_4,
    }

print(filmy["The Prestige"])

uzivatel = input("Zadej jmeno: ")

if uzivatel in uzivatele:
    print("V pořádku!")
    print(oddelovac)
    print("Vítejte v našem filmovém slovníku!".upper().center(len(oddelovac)))
    print(oddelovac)
    print("|".join(sluzby).center(len(oddelovac)))
    print(oddelovac)
else:
    print("Neregistrovaný uživatel!")
    quit()

vyber = input("Vyber sluzby: ")

if vyber == "dostupne filmy":
    print("Dostupné filmy: ")
    print(", ".join(filmy.keys()))
    print(oddelovac)

# PRIKLAD PSEUDOKODU
# pokud bude vyber uzivatele "detaily filmu":
#   ziskej od uživatele název filmu
#   vyhledej ve slovniku s filmy zadaný film podle jména
#   vytiskni jmeno tohoto filmu (jinak "Takovy film neni ve slovniku")
#   vytiskni oddelovac
# pokud bude vyber uzivatele "reziseri":
#   vyhledej pod kazdym filmem ve slovniku s filmy jmeno rezisera
#   vytiskni jména režiséru (každého jen jednou), která budou spojena čárkou
#   vytiskni oddelovac
# jinak:
#   vytiskni "Vybrana sluzba neni v nabidce, ukoncuji.."

elif vyber == "detaily filmu":
    film = input("Vyber film: ")
    film_detail = filmy.get(film, "Takovy film neni ve slovniku")
    print(film_detail)
    print(oddelovac)
elif vyber == "reziseri":
    reziseri = (
      filmy["Shawshank Redemption"]["REZISER"],
      filmy["The Godfather"]["REZISER"],
      filmy["The Dark Knight"]["REZISER"],
      filmy["The Prestige"]["REZISER"]
    )
    print(", ".join(set(reziseri)))
    print(oddelovac)
else:
    print("Vybrana sluzba neni v nabidce, ukoncuji..")


def isMonotonic(array):
    #O(N) Time | O(1)Space
    isNonDecrease = True
    isNonIncrease = True
    for i in range(1,len(array)):
        if array[i] < array[i - 1]:
            isNonDecrease = False
        if array[i] > array[i - 1]:
            isNonIncrease = False
    return isNonIncrease or isNonDecrease


from pprint import pprint
import csv
import pymongo
import json
import urllib.request
import re
import sys
import datetime
from bson import ObjectId
import hashids
from hashlib import blake2b, blake2s
import time

client = pymongo.MongoClient('localhost', 27017)
db = client.USTdev

components = db.stock.aggregate([
    # {"$match": {"_id": ObjectId('5c70984612875079b91f89e9')}},
    {"$lookup": {"from": 'store_positions', "localField":'position.posid', "foreignField": '_id', "as": 'position_info'}},
])

errs = set()

for i, comp in enumerate(components):
    try:
        print("====================================")
        # print(comp)
        position = None
        cid = comp['_id']
        stocks = set()

        # prevodni tabulka ze skladu na nezarazenou pozici
        stock = {
            ObjectId("5c67444e7e875154440cc28f"): None,# Praha
            ObjectId("5c67445b7e875154440cc297"): None,# sob provozovna
            ObjectId("5c67446b7e875154440cc299"): None,# Hvezdarna
        }
        stock_usage = {
            ObjectId("5c67444e7e875154440cc28f"): False,# Praha
            ObjectId("5c67445b7e875154440cc297"): False,# sob provozovna
            ObjectId("5c67446b7e875154440cc299"): False,# Hvezdarna
        }

        ## Ziskat pozice ve skladu (uprednostnit primarni a pak posledni)
        for j, p in enumerate(comp.get('position', [])):
            info = comp['position_info'][j]
            if p['primary']:
                stock[info['warehouse']] = p['posid']

        for j, p in enumerate(comp.get('position', [])):
            info = comp['position_info'][j]
            if not stock[info['warehouse']] or stock[info['warehouse']] in [ObjectId('5c709fa012875079fd76e238')]:
                stock[info['warehouse']] = p['posid']

        if not stock[ObjectId("5c67444e7e875154440cc28f")]: stock[ObjectId("5c67444e7e875154440cc28f")] = ObjectId('5c709fa012875079fd76e238')

        db.stock.update(
            {
                "_id": cid
            },
            {
                "$set": {"packets": []},
                #"$set": {"parameters": {}}
            }
        )

        for j, h in enumerate(comp.get('history', [])):
            if not 'stock' in h: h['stock'] = ObjectId("5c67444e7e875154440cc28f")
            if not stock_usage[h['stock']]:
                pid = ObjectId()
                stock_usage[h['stock']] = pid
                print("Vytvorim novy sacek {}".format(pid))
                query = {
                    "$push": {
                        "packets": {
                            '_id': pid,
                            'type': 'zip_bag',
                            'supplier': 'null',
                            'position': stock[h['stock']],
                            'description': 'Vytvoreno pri aktualizaci databaze'
                        }
                    }
                }
                db.stock.update(
                    {
                        "_id": cid
                    },
                    query
                )

        for j, h in enumerate(comp.get('history', [])):
            print("* ",j, h)
            pos = stock[h['stock']]

            query = {
                    "pid": stock_usage[h['stock']],
                    "count" : h.get('bilance', 0),
                    "unit_price": h.get('price', 0),
                    "type" : h.get('operation', 'service'),
                    "date" : h['_id'].generation_time,
                    "user" : h.get('user', 'null'),
                    "invoice" : h.get('invoice'),
                    "supplier" : h.get('supplier'),
                    "description" : "upgrade, " + h.get('description', '')
                }
            print(query)
            db.stock_operation.insert(query)

            # db.stock.update({
            #     "_id": cid
            # }, {
            #     "$push": {
            #         "packets": {
            #             '_id': pid,
            #             'type': 'zip_bag',
            #             'supplier': 'null',
            #             'position': 'null',
            #             'description': 'Vytvoreno oddelenim z {}'.format(src)
            #         }
            #     }
            # })
        # print(positions)

    except Exception as e:
        print("chyba", e)
        errs.add((comp['_id'], comp['name'], e))

print(i, "celkem mame tolik polozek")
for e in errs:
    print(e)


import numpy as np
import sys, os
from word2vector import Embedding
from basic_functions import softmax, sigmoid
from basic_layers import Softmax

class RNN:
    """ Recurrent Neural Network model (single block)

    A single block of RNN, which is the base of time series nerual network.
    """
    def __init__(self, Wx, Wh, b):
        """ Initialize RNN

        Parameters:
            Wx: affine matrix for x(t)
            Wh: affine matrix for h(t-1)
            b: bias vector
        """
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev):
        """ Forward propagation of RNN

        Parameters:
            x: x(t) is the tth item in time series. The size of x(t) is (1 * D) (a vector) or (N * D) (a mini-batch)
            h_pre: h(t-1) is the previous output of RNN. The size of h(t-1) is (1 * H) (a vector) or (N * H) (a mini-batch)
            Note that N is the batch size.

        Size of weights matrices:
            Wx: (D * H)
            Wh: (H * H)
            b: (1 * H)

        Return:
            h_next: h(t) is the output of the current RNN block, which will be sent to the next RNN block.
            the size of h_next: (1 * H) (a vector) or (N * H) (a mini-batch)
            how to calculate h_next: h(t) = tanh(h(t-1).Wh + x(t).Wx + b)
        """
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)

        self.cache = (x, h_prev, h_next)
        return h_next

    def backward(self, dh_next):
        """ Back propagation of RNN

        Parameter
            dh_next: the derivative matrix of h_next (i.e. h(t)). The size of dh_next is (N * H), which is identical to h_next.

        The size of matrices in each step:
            dt: (N * H)
            db: (1 * H)
            dWh: (H * H)
            dh_prev: (N * H)
            dWx: (D * H)
            dx: (N * D)

        Procedure:
            step 1. dt = dh_next * (1 - h_next^2). Note that [tanh(x)]' = 1 - [tanh(x)]^2
            step 2. db = np.sum(dt, axis=0). Add all rows togethor, dt (N * H) ---> db (1 * H)
            step 3. dWh = transpose(h_prev).dt. h_prev (N * H) ---> transpose(h_prev) (H * N) ---> (H * N).(N * H) ---> (H * H)
            step 4. dh_prev = dt.transpose(Wh). transpose(Wh) (H * H) ---> dt (N * H) ---> (N * H).(H * H) ---> (N * H)
            step 5. dWx = transpose(x).dt. x (N * D) ---> transpose(x) (D * N) ---> dt (N * H) ---> (D * N).(N * H) ---> (D * H)
            step 6. dx = dt.transpose(Wx). dt (N * H) ---> transpose(Wx) (H * D) ---> (N * H).(H * D) ---> (N * D)

        """
        Wx, Wh, _ = self.params
        x, h_prev, h_next = self.cache

        dt = dh_next * (1 - h_next**2)
        db = np.sum(dt, axis=0)
        dWh = np.dot(np.transpose(h_prev), dt)
        dh_prev = np.dot(dt, np.transpose(Wh))
        dWx = np.dot(np.transpose(x), dt)
        dx = np.dot(dt, np.transpose(Wx))

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        return dx, dh_prev

class TimeRNN:
    """ Time RNN combines RNN blocks and learns time series

    """
    def __init__(self, Wx, Wh, b, stateful=False):
        """ Initialize TimeRNN

        Parameters:
            Wx: (D * H) matrix
            Wh: (H * H) matrix
            b: (1 * H) vector
            stateful: use h_prev or not (if not, set h_prev to 0)
        """
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None
        self.h, self.dh = None, None
        self.stateful = stateful

    def set_state(self, h):
        """ Set hidden state to h

        Paramter:
            h: a hidden state matrix (size: (N * H))

        Return:
            None
        """
        self.h = h

    def reset_state(self):
        """ Reset hidden state to None
        """
        self.h = None

    def forward(self, xs):
        """ Forward propagation of TimeRNN

        Parameter:
            xs: a time series input. The size of xs is (N * T * D) where N is the batch size, T is the time, and D is the length of word vector

        Return:
            hs: a time series hidden state. The size of hs is (N * T * H) where H is the length of hidden vector

        Note:
            if stateful is False or h is None, the input h is set to an (N * H) zero matrix.
        """
        Wx, _, _ = self.params
        N, T, _ = np.shape(xs)
        _, H = np.shape(Wx)

        self.layers = []
        hs = np.empty((N, T, H), dtype="f")

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype="f")

        for t in range(T):
            layer = RNN(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        """ Back propagation of TimeRNN

        Parameter:
            dhs: the derivative matrix of hs. dhs has the same size with hs (N * T * H)

        Return:
            dxs: the derivative matrix of xs. dxs has the same size with xs (N * T * D)

        Note that the initial dh is always 0.
        """
        Wx, _, _ = self.params
        N, T, _ = np.shape(dhs)
        D, _ = np.shape(Wx)

        dxs = np.empty((N, T, D), dtype="f")
        dh = 0
        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx

            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh

        return dxs

class TimeEmbedding:
    """ Extend Embedding layer to time series

    """
    def __init__(self, W):
        """ Initialize TimeEmbedding class

        Parameter:
            W: an embedding matrix with size (V * D) where V is the vocabulary size
        """
        self.params = [W]
        self.grads = [np.zeros_like(W)]
        self.layers = None
        self.W = W

    def forward(self, xs):
        """ Forward propagation of TimeEmbedding

        Parameter:
            xs: a time series batch with size (N * T) and each element is a word id. If N = 2 and T = 5,
                xs may look like:
                [
                    batch 0: [0, 1, 4, 8, 35],
                    batch 1: [3, 6, 15, 7, 6],
                ]

        Return:
            out: convert each element in xs to a word vector with fixed length D. The size of out is (N * T * D)
        """
        N, T = np.shape(xs)
        _, D = np.shape(self.W)

        out = np.empty((N, T, D))
        self.layers = []

        for t in range(T):
            layer = Embedding(self.W)
            out[:, t, :] = layer.forward(xs[:, t])
            self.layers.append(layer)

        return out

    def backward(self, dout):
        """ Back propagation of TimeEmbedding

        Paramter:
            dout: the derivative matrix of out (see forward function). The size of dout is (N * T * D)

        Return:
            None

        Note that we add all grads togethor
        """
        T = np.shape(dout)[1]
        grad = 0

        for t in range(T):
            layer = self.layers[t]
            layer.backward(dout[:, t, :])
            grad += layer.grads[0]

        self.grads[0][...] = grad

class TimeAffine:
    """ Extend Affine layer to time series
    """
    def __init__(self, W, b):
        """ Initialize TimeAffine class

        Paramters:
            W: an affine matrix with size (H * V) where H is the length of hidden vector and V is the vocabulary size
            b: a bias vector with V elements.
        """
        self.params = [W, b]
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None

    def forward(self, x):
        """ Forward propagation of TimeAffine

        Parameter:
            x: in RNN, x is identical to hs (see TimeRNN class). The size of x is (N * T * H)

        Return:
            out: a time series "score" matrix with size (N * T * V)
        """
        N, T, _ = np.shape(x)
        W, b = self.params

        rx = np.reshape(x, newshape=(N * T, -1))
        out = np.dot(rx, W) + b
        out = np.reshape(out, newshape=(N, T, -1))
        self.x = x
        return out

    def backward(self, dout):
        """ Backward propagation of TimeAffine

        Parameter:
            dout: the derivative matrix of out (i.e. the score matrix, see forward function) with size (N * T * V)

        Return:
            dx: the derivative matrix of x. Here dx and x are dhs and hs respectively (see TimeRNN class). The size of x (hs) and dx (dhs) is (N * T * H)
        """
        x = self.x
        N, T, H = np.shape(x)
        W, _ = self.params

        dout = np.reshape(dout, newshape=(N * T, -1))
        rx = np.reshape(x, newshape=(N * T, -1))

        db = np.sum(dout, axis=0)
        dW = np.dot(np.transpose(rx), dout)
        dx = np.dot(dout, np.transpose(W))
        dx = np.reshape(dx, newshape=(N, T, H))

        self.grads[0][...] = dW
        self.grads[1][...] = db

        return dx

class TimeSoftmaxWithLoss:
    """ TimeSoftmaxWithLoss class

    We combine softmax layer and loss layer togethor and then convert them to time series version
    """
    def __init__(self):
        """ Initialize TimeSoftmaxWithLoss class

        There is no input argument

        self.ingore_label is used in dropout algorithm
        """
        self.params, self.grads = [], []
        self.cache = None
        self.ignore_label = -1

    def forward(self, xs, ts):
        """ Forward propagation of TimeSoftmaxWithLoss

        Parameters:
            xs: a score matrix from TimeAffine layer. The size of xs is (N * T * V), where V is the vocabulary size.
            ts: a label matrix. ts may have two various sizes:
                1. (N * T * V), each label is given as a one-hot vector with length V
                2. (N * T), each label is given as the target word id (a scalar)

        Return:
            loss: the loss of xs (a scalar)

        Note:
            how to calculate the loss of time series.

                    batch N-1             batch N-2        ......     batch 0
        t = 0   score_vec(N-1, 0)     score_vec(N-2, 0)    ...... score_vec(0, 0)    ---> ts(0, 0)  ---> Softmax and Cross Entropy Layer ---> loss 0    ---|
        t = 1   score_vec(N-1, 1)     score_vec(N-2, 1)    ...... score_vec(0, 1)    ---> ts(0, 1)  ---> Softmax and Cross Entropy Layer ---> loss 1    ---|
        .              .                     .                          .                    .                 .                                           |
        .              .                     .                          .                    .                 .                                           | ---> Average ---> loss
        .              .                     .                          .                    .                 .                                           |
        .              .                     .                          .                    .                 .                                           |
        t = T-1 score_vec(N-1, T-1)   score_vec(N-2, T-1)  ...... score_vec(0, T-1)  ---> ts(0, T-1) ---> Softmax and Cross Entropy Layer ---> loss T-1 ---|

        Size of matrices:
            xs: (N * T * V) ---> ((N * T) * V) (3D to 2D)
            ts: (N * T * V) ---> (N * T) ---> (1 * (N * T)) (3D (argmax)---> 2D (reshape) ---> 1D vector)
            ys: ((N * T) * V) (2D)
            ls: (1 * (N * T)) (1D vector)
            loss: scalar
        """
        N, T, V = np.shape(xs)

        if np.ndim(ts) == 3:
            ts = np.argmax(ts, axis=2)

        mask = (ts != self.ignore_label)

        xs = np.reshape(xs, newshape=(N * T, V))
        ts = np.reshape(ts, newshape=(N * T))
        mask = np.reshape(mask, newshape=(N * T))

        ys = softmax(xs)
        ls = np.log(ys[np.arange(N * T), ts])
        ls *= mask
        loss = -1 * np.sum(ls)
        loss /= np.sum(mask)

        self.cache = (ts, ys, mask, (N, T, V))

        return loss

    def backward(self, dout=1):
        """ Back propagation of TimeSoftmaxWithLoss

        Parameter:
            dout: the derivative of loss (a scalar)

        Return:
            dx: the derivative matrix of xs (see forward function). Size of dx is (N * T * V)
        """
        ts, ys, mask, (N, T, V) = self.cache

        dx = ys
        dx[np.arange(N * T), ts] -= 1
        dx *= dout
        dx /= np.sum(mask)
        dx *= mask[:, np.newaxis]
        dx = np.reshape(dx, newshape=(N, T, V))

        return dx

class LSTM:
    """ LSTM single block

    """
    def __init__(self, Wx, Wh, b):
        """ Initialize LSTM class

        Parameters:
            Wx: (D * 4H) matrix
            Wh: (H * 4H) matrix
            b: a vector with length 4H
        """
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev, c_prev):
        """Forward propagation of LSTM

        Parameters:
            x: (N * D) matrix
            h_prev: (N * H) matrix
            c_prev: (N * H) matrix

        Return:
            h_next: (N * H) matrix
            c_next: (N * H) matrix

        Steps of forward propagation:
            Sizes of matrices:
                x: (N * D)
                Wx: (D * 4H)
                h_prev: (N * H)
                Wh: (H * 4H)
                b: a vector with length 4H
                f, g, i, o: (N * H)
                c_next, h_next: (N * H)

            Step 1. A = x.Wx + h_prev.Wh + b
            Step 2. f = A[:, 0:H], g = A[:, H:2H], i = A[:, 2H:3H], o = A[:, 3H:]
            step 3. f = sigmoid(f) (forget gate)
                    g = tanh(g)
                    i = sigmoid(i) (input gate)
                    o = sigmoid(o) (output gate)
            step 4. c_next = c_prev * f + g * i
                    h_next = o * tanh(c_next)

        """
        Wx, Wh, b = self.params
        _, H = np.shape(h_prev)

        A = np.dot(x, Wx) + np.dot(h_prev, Wh) + b
        f = A[:, 0 : H]
        g = A[:, H : 2 * H]
        i = A[:, 2 * H : 3 * H]
        o = A[:, 3 * H : ]

        f = sigmoid(f)
        g = np.tanh(g)
        i = sigmoid(i)
        o = sigmoid(o)

        c_next = c_prev * f + g * i
        h_next = np.tanh(c_next) * o

        self.cache = (x, h_prev, c_prev, i, f, g, o, c_next)
        return h_next, c_next

    def backward(self, dh_next, dc_next):
        """ Back propagation of LSTM

        Parameters:
            dh_next: the derivative matrix of h_next (size: (N * H))
            dc_next: the derivative matrix of c_next (size: (N * H))

        Return:
            dx: the derivative matrix of x (size: (N * D))
            dh_prev: the derivative matrix of h_prev (size: (N * H))
            dc_prev: the derivative matrix of c_prev (size: (N * H))
        """
        Wx, Wh, _ = self.params
        x, h_prev, c_prev, i, f, g, o, c_next = self.cache

        ds = dc_next + dh_next * o * (1 - np.tanh(c_next)**2)
        dc_prev = ds * f

        do = dh_next * np.tanh(c_next)
        di = ds * g
        dg = ds * i
        df = c_prev * ds

        do *= o * (1 - o)
        di *= i * (1 - i)
        dg *= (1 - g**2)
        df *= f * (1 - f)

        dA = np.hstack((df, dg, di, do))

        db = np.sum(dA, axis=0)
        dWh = np.dot(np.transpose(h_prev), dA)
        dh_prev = np.dot(dA, np.transpose(Wh))
        dWx = np.dot(np.transpose(x), dA)
        dx = np.dot(dA, np.transpose(Wx))

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        return dx, dh_prev, dc_prev

class TimeLSTM:
    """ Apply LSTM to time series data

    """
    def __init__(self, Wx, Wh, b, stateful=False):
        """ Initialize TimeLSTM class

        Parameters:
            Wx: (D * 4H) matrix
            Wh: (H * 4H) matrix
            b: a vector with length 4H
            stateful: set False so that c and h are initialize with 0

            (see forward function of LSTM to get for information)
        """
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.c = None, None
        self.dh = None
        self.stateful = stateful

    def forward(self, xs):
        """ Forward propagation of TimeLSTM

        Parameter:
            xs: (N * T * D) matrix

        Return:
            hs: (N * T * H) matrix
        """
        Wx, Wh, b = self.params
        N, T, _ = np.shape(xs)
        H = np.shape(Wh)[0]

        self.layers = []
        hs = np.empty(shape=(N, T, H), dtype="f")

        if (not self.stateful) or (self.h is None):
            self.h = np.zeros(shape=(N, H), dtype="f")
        if (not self.stateful) or (self.c is None):
            self.c = np.zeros(shape=(N, H), dtype="f")

        for t in range(T):
            layer = LSTM(Wx, Wh, b)
            self.h, self.c = layer.forward(xs[:, t, :], self.h, self.c)
            hs[:, t, :] = self.h

            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        """ Back propagation of TimeLSTM

        Parameter:
            dhs: the derivative matrix of hs (size: (N * T * H))

        Return:
            dxs: the derivative matrix of xs (size: (N * T * D))
        """
        Wx, _, _ = self.params
        N, T, _ = np.shape(dhs)
        D = np.shape(Wx)[0]

        dxs = np.empty(shape=(N, T, D), dtype="f")
        dh, dc = 0, 0

        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh, dc = layer.backward(dh + dhs[:, t, :], dc)
            dxs[:, t, :] = dx
            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh

        return dxs

    def set_state(self, h, c=None):
        self.h, self.c = h, c

    def reset_state(self):
        self.h, self.c = None, None

class TimeDropout:
    """ Dropout layer used in time series
    """
    def __init__(self, dropout_rate=0.5):
        """ Initialize TimeDropout class

        Parameter:
            dropout_rate: the proportion of nodes that will be dropout
        """
        self.params, self.grads = [], []
        self.dropout_rate = dropout_rate
        self.mask = None
        self.train_flag = True

    def forward(self, xs):
        """ Forward propagation of TimeDropout

        Parameter:
            xs: (N * T * D) matrix

        Return:
            xs: some elements are set to 0 (i.e. dropout)

        Note:
            Dropout is only used in training process but NOT used in evaluating process.
        """
        if self.train_flag:
            flag = np.random.randn(*xs.shape) > self.dropout_rate
            scale = 1 / (1.0 - self.dropout_rate)
            self.mask = flag.astype("f") * scale
            return xs * self.mask
        else:
            return xs

    def backward(self, dout):
        """ Backward propagation of TimeDropout

        Parameter:
            dout: a derivative matrix of xs (size: (N * T * D))

        Return:
            dout * mask: (N * T * D) matrix

        Note:
            In the returned matrix, the (elements) nodes that are dropout in
            forward propagation will also be set to 0.
        """
        return dout * self.mask

class Encoder:
    """ Encoder part (seq2seq)

    """
    def __init__(self, vocab_size, wordvec_size, hidden_size):
        """ Building Encoder block of seq2seq

        Parameters:
            vocab_size: vocabulary size of a given corpus
            wordvec_size: the length of word vector (embedding layer)
            hidden_size: the length of hidden vector (LSTM layer)

        Matrices and their sizes:
            1. Used in TimeEmbedding layer
                embed_W: (V * D)

            2. Used in TimeLSTM layer
                lstm_Wx: (D * 4H)
                lstm_Wh: (H * 4H)
                lstm_b: a vector with length 4H

        Model:
            xs (N * T) ---> TimeEmbedding layer --- xs (N * T * D) ---> TimeLSTM layer --- hs (N * T * H)
                                                                                |
                                                                                |
                                                                                |
                                                                                v
                                                                                hs[:, -1, :] (The last result will be sent to Decode part)
        """
        V, D, H = vocab_size, wordvec_size, hidden_size

        embed_W = (np.random.randn(V, D) / 100).astype("f")
        lstm_Wx = (np.random.randn(D, 4 * H) / np.sqrt(D)).astype("f")
        lstm_Wh = (np.random.randn(H, 4 * H) / np.sqrt(H)).astype("f")
        lstm_b = np.zeros(4 * H).astype("f")

        self.embed = TimeEmbedding(embed_W)
        self.lstm = TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=False)

        self.params = self.embed.params + self.lstm.params
        self.grads = self.embed.grads + self.lstm.grads
        self.hs = None

    def forward(self, xs):
        """ Forward propagation of Encoder

        Parameters:
            xs: the input mini-batch with size (N * T)

        Return:
            hs[:, -1, :]: the last hidden result will be sent to Decoder part (size: (N * H))
                          (see the comment of __init__ function)
        """
        xs = self.embed.forward(xs)
        hs = self.lstm.forward(xs)
        self.hs = hs
        return hs[:, -1, :]

    def backward(self, dh):
        """ Back propagation of Encoder

        Parameter:
            dh: the derivative matrix of hs[:, -1, :] (size: (N * H))
        """
        dhs = np.zeros_like(self.hs)
        dhs[:, -1, :] = dh

        dout = self.lstm.backward(dhs)
        self.embed.backward(dout)

class Decoder:
    """ Decoder part (seq2seq)

    """
    def __init__(self, vocab_size, wordvec_size, hidden_size):
        """ Initialize Decoder class

        Parameter:
            vocab_size: vocabulary size of a given corpus
            wordvec_size: the length of word vector (embedding layer)
            hidden_size: the length of hidden vector (LSTM layer)

        Matrices and their sizes:
            1. Used in TimeEmbedding layer
                embed_W: (V * D)

            2. Used in TimeLSTM layer
                lstm_Wx: (D * 4H)
                lstm_Wh: (H * 4H)
                lstm_b: a vector with length 4H

            3. Used in Affine layer
                affine_W: (H * V)
                affine_b: a vector with length V

        Model:
            xs (mini-batch of output, size: (N * T)) ---> TimeEmbedding layer --- xs (N * T * D)--|
                                                                                |--> TimeLSTM --- hs (N * T * H) ---> TimeAffine ---> score (N * T * V)
                                                                    h (N * H, from Encoder part)--|
        """
        V, D, H = vocab_size, wordvec_size, hidden_size

        embed_W = (np.random.randn(V, D) / 100).astype("f")
        lstm_Wx = (np.random.randn(D, 4 * H) / np.sqrt(D)).astype("f")
        lstm_Wh = (np.random.randn(H, 4 * H) / np.sqrt(H)).astype("f")
        lstm_b = np.zeros(4 * H).astype("f")
        affine_W = (np.random.randn(H, V) / np.sqrt(H)).astype("f")
        affine_b = np.zeros(V).astype("f")

        self.embed = TimeEmbedding(embed_W)
        self.lstm = TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True)
        self.affine = TimeAffine(affine_W, affine_b)

        self.params, self.grads = [], []
        for layer in (self.embed, self.lstm, self.affine):
            self.params += layer.params
            self.grads += layer.grads

    def forward(self, xs, h):
        """ Forward propagation of Decoder

        Parameters:
            xs: mini-batch of output (size: (N * T))
            h: hidden matrix from Encoder (size: (N * H))

        Return:
            score: score matrix with size (N * T * V)

        Note that we do not calculate loss here. Loss value will be calculate in seq2seq class.
        See Example06 to get more detail.
        """
        self.lstm.set_state(h)

        out = self.embed.forward(xs)
        out = self.lstm.forward(out)
        score = self.affine.forward(out)

        return score

    def backward(self, dscore):
        """ Back propagation of Decoder

        Parameter:
            dscore: the derivative matrix of score (size: (N * T * V))

        Return:
            dh: the derivative matrix of h (size: (N * H)).

        Note that dh will be used in back propagation of Encoder part
        (See backward function of Encoder class)
        """
        dout = self.affine.backward(dscore)
        dout = self.lstm.backward(dout)
        self.embed.backward(dout)
        dh = self.lstm.dh
        return dh

    def generate(self, h, start_id, sample_size):
        """ Generate text automatically with trained model

        Paramters:
            h: hidden matrix (size: (N * H))
            start_id: the start word id
            sample_size: the size that how many words will be selected

        Return:
            sample: an automatically generated text
        """
        sample = []
        sample_id = start_id
        self.lstm.set_state(h)

        for _ in range(sample_size):
            x = np.array(sample_id).reshape((1, 1))
            out = self.embed.forward(x)
            out = self.lstm.forward(out)
            score = self.affine.forward(out)
            sample_id = np.argmax(score.flatten())
            sample.append(int(sample_id))

        return sample

class PeekyDecoder:
    """ PeekyDecoder is an improved Decoder class

    The difference between Decoder and PeekyDecoder is as follows:
        Decoder: h, the hidden matrix with size (N * H) from Encoder part,
                 is only used by the first LSTM block.

        PeekyDecoder: h is used in all LSTM block and all Affine block

    See this paper to get more detail:

        Kyunghyun Cho, et al: "Learning Phrase Representations Using RNN Encoder-Decoder for Statistical Mochine Translation",
        arXiv: 1406.1078v3, 2014 (link: https://arxiv.org/abs/1406.1078)
    """
    def __init__(self, vocab_size, wordvec_size, hidden_size):
        """ Initialize

        Parameters:
            vocab_size: vocabulary size of a given corpus
            wordvec_size: the length of word vector (embedding layer)
            hidden_size: the length of hidden vector (LSTM layer)

        Matrices and their sizes:
            1. Used in Embedding layer
                embed_W: (V * D)

            2. Used in LSTM layer
                lstm_Wx: ((H + D) * (4H))
                lstm_Wh: (H * 4H)
                lstm_b: a vector with length 4H

            3. Used in Affine layer
                affine_W: ((H + H) * V)
                affine_b: a vector with length V

        Note:
            1. The size of lstm_Wx in Decoder is (D * 4H) while the size of lstm_Wx in PeekyDecoder
               is ((H + D) * 4H). This is because in PeekyDecoder, we conbime xs and h together as
               the input of LSTM
            2. h is not only used in LSTM layer but is also used in Affine layer. The size of input
               of Affine layer in Decoder is (H * V) while it is ((H + H) * V) in PeekyDecoder

        Model:
            The PeekyDecoder model is identical to Decoder model.
        """
        V, D, H = vocab_size, wordvec_size, hidden_size

        embed_W = (np.random.randn(V, D) / 100).astype("f")
        lstm_Wx = (np.random.randn(H + D, 4 * H) / np.sqrt(H + D)).astype("f")
        lstm_Wh = (np.random.randn(H, 4 * H) / np.sqrt(H)).astype("f")
        lstm_b = np.zeros(4 * H).astype("f")
        affine_W = (np.random.randn(H + H, V) / np.sqrt(H + H)).astype("f")
        affine_b = np.zeros(V).astype("f")

        self.embed = TimeEmbedding(embed_W)
        self.lstm = TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True)
        self.affine = TimeAffine(affine_W, affine_b)

        self.params, self.grads = [], []
        for layer in (self.embed, self.lstm, self.affine):
            self.params += layer.params
            self.grads += layer.grads
        self.cache = None

    def forward(self, xs, h):
        """ Forward propagation of PeekyDecoder

        Parameters:
            xs: (N * T) matrix (input mini-batch)
            h: (N * H) matrix (from Encoder part)

        Return:
            score: the score matrix with size (N * T * V)
        """
        N, T = np.shape(xs)
        N, H = np.shape(h)

        self.lstm.set_state(h)
        out = self.embed.forward(xs)
        hs = np.repeat(h, T, axis=0).reshape((N, T, H))
        out = np.concatenate((hs, out), axis=2)

        out = self.lstm.forward(out)
        out = np.concatenate((hs, out), axis=2)

        score = self.affine.forward(out)
        self.cache = H

        return score

    def backward(self, dscore):
        """ Back propagation of PeekyDecoder

        Parameter:
            dscore: the derivative matrix of score (size: (N * T * V))

        Return:
            dh: the derivative matrix of h (size: (N * H))
        """
        H = self.cache

        dout = self.affine.backward(dscore)
        dout, dhs0 = dout[:, :, H:], dout[:, :, :H]
        dout = self.lstm.backward(dout)
        dembed, dhs1 = dout[:, :, H:], dout[:, :, :H]
        self.embed.backward(dembed)

        dhs = dhs0 + dhs1
        dh = self.lstm.dh + np.sum(dhs, axis=1)
        return dh

    def generate(self, h, start_id, sample_size):
        """ Generate text automatically with trained model

        Paramters:
            h: hidden matrix (size: (N * H))
            start_id: the start word id
            sample_size: the size that how many words will be selected

        Return:
            sample: an automatically generated text
        """
        sample = []
        char_id = start_id
        self.lstm.set_state(h)

        H = np.shape(h)[1]
        peeky_h = np.reshape(h, newshape=(1, 1, H))
        for _ in range(sample_size):
            x = np.array([char_id]).reshape((1, 1))

            out = self.embed.forward(x)
            out = np.concatenate((peeky_h, out), axis=2)
            out = self.lstm.forward(out)
            out = np.concatenate((peeky_h, out), axis=2)
            score = self.affine.forward(out)

            char_id = np.argmax(score.flatten())
            sample.append(char_id)

        return sample

class WeightSum:
    """ WeightSum class (part of Attention)

    WeightSum class is used to calculate the weigh sum of hidden matrix given by LSTM
    and probability vector a.

    For a single input time series:
        hs: (T * H) hidden matrix given by TimeLSTM layer
        a: a weight vector with length T

        Then, the calculation is as follows:

        a (length T) ----> ar (T * 1) matrix (reshape) ----> t = hs * ar (numpy broadcast) ----> c = sum(t, axis=0) (c is a content vector with lengyth H)

    For a mini-batch:
        hs: (N * T * H)
        a: (N * T)

        The calculation is identical to above one and the result c is an (N * H) content matrix

    """
    def __init__(self):
        """ Initialize WeightSum class

        There is no parameter
        """
        self.params, self.grads = [], []
        self.cache = None

    def forward(self, hs, a):
        """ Forward propagation of WeightSum class

        Parameters:
            hs: (N * T * H) hidden matrix
            a: (N * T) weight matrix

        Return:
            c: (N * H) content matrix
        """
        N, T, _ = np.shape(hs)

        ar = np.reshape(a, newshape=(N, T, 1))
        t = hs * ar
        c = np.sum(t, axis=1)

        self.cache = (hs, ar)
        return c

    def backward(self, dc):
        """ Back propagation of WeightSum class

        Parameter:
            dc: the derivative matrix of content matrix c (size: (N * H))

        Return:
            dhs: the derivative matrix of hs (size: (N * T * H))
            da: the derivative matrix of a (size: (N * T))
        """
        hs, ar = self.cache
        N, T, H = np.shape(hs)
        dt = np.reshape(dc, newshape=(N, 1, H))
        dt = np.repeat(dt, repeats=T, axis=1)
        dar = dt * hs
        dhs = dt * ar
        da = np.sum(dar, axis=2)

        return dhs, da

class AttentionWeight:
    """ AttentionWeight class

    AttentionWeight is used to calculate the weight matrix a.
    Given the matrix hs from TimeLSTM in Encoder and the matrix
    h from TimeLSTM in Decoder, weight matrix a is calculated by:

    Step 1. Reshape h to hr (size: h (N * H), hr (N * 1 * H))
    Step 2. hr is reshaped to (N * T * H)
    Step 3. t = hr * hs (element-wise product)
    Step 4. s = sum(t, axis=2) (s: (N * T))
    Step 5. a = softmax(s) (convert score s to weight a) (a: (N * T))

    """
    def __init__(self):
        """ Initialize AttentionWeight class

        There is no parameter
        """
        self.params, self.grads = [], []
        self.softmax = Softmax()
        self.cache = None

    def forward(self, hs, h):
        """ Forward propagation of AttentionWeight

        Parameters:
            hs: (N * T * H) matrix
            h: (N * H) matrix

        Return:
            a: (N * T) weight matrix
        """
        N, T, H = np.shape(hs)

        hr = np.reshape(h, newshape=(N, 1, H))
        hr = np.repeat(hr, repeats=T, axis=1)
        t = hs * hr
        s = np.sum(t, axis=2)
        a = self.softmax.forward(s)

        self.cache = (hs, hr)
        return a

    def backward(self, da):
        """ Back propagation of AttentionWeight class

        Paramter:
            da: the derivative matrix of weight matrix a (size: (N * T))

        Return:
            dhs: the derivative matrix of hs (size: (N * T * H))
            dh: the derivative matrix of h (size: (N * H))
        """
        hs, hr = self.cache
        N, T, H = np.shape(hs)

        ds = self.softmax.backward(da)
        dt = np.reshape(ds, newshape=(N, T, 1))
        dt = np.repeat(dt, repeats=H, axis=2)
        dhr = dt * hs
        dhs = dt * hr
        dh = np.sum(dhr, axis=1)

        return dhs, dh

class Attention:
    """ Single Attention block

    A single Attention block is just constructed by AttentionWeight block and WeightSum block.
    """
    def __init__(self):
        """ Initialize Attention class
        """
        self.params, self.grads = [], []
        self.attention_weight_layer = AttentionWeight()
        self.weight_sum_layer = WeightSum()
        self.attention_weight = None

    def forward(self, hs, h):
        """ Forward propagation of Attention

        Parameters:
            hs: (N * T * H) matrix (from Encoder TimeLSTM block)
            h: (N * H) matrix (from Decoder LSTM block)

        Return:
            out: (N * H) content matrix
        """
        a = self.attention_weight_layer.forward(hs, h)
        out = self.weight_sum_layer.forward(hs, a)
        self.attention_weight = a
        return out

    def backward(self, dout):
        """ Back propagation of Attention

        Parameter:
            dout: the derivative matrix of content matrix out (size: (N * H))

        Return:
            dhs: the derivative matrix of hs (size: (N * T * H))
            dh: the derivative matrix of h (size: (N * H))
        """
        dhs0, da = self.weight_sum_layer.backward(dout)
        dhs1, dh = self.attention_weight_layer.backward(da)
        dhs = dhs0 + dhs1
        return dhs, dh

class TimeAttention:
    """ TimeAttention class

    TimeAttention class convert single Attention block to time series
    """
    def __init__(self):
        self.params, self.grads = [], []
        self.layers = None
        self.attention_weights = None

    def forward(self, hs_enc, hs_dec):
        _, T, _ = np.shape(hs_dec)
        out = np.empty_like(hs_dec)
        self.layers = []
        self.attention_weights = []

        for t in range(T):
            layer = Attention()
            out[:, t, :] = layer.forward(hs_enc, hs_dec[:, t, :])
            self.layers.append(layer)
            self.attention_weights.append(layer.attention_weight)

        return out

    def backward(self, dout):
        _, T, _ = np.shape(dout)
        dhs_enc = 0
        dhs_dec = np.empty_like(dout)

        for t in range(T):
            layer = self.layers[t]
            dhs, dh = layer.backward(dout[:, t, :])
            dhs_enc += dhs
            dhs_dec[:, t, :] = dh

        return dhs_enc, dhs_dec

def eval_perplexity(model, corpus, batch_size=10, time_size=35):
    """ Evaluate perplexity of current model

    Perplexity is used to evaluate a language model. It is defined as

                    perplexity = exp(L)

    where L is the average loss of model.

    The smaller the perplexity is, the better the model is

    Parameters:
        model: a given language model
        corpus: a given corpus
        batch_size: the batch size
        time_size: time size

    Return:
        ppl: perplexity
    """
    print("evaluating perplexity ... ")
    corpus_size = len(corpus)
    total_loss = 0
    max_iters = (corpus_size - 1) // (batch_size * time_size)
    jump = (corpus_size - 1) // batch_size

    for iters in range(max_iters):
        xs = np.zeros(shape=(batch_size, time_size), dtype=np.int32)
        ts = np.zeros(shape=(batch_size, time_size), dtype=np.int32)
        time_offset = iters * time_size
        offsets = [time_offset + (i * jump) for i in range(batch_size)]
        for t in range(time_size):
            for i, offset in enumerate(offsets):
                xs[i, t] = corpus[(offset + t) % corpus_size]
                ts[i, t] = corpus[(offset + t + 1) % corpus_size]

        try:
            loss = model.forward(xs, ts, train_flag=False)
        except TypeError:
            loss = model.forward(xs, ts)
        total_loss += loss

        sys.stdout.write('\r%d / %d' % (iters, max_iters))
        sys.stdout.flush()

    print("")
    ppl = np.exp(total_loss / max_iters)
    return ppl

def eval_seq2seq(model, question, correct, id_to_char, verbos=False, is_reverse=False):
    correct = correct.flatten()

    start_id = correct[0]
    correct = correct[1:]
    guess = model.generate(question, start_id, len(correct))

    question = "".join([id_to_char[int(c)] for c in question.flatten()])
    correct = "".join([id_to_char[int(c)] for c in correct])
    guess = "".join([id_to_char[int(c)] for c in guess])

    if verbos:
        if is_reverse:
            question = question[::-1]

        colors = {"ok": "\033[92m", "fail": "\033[91m", "close": "\033[0m"}
        print("Q", question)
        print("T", correct)

        is_windows = os.name == "nt"
        if correct == guess:
            mark = colors["ok"] + "☑" + colors["close"]
            if is_windows:
                mark = "O"
            print(mark, " ", guess)
        else:
            mark = colors["fail"] + "☒" + colors["close"]
            if is_windows:
                mark = "X"
            print(mark, " ", guess)
        print("----")

    return 1 if guess == correct else 0


import threading
import subprocess
import sqlite3
from datetime import datetime

def count_miners_and_insert_into_db():
    # Run shell commands to count the number of unique miners
    try:
        cmd = "tail -500000 access.log | grep verify | cut -d' ' -f1 | sort | uniq | wc -l"
        total_miners = subprocess.getoutput(cmd)
        total_miners = int(total_miners.strip())  # Convert the output to an integer

        # Connect to the difficulty database
        conn = sqlite3.connect('difficulty.db')
        cursor = conn.cursor()

        # Create the miners table if it doesn't exist
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS miners (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            date DATETIME DEFAULT CURRENT_TIMESTAMP,
            total_miners INTEGER
        );
        ''')

        # Insert the count into the miners table
        cursor.execute("INSERT INTO miners (total_miners) VALUES (?)", (total_miners,))
        conn.commit()

        # Close the database connection
        conn.close()

        print(f"Inserted {total_miners} into the database at {datetime.now()}")
    except Exception as e:
        print(f"An error occurred: {e}")

# Set up a timer to run the function every 60 seconds
def start_timer():
    threading.Timer(60.0, start_timer).start()
    count_miners_and_insert_into_db()

# Start the timer
start_timer()


# Libraries used
import json
from logging_error import get_logger
import requests
from flask import Flask, request
from flask_restful import Api, Resource

app = Flask(__name__)
api = Api(app)

write = get_logger(__name__)
resp = {}

''' Fetch data from request and insert in log file '''
class service(Resource):
    def post(self):
        # Get data from request
        req = request.get_json()

        try:
            # Success, write data in log file
            write.debug(f'{req["status"]}|{req["service_name"]}|{req["error_code"]}|{req["exception_type"]}|{req["file_name"]}|{req["line_number"]}|{req["error_info"]}')
            resp["log"] = "success,{}".format(200)
        except IOError as err:
            # Fail case
            resp["log"] = "fail,{}".format(500)

        return resp

api.add_resource(service,'/error_handler/')
if __name__ == '__main__':

    # Seeting App Environment
    app.config["ENV"] = "dev"
    # Running app on specified port
    app.run(
        threaded=True,
        host="127.0.0.1",
        port=5000
    )


import flet as ft
import httpx
import json
from ..components.appbar import app_bar
from ..components.post_min_card import PostMinComponent

class PostResponsive(ft.UserControl):
    _controls_responsive: list = []
    _responsive_row = ft.ResponsiveRow()

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._data_post = None

    def get_data(self):
        response = httpx.get('http://127.0.0.1:8000/api/posts/').content.decode('latin')

        return json.loads(response)

    def did_mount(self):

        dict_data = self.get_data()

        if not dict_data:
            self._responsive_row.controls = [
                ft.Column(
                    alignment=ft.MainAxisAlignment.CENTER,
                    col=12,
                    controls=[
                        ft.Row(
                            vertical_alignment=ft.CrossAxisAlignment.CENTER,
                            controls=[
                                ft.Image(
                                    'img/no-no.gif',
                                    width=100,
                                ),
                                ft.Text(
                                    ' Se ha publicado nada aún...',
                                    style=ft.TextThemeStyle.DISPLAY_MEDIUM,
                                )
                            ]
                        )

                    ]
                )
            ]
            self.update()
        else:

            self._controls_responsive.clear()

            for post in dict_data:
                self._controls_responsive.append(
                    ft.Column(
                        col={'sm': 12, 'md': 6},
                        controls=[
                            PostMinComponent(data=post),
                        ]
                    )
                )
            self._responsive_row.controls = self._controls_responsive
            self.update()

    def build(self):

        self._responsive_row.controls = [
            ft.Column(
                alignment=ft.MainAxisAlignment.CENTER,
                col=12,
                controls=[
                    ft.ProgressRing(width=16, height=16, stroke_width=2)
                ]
            )
        ]

        return self._responsive_row

def posts_view(page: ft.Page):
    _posts = PostResponsive()

    _view = ft.View(
        appbar=app_bar(page),
        scroll=ft.ScrollMode.HIDDEN,
        controls=[
            ft.ResponsiveRow(
                controls=[
                    ft.Column(
                        spacing=20,
                        horizontal_alignment=ft.CrossAxisAlignment.CENTER,
                        col=12,
                        controls=[
                            ft.Text(
                                value='Mi Blog',
                                style=ft.TextThemeStyle.DISPLAY_LARGE,
                                text_align=ft.TextAlign.CENTER,
                                font_family='Milky'
                            ),
                            ft.Text(
                                value='Aqui encontraras las publicaciones posteadas por el usuario.',
                                style=ft.TextThemeStyle.HEADLINE_LARGE,
                                text_align=ft.TextAlign.CENTER,
                            )
                        ]),
                    ft.Column(col={'sm': 12, 'md': 2}, controls=[]),
                    ft.Column(col={'sm': 12, 'md': 8}, controls=[_posts]),
                    ft.Column(col={'sm': 12, 'md': 2}, controls=[]),
                ]
            ),

        ]
    )

    return _view


#!/usr/bin/python

from bs4 import BeautifulSoup
from sys import argv

with open(argv[1]) as f:
	xml = BeautifulSoup(f)
	for query in xml.find_all('query'):
		num = query.find('number').get_text().strip()
		text = query.find('text').get_text().strip()
		print('\t'.join([num, text]))