import cv2
import glob
import imghdr
import os
import tempfile
from pathlib import Path
import numpy as np

from PySide2.QtWidgets import QVBoxLayout, QHBoxLayout, QToolButton, QSpinBox, QLineEdit, QGraphicsView, QSlider, QWidget, QApplication, QFileDialog, QMainWindow, QAction, QMessageBox, QPushButton, QLabel, QGroupBox, QComboBox
from PySide2.QtGui import QPixmap, QImage
from PySide2.QtCore import Slot, Qt
from .SegmenterView import ImageSegmenterView
from .CustomClasses import LabeledComboBox, LabeledSlider, LabeledSpinBox, ClickableLineEdit

def ensure_dir(*paths):
    full_path = ''
    for path in paths:
        full_path = os.path.join(full_path, path)
        if os.path.exists(full_path) and os.path.isdir(full_path):
            continue
        elif os.path.exists(full_path):
            raise ValueError("A file without an extension is blocking directory creation at {}".format(full_path))
        else:
            os.mkdir(full_path)
    return full_path

def QImage_to_CVMat(image):
    image = image.convertToFormat(QImage.Format.Format_ARGB32_Premultiplied)
    width = image.width()
    height = image.height()

    ptr = image.constBits()
    arr = np.array(ptr).reshape(height, width, 4)
    return arr

class SegmenterWidget(QWidget):
    def __init__(self):
        super(SegmenterWidget, self).__init__()
        self.active_image_dir = None
        self.active_label_dir = None
        self.src_paths = []
        self.current_idx = None
        self.prev_idx = 0
        label_types = [
            'Foot',
            'Inner Wound',
            'Outer Wound'
        ]

        # Load last used image directory
        self.meta_file = os.path.join(os.path.dirname(__file__), 'meta.txt')
        if os.path.exists(self.meta_file):
            with open(self.meta_file, 'r') as f:
                last_active_image_dir = f.readline().strip()
                # print(last_active_image_dir)
                if os.path.exists(last_active_image_dir):
                    self.active_image_dir = last_active_image_dir

        # Set up Segmenter Widget
        self.image_title = QLineEdit(self)
        self.image_title.setText('--None--')
        self.image_title.setReadOnly(True)
        self.viewer = ImageSegmenterView(self)

        # Bottom Toolbar Widgets

        ## 'Set Image Folder' button
        self.select_dir_btn = QToolButton(self)
        self.select_dir_btn.setText('Set Image Folder')
        self.select_dir_btn.clicked.connect(self.set_image_dir)
        self.current_dir = ClickableLineEdit(self, text='--None--', readOnly=True)
        self.current_dir.clicked.connect(self.set_image_dir)

        self.select_label_dir_btn = QToolButton(self)
        self.select_label_dir_btn.setText('Set Label Folder')
        self.select_label_dir_btn.clicked.connect(self.set_label_dir)
        self.current_label_dir = ClickableLineEdit(self, text='--None--', readOnly=True)
        self.current_label_dir.clicked.connect(self.set_label_dir)

        self.prev_btn = QToolButton(self)
        self.prev_btn.setText('Previous Image')
        self.prev_btn.clicked.connect(self.goto_previous)
        self.image_idx = LabeledSpinBox('Current Image:',
        minimum=0,
        maximum=0,
        starting_value=0)
        self.image_idx.valueChanged.connect(self.goto_image)
        self.next_btn = QToolButton(self)
        self.next_btn.setText('Next Image')
        self.next_btn.clicked.connect(self.goto_next)
        self.skip_btn = QToolButton(self)
        self.skip_btn.setText('Next Unlabeled Image')
        self.skip_btn.clicked.connect(self.skipto_next)
        self.skip_label_btn = QToolButton(self)
        self.skip_label_btn.setText('Next Labeled Image  ')
        self.skip_label_btn.clicked.connect(self.skipto_next_label)

        ## Sliders
        self.opacity_slider = LabeledSlider('Mask Opacity: {}%',
                                            single_step=5,
                                            page_step=20,
                                            minimum=0,
                                            maximum=100,
                                            starting_value=50)
        self.opacity_slider.valueChanged.connect(self.viewer.set_opacity)
        self.pen_size_slider = LabeledSlider('Pen Size: {}px',
                                             single_step=5,
                                             page_step=10,
                                             minimum=1,
                                             maximum=500,
                                             starting_value=30)
        self.pen_size_slider.valueChanged.connect(self.viewer.set_pen_size)

        # Right Toolbar
        self.label_options = LabeledComboBox("Current Segmentation Mask", items=label_types)
        self.label_options.currentTextChanged.connect(self.label_changed)

        ## Paint Buttons
        self.brush_box = QGroupBox("Brush Options")
        self.foreground_btn = QPushButton('Foreground')
        self.foreground_btn.setCheckable(True)
        self.foreground_btn.setChecked(True)
        self.foreground_btn.setAutoExclusive(True)
        self.foreground_btn.clicked.connect(self.viewer.set_foreground)
        self.poss_foreground_btn = QPushButton("Possible Foreground")
        self.poss_foreground_btn.setCheckable(True)
        self.poss_foreground_btn.setChecked(False)
        self.poss_foreground_btn.setAutoExclusive(True)
        self.poss_foreground_btn.clicked.connect(self.viewer.set_possible_foreground)
        self.background_btn = QPushButton('Background')
        self.background_btn.setCheckable(True)
        self.background_btn.setChecked(False)
        self.background_btn.setAutoExclusive(True)
        self.background_btn.clicked.connect(self.viewer.set_background)
        self.eraser_button = QPushButton('Erase')
        self.eraser_button.setCheckable(True)
        self.eraser_button.setChecked(False)
        self.eraser_button.setAutoExclusive(True)
        self.eraser_button.clicked.connect(self.viewer.set_possible_background)
        brush_layout = QVBoxLayout()
        brush_layout.addWidget(self.foreground_btn)
        brush_layout.addWidget(self.poss_foreground_btn)
        brush_layout.addWidget(self.background_btn)
        brush_layout.addWidget(self.eraser_button)
        self.brush_box.setLayout(brush_layout)

        ## Image Action Buttons
        self.undo_btn = QToolButton(self)
        self.undo_btn.setText('Undo')
        self.undo_btn.clicked.connect(self.viewer.undo)
        self.redo_btn = QToolButton(self)
        self.redo_btn.setText('Redo')
        self.redo_btn.clicked.connect(self.viewer.redo)

        self.save_button = QToolButton(self)
        self.save_button.setText('&Save Mask')
        self.save_button.clicked.connect(self.saveSegmentation)
        self.clear_button = QToolButton(self)
        self.clear_button.setText('Clear Mask')
        self.clear_button.clicked.connect(self.viewer.resetSegLayer)

        self.grabcut_button = QToolButton(self)
        self.grabcut_button.setText('Run &GrabCut Segmenter')
        self.grabcut_button.clicked.connect(self.run_grabcut)

        self.hide_image_button = QToolButton(self)
        self.hide_image_button.setText('Show/&Hide Image')
        self.hide_image_button.pressed.connect(self.viewer.hide_image)

        # Arrange layout
        MainLayout = QVBoxLayout(self)

        BottomStack = QVBoxLayout()

        BottomToolbar = QHBoxLayout()
        BottomToolbar.setAlignment(Qt.AlignLeft)
        Selectors = QVBoxLayout()
        ImageDirSelector = QHBoxLayout()
        ImageDirSelector.addWidget(self.select_dir_btn)
        ImageDirSelector.addWidget(self.current_dir)
        Selectors.addLayout(ImageDirSelector)
        LabelDirSelector = QHBoxLayout()
        LabelDirSelector.addWidget(self.select_label_dir_btn)
        LabelDirSelector.addWidget(self.current_label_dir)
        Selectors.addLayout(LabelDirSelector)

        BottomToolbar.addLayout(Selectors)
        BottomToolbar.addWidget(self.image_idx)
        BottomToolbar.addWidget(self.prev_btn)
        BottomToolbar.addWidget(self.next_btn)
        Skippers = QVBoxLayout()
        Skippers.addWidget(self.skip_btn)
        Skippers.addWidget(self.skip_label_btn)

        # BottomToolbar.addWidget(self.skip_btn)
        BottomToolbar.addLayout(Skippers)
        BottomStack.addLayout(BottomToolbar)

        RightToolbar = QVBoxLayout()
        RightToolbar.setAlignment(Qt.AlignCenter)
        RightToolbar.addWidget(self.label_options)
        RightToolbar.addStretch()
        RightToolbar.addWidget(self.brush_box)
        RightToolbar.addWidget(self.undo_btn)
        RightToolbar.addWidget(self.redo_btn)
        RightToolbar.addStretch()
        # self.gc_spinner.setFixedHeight(0)
        # RightToolbar.addWidget(self.gc_spinner)
        RightToolbar.addWidget(self.hide_image_button)
        RightToolbar.addWidget(self.grabcut_button)
        RightToolbar.addWidget(self.save_button)
        RightToolbar.addWidget(self.clear_button)

        TopLayout = QHBoxLayout()
        ImageLayout = QVBoxLayout()
        ImageLayout.addWidget(self.image_title)
        ImageLayout.addWidget(self.viewer)
        TopLayout.addLayout(ImageLayout)
        TopLayout.addLayout(RightToolbar)

        MainLayout.addLayout(TopLayout)
        FlatSliders = QHBoxLayout()
        FlatSliders.addWidget(self.pen_size_slider)
        FlatSliders.addWidget(self.opacity_slider)
        MainLayout.addLayout(FlatSliders)
        MainLayout.addLayout(BottomStack)

    @Slot()
    def exit_app(self, checked):
        QApplication.quit()

    def set_image_dir(self):
        dialog = QFileDialog(self, 'Select Image Directory')
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setNameFilters(["Any Files (*)"])
        dialog.setViewMode(QFileDialog.List)
        if dialog.exec():
            selected_dir = dialog.selectedFiles()[0]
            if not os.path.isdir(selected_dir):
                selected_dir = os.path.dirname(selected_dir)
            self.active_image_dir = selected_dir
            with open(self.meta_file, 'a+') as f:
                f.write('Set Image Dir: ' + self.active_image_dir + '\n')
            if self.active_label_dir is None:
                self.active_label_dir = self.active_image_dir
                self.current_label_dir.setText(self.active_label_dir)
                with open(self.meta_file, 'a+') as f:
                    f.write('Set Label Dir: ' + self.active_label_dir + '\n')
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, self.label_options.currentText().replace(' ', '_')))
            # image_paths = glob.glob(os.path.join(self.active_image_dir, '*.png')) + glob.glob(os.path.join(self.active_image_dir, '*.jpg'))
            paths = glob.glob(os.path.join(self.active_image_dir, '*'))
            image_paths = []
            for path in paths:
                if os.path.isdir(path):
                    continue
                if imghdr.what(path) in ['jpeg', 'png', 'jpg']:
                    image_paths.append(path)
            self.src_paths = sorted(image_paths)
            self.current_dir.setText(self.active_image_dir)
            self.image_idx.setRange(1, len(self.src_paths))
            self.goto_image(1)

    def set_label_dir(self):
        dialog = QFileDialog(self, 'Select Label Directory')
        dialog.setFileMode(QFileDialog.Directory)
        dialog.setNameFilters(["Any Files (*)"])
        dialog.setViewMode(QFileDialog.List)
        if dialog.exec():
            selected_dir = dialog.selectedFiles()[0]
            if not os.path.isdir(selected_dir):
                selected_dir = os.path.dirname(selected_dir)
            self.active_label_dir = selected_dir
            self.current_label_dir.setText(self.active_label_dir)
            with open(self.meta_file, 'a+') as f:
                f.write('Set Label Dir: ' + self.active_label_dir + '\n')
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, self.label_options.currentText().replace(' ', '_')))
            if self.active_image_dir is not None:
                image_name = os.path.basename(self.src_paths[self.current_idx])
                image_name, suff = os.path.splitext(image_name)
                self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(self.current_label_path):
                    self.viewer.setSegLayer(QPixmap(self.current_label_path))
                else:
                    self.viewer.resetSegLayer()
                self.viewer.changed = False

    def label_changed(self, value):
        if len(self.src_paths) > 0:
            if self.viewer.changed:
                result = self.show_save_warning()
                if result == QMessageBox.Save:
                    self.saveSegmentation()
                elif result == QMessageBox.Cancel:
                    self.label_options.revert()
                    return
                elif result == QMessageBox.Discard:
                    pass
            self.label_dir = ensure_dir(os.path.join(self.active_label_dir, value.replace(' ', '_')))
            image_name = os.path.basename(self.src_paths[self.current_idx])
            image_name, suff = os.path.splitext(image_name)
            self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
            if os.path.exists(self.current_label_path):
                self.viewer.setSegLayer(QPixmap(self.current_label_path))
            else:
                self.viewer.resetSegLayer()
            self.viewer.changed = False

    def goto_image(self, idx):
        if len(self.src_paths) > 0:
            if self.viewer.changed:
                result = self.show_save_warning()
                if result == QMessageBox.Save:
                    self.saveSegmentation()
                elif result == QMessageBox.Cancel:
                    self.image_idx.setValue(self.prev_idx, quiet=True)
                    return
                elif result == QMessageBox.Discard:
                    pass
            self.viewer.clear_history()
            self.current_idx = idx - 1
            self.viewer.setPhoto(QPixmap(self.src_paths[self.current_idx]))
            image_name = os.path.basename(self.src_paths[self.current_idx])
            image_name, suff = os.path.splitext(image_name)
            self.image_title.setText(image_name)
            self.current_label_path = os.path.join(self.label_dir, image_name + '_label.png')
            if os.path.exists(self.current_label_path):
                self.viewer.setSegLayer(QPixmap(self.current_label_path))
            self.prev_idx = idx

    def skipto_next(self):
        # print(self.current_idx)
        if self.current_idx < len(self.src_paths):
            check_idx = self.current_idx
            found = False
            while check_idx < len(self.src_paths):
                # print(check_idx, len(self.src_paths))
                image_name = os.path.basename(self.src_paths[check_idx])
                image_name, suff = os.path.splitext(image_name)
                label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(label_path):
                    check_idx += 1
                    continue
                else:
                    found = True
                    break
            if found:
                self.image_idx.setValue(check_idx + 1)
            else:
                return QMessageBox.warning(self, "Segmenter Tool", "No unlabeled images found for label type '{}'".format(self.label_options.currentText()),
                                   QMessageBox.Ok, QMessageBox.Ok)

    def skipto_next_label(self):
        if self.current_idx < len(self.src_paths):
            check_idx = self.current_idx + 1
            found = False
            while check_idx < len(self.src_paths):
                image_name = os.path.basename(self.src_paths[check_idx])
                image_name, suff = os.path.splitext(image_name)
                label_path = os.path.join(self.label_dir, image_name + '_label.png')
                if os.path.exists(label_path):
                    found = True
                    break
                else:
                    check_idx += 1
                    continue
            if found:
                self.image_idx.setValue(check_idx + 1)
            else:
                return QMessageBox.warning(self, "Segmenter Tool", "No labeled images found for label type '{}'".format(self.label_options.currentText()),
                                   QMessageBox.Ok, QMessageBox.Ok)

    def goto_next(self):
        if self.prev_idx < len(self.src_paths):
            self.image_idx.setValue(self.prev_idx + 1)

    def goto_previous(self):
        if self.prev_idx > 1:
            self.image_idx.setValue(self.prev_idx - 1)

    def saveSegmentation(self):
        if self.viewer.hasPhoto():
            segmentation = QImage_to_CVMat(self.viewer.seg_image)
            cv2.imwrite(self.current_label_path, segmentation)
            self.viewer.changed = False

    def run_grabcut(self):
        if self.viewer.hasPhoto():
            # self.gc_spinner.start()
            image = QImage_to_CVMat(QPixmap(self.src_paths[self.current_idx]).toImage())[:, :, :3]
            segmentation = QImage_to_CVMat(self.viewer.seg_image)
            rescale_factor = 0.125
            image_height, image_width = image.shape[:2]
            image = cv2.resize(image, (int(image_width * rescale_factor), int(image_height * rescale_factor)), interpolation=cv2.INTER_NEAREST)
            new_segmentation = cv2.resize(segmentation, (int(image_width * rescale_factor), int(image_height * rescale_factor)), interpolation=cv2.INTER_NEAREST)

            pfg, fg, bg, pbg = np.split(new_segmentation / 255, segmentation.shape[-1], axis=-1)
            pbg = 1 - pbg
            has_bg = (pbg.sum() + bg.sum()) > 0
            has_fg = (pfg.sum() + fg.sum()) > 0
            if not (has_bg and has_fg):
                if not has_bg:
                    message = 'You must select some background or empty area.'
                elif not has_fg:
                    message = 'You must select some foreground or possible foreground.'
                x = QMessageBox.critical(self, "Segmenter Tool", message, QMessageBox.Ok)
                # self.gc_spinner.stop()
                return

            mask = (cv2.GC_FGD * fg + cv2.GC_BGD * bg + cv2.GC_PR_FGD * pfg + cv2.GC_PR_BGD * pbg).astype(np.uint8)
            bgd_model = np.zeros((1, 65), np.float64)
            fgd_model = np.zeros((1, 65), np.float64)
            mask, _, _ = cv2.grabCut(image, mask, None, bgd_model, fgd_model, 2, cv2.GC_INIT_WITH_MASK)
            pfg = np.where(mask == cv2.GC_PR_FGD, 255, 0) + np.where(mask == cv2.GC_FGD, 255, 0)
            # fg = np.where(mask==cv2.GC_FGD, 255, 0)
            # bg = np.where(mask==cv2.GC_BGD, 255, 0)
            fg = np.zeros_like(pfg)
            bg = np.zeros_like(pfg)
            pbg = np.zeros_like(pfg)
            # pbg = np.where(mask == cv2.GC_PR_BGD, 0, 255) + np.where(mask == cv2.GC_BGD)
            # new_segmentation = np.concatenate([pfg, fg, bg, pbg], axis=2).astype(np.uint8)
            new_segmentation = cv2.resize(pfg, (image_width, image_height), interpolation=cv2.INTER_NEAREST)
            new_segmentation = new_segmentation - segmentation[:, :, 1]
            new_segmentation = np.concatenate([new_segmentation[:, :, np.newaxis], segmentation[:, :, 1:3]], axis=2)
            new_segmentation = np.concatenate([new_segmentation, new_segmentation.max(axis=2, keepdims=True)], axis=2)
            # new_segmentation = cv2.resize(new_segmentation, (image_width, image_height), interpolation=cv2.INTER_NEAREST)
            # new_segmentation[:, :, 0] = new_segmentation[:, :, 0] - segmentation[:, :, 1]
            # new_segmentation[:, :, 1:3] = segmentation[:, :, 1:3]
            # new_segmentation[:, :, 3] = new_segmentation.max(axis=2)
            # new_segmentation = np.where((segmentation[:, :, 1:2] == 255) + (segmentation[:, :, 2:3] == 255), segmentation, new_segmentation)
            tempdir = tempfile.mkdtemp()
            temp_png = os.path.join(tempdir, 'grabcut.png')
            cv2.imwrite(temp_png, new_segmentation)
            self.viewer.setSegLayer(QPixmap(temp_png))
            self.viewer.changed = True
            os.remove(temp_png)
            os.removedirs(tempdir)
            # self.gc_spinner.stop()

    def show_save_warning(self):
        return QMessageBox.warning(self, "Segmenter Tool", 'This segmentation has been modified.\nDo you want to save your changes?',
                                   QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel, QMessageBox.Save)

class SegmenterWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Segmenter Tool")
        self.widget = SegmenterWidget()
        self.menu = self.menuBar()
        self.file_menu = self.menu.addMenu("File")
        self.edit_menu = self.menu.addMenu("Edit")

        save_action = QAction("Save", self)
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.widget.saveSegmentation)

        exit_action = QAction("Exit", self)
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.exit_app)

        undo_action = QAction("Undo", self)
        undo_action.setShortcut('Ctrl+Z')
        undo_action.triggered.connect(self.widget.viewer.undo)

        redo_action = QAction("Redo", self)
        redo_action.setShortcut('Shift+Ctrl+Z')
        redo_action.triggered.connect(self.widget.viewer.redo)

        grabcut_action = QAction("Run GrabCut", self)
        grabcut_action.setShortcut("Ctrl+G")
        grabcut_action.triggered.connect(self.widget.run_grabcut)

        hide_action = QAction('Show/Hide Image', self)
        hide_action.setShortcut("Shift+H")
        hide_action.triggered.connect(self.widget.viewer.hide_image)

        self.file_menu.addAction(save_action)
        self.file_menu.addAction(exit_action)
        self.edit_menu.addAction(undo_action)
        self.edit_menu.addAction(redo_action)
        self.edit_menu.addAction(grabcut_action)
        self.edit_menu.addAction(hide_action)

        self.setCentralWidget(self.widget)

    @Slot()
    def exit_app(self, checked):
        if self.widget.viewer.changed:
            result = self.widget.show_save_warning()
            if result == QMessageBox.Save:
                self.widget.saveSegmentation()
            elif result == QMessageBox.Cancel:
                return
            elif result == QMessageBox.Discard:
                pass
        QApplication.quit()


# %%
from cellpose import models
import numpy as np
import matplotlib.pyplot as plt
# from process_zebrafish_zarr import process_bette_zarr
import zarr

#%% preprocess dataset into groups
# zarr_file = "/mnt/efs/shared_data/YeastiBois/zarr_files/Tien/glass_60x_023.zarr"
# process_bette_zarr(zarr_file)

# # %% Get voxel size to compute anisotropy
# import nd2
# nd2_data = "/mnt/efs/shared_data/YeastiBois/TiensVideos/glass_60x_023.nd2"
# f = nd2.ND2File(nd2_data)
# f.voxel_size()

# # %% Load data
# zarr_file = "/mnt/efs/shared_data/YeastiBois/zarr_files/Tien/glass_60x_023_processed.zarr"
# zfile = zarr.open(zarr_file)

# # %% T, Z, C, X, Y (15, 11, 3, 1024, 1024)
# data = zfile['exp1']

# %%
def pretty_plot(image):
    p5, p95 = np.percentile(image, [1, 99])
    plt.imshow(image, cmap='gray', vmin=p5, vmax=p95)
    plt.show()

from skimage.color import label2rgb

def pplot_img_mask(image, mask, figsize=(10, 5)):
    p5, p95 = np.percentile(image, [1, 99])
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=figsize)
    ax1.imshow(image, cmap='gray', vmin=p5, vmax=p95)
    ax2.imshow(label2rgb(mask))
    ax1.axis('off')
    ax2.axis('off')
    plt.show()

# # %% Plot some examples
# pretty_plot(data[0, 0, 0])
# pretty_plot(data[0, 0, 1])
# pretty_plot(data[0, 0, 2])
# %%
# model = models.Cellpose(model_type='cyto2', gpu=True)

# %%
# membrane_idx = 0
# nucleus_idx = 2
# masks, flows, styles, diams = model.eval(
#     data[0],
#     anisotropy=10,
#     diameter=120,
#     channels=[membrane_idx + 1, nucleus_idx + 1],
#     channel_axis=1,
#     do_3D=True
# )
# # %% Show an example
# idx = 7
# pplot_img_mask(data[0, idx, membrane_idx], masks[idx])
# # %%


import collections as coll

from utils import *

def find_options(cur):
    options = set()
    for i in range(len(cur)):
        if cur[i:i+2] in ins:
            for ii in ins[cur[i:i+2]]:
                options.add(cur[0:i] + ii + cur[i + 2:])
        if cur[i] in ins:
            for ii in ins[cur[i]]:
                options.add(cur[0:i] + ii + cur[i+1:])
    return options

ins = coll.defaultdict(list)

def solve(d):
    global ins
    ins = coll.defaultdict(list)
    target = ""

    ww = words(d)
    for line in ww:
        if len(line) == 0:
            continue
        if len(line) == 1:
            target = line[0]
            break
        ins[line[0]].append(line[2])

    # I at first did a brute force and it didn't work. I got insipired by some reddit comments which are genious!
    # A few simple obversavtions are important. Lets call any molecul X except of Rn, Ar and Y,
    # which are special moleculs that are used to build other moleculs.
    # We either produce X => XX
    # Or we produce a complicated X => X RN X AR | X => X RN X Y X AR | X => X => X RN X Y X Y X AR
    # We can simplify this by calling it X=>X(X) ; X=>X(X,X) ; X=> X(X,X,X)
    # So in the simple case we reduce per step by 1 and in the complicated case we reduce by 3,5 or 7
    # XXX => X are two steps. X => e is one step, so if we just have some amount of X, that amount is the amount of steps
    # But we can reduce the amount of steps depending on ( and ,
    # For ( we get from 4,6,8 mols to 1, depending on the amount of , (in one step)
    # X(X,X,X)X = 3 steps, X(X,X,X)=>X : 1, XX => e : 2
    # X(X,X)X = 3 steps, X(X,X)=>X : 1, XX => e : 2
    # X(X)X = 3 steps, X(X)=>X : 1, XX => e : 2
    # So can we just count X and substract the amount of , ?

    simple_string = target.replace('Rn', "(")
    simple_string = simple_string.replace('Ar', ")")
    simple_string = simple_string.replace('Y', ",")
    for i in ins:
        simple_string = simple_string.replace(i, 'X')

    return len(find_options(target)), \
           simple_string.count('X') - simple_string.count(',') - int(simple_string.count(',') != 0)  #207

def main():
    if test():
        solutions = solve(inp())
        print("\n\n" + BColors.HEADER + "Solutions" + BColors.ENDC)
        for s in solutions:
            print(s)
    else:
        print("\n\n" + BColors.FAIL + "Not All Test Successful" + BColors.ENDC)

def test():
    s = """e => H
e => O
H => HO
H => OH
O => HH

HOHOHO"""

    a1 = 7
    a2 = 6
    return validate_solution(solve(s), (a1, a2))

if __name__ == '__main__':
    main()


from Auto_Augment.core.mnist import get_mnist, data_generator, SimpleModel, get_and_compile_model
from Auto_Augment.core.util.supervised_loop import supervised_train_loop
from ray import tune

def run_trial(lr=1e-5, batch_size=16, epochs=5):
    print("--------------------")
    print(lr, batch_size, epochs)
    print("--------------------")
    train, val, test = get_mnist()
    model = get_and_compile_model(SimpleModel, lr=lr)
    loss, val_loss, acc, val_acc = supervised_train_loop(model, train, test, data_generator,
                                                         batch_size=batch_size, epochs=epochs, debug=True)
    return max(val_acc)

def training_func(config):
    import tensorflow as tf
    lr, batch_size, epochs = config['lr'], config['batch_size'], config['epochs']
    val_acc = run_trial(lr, batch_size, epochs)
    tune.report(mean_loss=val_acc)

if __name__ == "__main__":
    from Auto_Augment.core.util import set_memory_growth
    import time

    train, val, test = get_mnist()

    analysis = tune.run(
        training_func,
        config={
            "lr": tune.choice([1e-5]),
            "batch_size": tune.choice([16]),
            "epochs": tune.choice([5])
        },
        resources_per_trial={'cpu': 8, 'gpu': 1},
        num_samples=1
    )


"""The bridge data file definitions and functions for any horde worker type."""

from __future__ import annotations

import pathlib
import re
import urllib.parse

from loguru import logger
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from strenum import StrEnum

from horde_sdk.ai_horde_api.consts import ALCHEMY_FORMS
from horde_sdk.ai_horde_worker.locale_info.bridge_data_fields import BRIDGE_DATA_FIELD_DESCRIPTIONS
from horde_sdk.generic_api.consts import ANON_API_KEY

_UNREASONABLE_NUMBER_OF_MODELS = 1000
"""1000"""

class MetaInstruction(StrEnum):
    ALL_REGEX = r"all$"

    TOP_N_REGEX = r"TOP (\d+)"
    """The regex to use to match the top N models. The number is in a capture group on its own."""

    BOTTOM_N_REGEX = r"BOTTOM (\d+)"

class BaseHordeBridgeData(BaseModel):
    """The base bridge data file for all worker types."""

    model_config = ConfigDict(extra="allow")

    @model_validator(mode="after")  # type: ignore # FIXME: https://github.com/python/mypy/issues/15620
    def validate_extra_params_warning(self) -> BaseHordeBridgeData:
        """Warn on extra parameters being passed."""
        if not self.model_extra:
            return self

        for key in self.model_extra:
            logger.warning(f"Unknown parameter {key} in bridge data file.")

        return self

class SharedHordeBridgeData(BaseHordeBridgeData):
    """The base bridge data file for all worker types."""

    #
    # Shared Worker-type Settings
    #

    api_key: str = Field(
        default=ANON_API_KEY,
        title="API Key",
    )
    """The API key to use to authenticate with the Horde API. Defaults to the anonymous API key."""

    default_worker_name: str = Field("An Awesome AI Horde Worker", alias="worker_name")
    """The default name for any worker type. This is used to easily identify the worker.

    Note that if the user switches to a different worker type after having run a different worker type, the name will
    be rejected by the Horde API. This is because the name is unique to the worker type.
    """

    models_folder_parent: str = Field(
        default="./",
        alias="cache_home",
    )
    """The directories to use to store the model folders. The spirit of this derives from `XDG_CACHE_HOME`, but it is
    not a *volatile* cache. It is the ordinary location for the models. Defaults to the current working directory."""

    temp_dir: str = "./tmp/"
    """The directory to use for temporary files such as model caches. Defaults to `./tmp/`."""

    allow_unsafe_ip: bool = True
    """Whether to allow img2img from unsafe IP addresses. This is only relevant if img2img is enabled."""

    priority_usernames: list[str] = Field(default_factory=list)
    """A list of usernames to prioritize. The API always considers this worker's user to be on this list."""

    stats_output_frequency: int = Field(default=30, json_schema_extra={"unit": "seconds"})
    """The frequency at which to output stats to the console, in seconds. Set to 0 to disable."""
    disable_terminal_ui: bool = False
    """Whether to disable the terminal UI. This is useful for running the worker in a container."""
    always_download: bool = True
    """Whether to always download models, without prompting for permission for each one."""
    suppress_speed_warnings: bool = False
    """Whether to suppress warnings about slower than ideal generations."""
    horde_url: str = ""  # TODO: consts.DEFAULT_HORDE_API_URL
    """The URL of the Horde API to use. Defaults to the official API."""

    @field_validator("horde_url")
    def validate_url(cls, v: str) -> str:
        """Validate the URL is valid."""
        parsed_url = urllib.parse.urlparse(v)
        if not parsed_url.scheme or not parsed_url.netloc:
            raise ValueError(f"Invalid URL: {v}")
        return v

    @field_validator("api_key")
    def validate_api_key_length(cls, v: str) -> str:
        """Validate that the API key is the correct length."""
        if v == ANON_API_KEY:
            return v
        if len(v) != 22:
            raise ValueError("API key must be 22 characters long")
        return v

    @field_validator("models_folder_parent", "temp_dir")
    def validate_is_dir(cls, v: str) -> str:
        """Validate that the path is a directory."""
        paths_list = []
        # Does it specific multiple directories?
        if ";" in v:
            for sub_v in v.split(";"):
                paths_list.append(sub_v)
        else:
            paths_list.append(v)
        for sub_v in paths_list:
            # See if it's a well-formed path, but don't check if it exists
            pathlib.Path(sub_v).absolute()

        return v

class ImageWorkerBridgeData(SharedHordeBridgeData):
    """The bridge data file for a Dreamer or Alchemist worker."""

    #
    # Dreamer
    #

    allow_controlnet: bool = False
    """Whether to allow the use of ControlNet. This requires img2img to be enabled."""

    allow_img2img: bool = True
    """Whether to allow the use of img2img."""

    allow_lora: bool = False
    """Whether to allow the use of LoRA."""

    max_lora_cache_size: int = Field(
        default=10,
        ge=10,
        le=2048,  # I do not foresee anyone having a 2TB lora cache
        json_schema_extra={"unit": "GB"},
    )
    """The maximum size of the LoRA cache, in gigabytes."""

    allow_inpainting: bool = Field(
        default=False,
        alias="allow_painting",
    )
    """Whether to allow the use of inpainting."""

    allow_post_processing: bool = False
    """Whether to allow the use of post-processing."""

    blacklist: list[str] = Field(
        default_factory=list,
    )
    """A list of terms or phrases to blacklist. Jobs containing these terms will be not be assigned to this worker."""

    censor_nsfw: bool = False
    """Whether to censor NSFW content."""

    censorlist: list[str] = Field(
        default_factory=list,
    )
    """A list of terms to allow through the censor. Jobs containing these terms will not be censored for those words
    alone."""

    disable_disk_cache: bool = False
    """Whether to disable the disk cache."""

    dreamer_worker_name: str = Field(
        default="An Awesome Dreamer",
        alias="dreamer_name",
    )

    dynamic_models: bool = False
    """Whether to dynamically switch the models loaded to correspond to the models with the longest queues."""

    max_dynamic_models_to_download: int = Field(
        default=10,
        ge=0,
        le=_UNREASONABLE_NUMBER_OF_MODELS,
        alias="max_models_to_download",
    )
    """The maximum number of models to download."""

    number_of_dynamic_models_to_load: int = Field(
        default=1,
        ge=0,
        le=_UNREASONABLE_NUMBER_OF_MODELS,
        alias="number_of_dynamic_models",
    )
    """The number of dynamic models to load at a time."""

    max_power: int = Field(
        default=8,
        ge=1,
        le=512,
        json_schema_extra={"unit": "* (8 * 64 * 64) pixels"},
    )
    """The factor in the equation `max_power * (8 * 64 * 64)`. This will be the maximum number of pixels that can be
    generated (with inference) in a single job by this worker."""

    image_models_to_load: list[str] = Field(
        default_factory=list,
        alias="models_to_load",
    )

    """A list of models to load. This can be a list of model names, or a list of model loading instructions, such as
    `top 100` or `all models`."""

    image_models_to_skip: list = Field(
        default_factory=list,
        alias="models_to_skip",
    )
    """When a meta model loading instruction is given, like `top 100`, models in this list will be skipped."""

    nsfw: bool = True
    """Whether to allow NSFW content."""

    queue_size: int = Field(default=1, ge=0, le=4)
    """The number of jobs to queue."""

    max_threads: int = Field(
        default=1,
        ge=1,
        le=16,  # 8 already seems incredibly out of reach for most people, but I have set it to 16 just in case
    )
    """The number of threads to use for inference."""

    require_upfront_kudos: bool = False
    """Whether to require upfront kudos for jobs. This effectively makes the worker reject jobs from
    the anonymous API key, or from keys with no kudos."""

    ram_to_leave_free: str = Field(
        default="80%",
        pattern=r"^\d+%$|^\d+$",
        json_schema_extra={"unit": "Percent or MB"},
    )
    """The amount of RAM to leave free for the system to use. This is a percentage or a number in megabytes."""

    vram_to_leave_free: str = Field(
        default="80%",
        pattern=r"^\d+%$|^\d+$",
        json_schema_extra={"unit": "Percent or MB"},
    )
    """The amount of VRAM to leave free for the system to use. This is a percentage or a number in megabytes."""

    #
    # Alchemist Settings
    #

    alchemist_name: str = "An Awesome Alchemist"
    """The name of the worker if it is an alchemist. This is used to easily identify the worker."""

    forms: list[str] = ["caption", "nsfw", "interrogation", "post-process"]
    """The type of services or processing an alchemist worker will provide."""

    @model_validator(mode="after")  # type: ignore # FIXME: https://github.com/python/mypy/issues/15620
    def validate_param_conflict(self) -> ImageWorkerBridgeData:
        """Validate that the parameters are not conflicting."""
        if not self.allow_img2img and self.allow_controlnet:
            logger.warning(
                (
                    "allow_controlnet is set to True, but allow_img2img is set to False. "
                    "ControlNet requires img2img to be enabled. Setting allow_controlnet to false."
                ),
            )
            self.allow_controlnet = False
        return self

    _meta_load_instructions: list[str] | None = None

    @property
    def meta_load_instructions(self) -> list[str] | None:
        """The meta load instructions."""
        return self._meta_load_instructions

    @model_validator(mode="after")
    def handle_meta_instructions(self) -> ImageWorkerBridgeData:
        # See if any entries are meta instructions, and if so, remove them and place them in _meta_load_instructions
        for instruction_regex in MetaInstruction.__members__.values():
            for i, model in enumerate(self.image_models_to_load):
                if re.match(instruction_regex, model, re.IGNORECASE):
                    if self._meta_load_instructions is None:
                        self._meta_load_instructions = []
                    self._meta_load_instructions.append(model)
                    self.image_models_to_load.pop(i)

        return self

    @field_validator("image_models_to_load")
    def validate_models_to_load(cls, v: list) -> list:
        """Validate and parse the models to load."""
        if not isinstance(v, list):
            v = [v]
        if not v:
            v = ["top 2"]
            logger.warning("No models specified in bridgeData.yaml. Defaulting to top 2 models.")
        return v

    @field_validator("ram_to_leave_free", "vram_to_leave_free")
    def validate_ram_to_leave_free(cls, v: str | int | float) -> str | int | float:
        """Validate VRAM/RAM to leave free."""
        if isinstance(v, str):
            if v.isdigit():
                v = int(v)
            elif v.endswith("%"):
                _ = float(v[:-1])
                if _ < 0 or _ > 100:
                    raise ValueError("RAM to leave free must be between 0 and 100%")
        else:
            if v < 0:
                raise ValueError("RAM to leave free must be greater than or equal to 0")
        return v

    @field_validator("forms")
    def validate_alchemy_forms(cls, v: list) -> list:
        """Validate the alchemy forms (services offered)."""
        if not isinstance(v, list):
            raise ValueError("forms must be a list")
        validated_forms = []
        for form in v:
            form = str(form).lower()
            form = form.replace("-", "_")
            if form not in ALCHEMY_FORMS.__members__:
                raise ValueError(f"Invalid form: {form}")
            validated_forms.append(form)
        return validated_forms

class ScribeBridgeData(SharedHordeBridgeData):
    """The bridge data file subset for a Scribe worker."""

    # Scribe Settings
    scribe_name: str = "An Awesome Scribe"
    kai_url: str = "http://localhost:5000"
    """The URL of the KoboldAI API to use. Defaults to the official API."""

    max_context_length: int = Field(
        default=1024,
        ge=1,
        le=16384,  # This is just a sanity check, I doubt anyone would want to go this high
        json_schema_extra={"unit": "tokens"},
    )
    """The maximum number of tokens to use for context with a scribe job."""

    max_length: int = Field(
        default=80,
        ge=1,
        le=16384,  # This is just a sanity check, I doubt anyone would want to go this high
        json_schema_extra={"unit": "tokens"},
    )
    """The maximum number of tokens to generate for a scribe job."""

    branded_model: bool = True
    """If true, this is a custom scribe model, and the horde API will identify uniquely to this worker's API key."""

class CombinedHordeBridgeData(ImageWorkerBridgeData, ScribeBridgeData):
    """The bridge data file for a worker bridge that supports all worker types."""

# Get all BaseModels from the current file
ALL_DATA_FILES_TYPES: list[type[BaseModel]] = [
    SharedHordeBridgeData,
    ImageWorkerBridgeData,
    ScribeBridgeData,
    CombinedHordeBridgeData,
]

# Dynamically add descriptions to the fields of all the base models
for data_file_type in ALL_DATA_FILES_TYPES:
    for field_name, field in data_file_type.model_fields.items():
        if field_name in BRIDGE_DATA_FIELD_DESCRIPTIONS:
            field.description = BRIDGE_DATA_FIELD_DESCRIPTIONS[field_name]


import os
import sys
import scipy.io as sio

from readfiles import *

EPS = np.finfo(float).eps

whiskers=[
    "RA0","RA1","RA2","RA3","RA4",
    "RB0","RB1","RB2","RB3","RB4",#"RB5",
    "RC0","RC1", "RC2","RC3","RC4","RC5",
    "RD0","RD1","RD2","RD3","RD4","RD5",
    "RE1","RE2","RE3","RE4","RE5"]

whisker_id=[(0,0),(0,1),(0,2),(0,3),(0,4),
    (1,0),(1,1),(1,2),(1,3),(1,4),#(1,5),
    (2,0),(2,1),(2,2),(2,3),(2,4),(2,5),
    (3,0),(3,1),(3,2),(3,3),(3,4),(3,5),
    (4,1),(4,2),(4,3),(4,4),(4,5)]

def smooth(x,window_len=11,window='hanning'):
    # https://scipy.github.io/old-wiki/pages/Cookbook/SignalSmooth.html

    s=np.r_[x[window_len-1:0:-1],x,x[-2:-window_len-1:-1]]
    if window == 'flat': #moving average
        w=np.ones(window_len,'d')
    else:
        w=eval('np.'+window+'(window_len)')

    y=np.convolve(w/w.sum(),s,mode='valid')
    return y[int(window_len/2-1):-int(window_len/2)]

def get_xyzc(dirname, whiskername):

    filename = dirname + '/kinematics/x/' + whiskername + '.csv'
    x = read_csv(filename)

    filename = dirname + '/kinematics/y/' + whiskername + '.csv'
    y = read_csv(filename)

    filename = dirname + '/kinematics/z/' + whiskername + '.csv'
    z = read_csv(filename)

    filename = dirname + '/kinematics/c/' + whiskername + '.csv'
    c = read_csv(filename)
    x =  np.array(x).T
    y =  np.array(y).T
    z =  np.array(z).T
    c =  np.array(c).T

    return x, y, z, c

def get_M(dirname,smoothon=True):

    filename = dirname + '/dynamics/Mx.csv'
    Mx = read_csv(filename)

    filename = dirname + '/dynamics/My.csv'
    My = read_csv(filename)

    filename = dirname + '/dynamics/Mz.csv'
    Mz = read_csv(filename)

    Mx =  np.array(Mx).T
    My =  np.array(My).T
    Mz =  np.array(Mz).T

    if smoothon:
        for i in range(len(Mz)):
            Mx[i,:-1] = smooth(Mx[i,:],window_len=11)[1:-1]
            My[i,:-1] = smooth(My[i,:],window_len=11)[1:-1]
            Mz[i,:-1] = smooth(Mz[i,:],window_len=11)[1:-1]

    return Mx, My, Mz

def get_F(dirname,smoothon=True):
    filename = dirname + '/dynamics/Fx.csv'
    Fx = read_csv(filename)

    filename = dirname + '/dynamics/Fy.csv'
    Fy = read_csv(filename)

    filename = dirname + '/dynamics/Fz.csv'
    Fz = read_csv(filename)

    Fx =  np.array(Fx).T
    Fy =  np.array(Fy).T
    Fz =  np.array(Fz).T

    if smoothon:
        for i in range(len(Fz)):
            Fx[i,:-1] = smooth(Fx[i,:],window_len=11)[1:-1]
            Fy[i,:-1] = smooth(Fy[i,:],window_len=11)[1:-1]
            Fz[i,:-1] = smooth(Fz[i,:],window_len=11)[1:-1]

    return Fx, Fy, Fz

def read_whiskit_data(whiskers=['RA0'],pathin='',pathout='',parapath='',t0=0,tf=None):

    Mx,My,Mz = get_M(pathin)
    M = np.array([Mx,My,Mz])
    Fx,Fy,Fz = get_F(pathin)
    F = np.array([Fx,Fy,Fz])

    X = []
    Y = []
    Z = []
    C = []
    for w in whiskers:

        x,y,z,c = get_xyzc(pathin,w)
        X.append(x)
        Y.append(y)
        Z.append(z)
        C.append(c)

    C = np.array(C)
    X = np.array(X)
    Y = np.array(Y)
    Z = np.array(Z)

    if tf==None:
        tf = M.shape[2]

    M = M[:,:,t0:tf]
    F = F[:,:,t0:tf]
    X = X[:,:-1,t0:tf]
    Y = Y[:,:-1,t0:tf]
    Z = Z[:,:-1,t0:tf]
    C = C[:,:,t0:tf]

    if parapath != '':
        params = read_txt(parapath)
        print(params.shape)
    else:
        params=[]

    np.savez(pathout+'.npz',M=M,F=F,Col=C,X=X,Y=Y,Z=Z,simparam=params)
    sio.savemat(pathout+'.mat',dict(M=M,F=F,Col=C,X=X,Y=Y,Z=Z,simparam=params))
    print('Data loaded. Saved in: '+pathout)


from __future__ import print_function
import os
import json
import uproot
import hist
import numpy as np
from scipy.optimize import curve_fit
from scipy.stats import moyal
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import mplhep as hep
import re
hep.set_style(hep.style.CMS)

def calc_mean(x, y):
    return sum(x * y) / sum(y)

def calc_sigma(x, y):
    mean = calc_mean(x, y)
    return np.sqrt(sum(y * (x - mean) ** 2) / sum(y))

def gauss(x, p0, p1, x0, sigma):
    return p0 + p1 * np.exp(-((x - x0) ** 2) / (2 * sigma**2))

def landau(x, H, A, x0, sigma):
    return H + A * moyal.pdf(x, x0, sigma)

def landau_gauss(x, p0, p1, x0, sigma):
    # return moyal.pdf(x,x0,sigma)*gauss(x,H,A,x0,sigma)
    return p0 + p1 * np.exp(-((x - x0) ** 2) / (2 * sigma**2)) * moyal.pdf(
        x, x0, sigma
    )

def cms_label(exp_label, year, fs, ax):
    if "private" in exp_label.lower():
        hep.label._exp_label(
            exp="",
            llabel=exp_label,
            year=year,
            ax=ax,
            fontsize=fs,
            data=False
        )
    else:
        hep.cms.label(llabel="Simulation "+exp_label, year=year, fontsize=fs, ax=ax, data=False)
        # hep.cms.label(year=year, fontsize=fs, ax=ax, data=False)

class UnfoldingToyClosure(object):
    def __init__(self, modeldir, workdir, n_toys, name="ToyClosure"):
        self._modeldir = os.path.abspath(modeldir)
        if not os.path.isfile(modeldir+"/model_combined.root"):
            raise FileNotFoundError("Could not locate model_combined.root in model dir. "
                                    "Either provided directory is wrong, or workspace has not been created yet.")
        self._workdir = os.path.abspath(workdir)
        self._cmssw_dir = (
            "/afs/desy.de/user/a/albrechs/xxl/af-cms/UHH2/10_6_28/CMSSW_10_6_28/src/UHH2/JetMass/rhalph/"
            "CMSSW_11_3_4/src"
        )
        self._toys_per_job = 1
        self._n_toys = n_toys
        if n_toys % self._toys_per_job != 0:
            raise ArithmeticError("Number of toys has to be a multiple of {}!".format(self._toys_per_job))
        self._n_jobs = int(n_toys/self._toys_per_job)

        self._name = name

        self._config = json.load(open(self._modeldir + "/config.json", "r"))

        self._pois = ["r_" + genbin for genbin in self._config.get("genbins", [""])]

        if self._pois == ["r_"]:
            raise RuntimeError("could not find any genbins in config.")

        self._initial_seed = 123456
        self._base_seed = int(self._initial_seed*10**(np.floor(np.log10(self._n_toys))+1))

        if not os.path.exists(self._workdir):
            os.makedirs(self._workdir)

    def submit_toys(self, debug=False):
        os.chdir(self._workdir)
        for i in range(self._n_jobs):
            cmd = "ulimit -s unlimited;"
            cmd += (
                "combineTool.py -M FitDiagnostics -d {}/model_combined.root  --cminDefaultMinimizerStrategy 0 ".format(
                    self._modeldir
                )
            )
            cmd += "--redefineSignalPOIs {} ".format(",".join(self._pois))
            cmd += "--setParameters {}=1 ".format("=1,".join(self._pois))
            cmd += "--robustFit 1 "
            cmd += "--robustHesse 1 "
            cmd += "-n .{}{} ".format(self._name, i)
            cmd += "--seed {} ".format(self._base_seed + i)
            cmd += "-t {} ".format(self._toys_per_job)
            cmd += "--trackParameters rgx{r_.*} "
            cmd += "--trackErrors rgx{r_.*} "
            cmd += "--job-mode condor "
            # cmd += "--memory 4 "
            cmd += "--task-name {}_job_{} ".format(self._name, i)
            if debug:
                print(cmd)
            else:
                os.system(cmd)

    def get_parameters(self, i_job):  # , fit_result_name="fit_s"):
        fname = "{}/higgsCombine.{}{}.FitDiagnostics.mH120.{}.root".format(
            self._workdir, self._name, i_job, self._base_seed + i_job
        )
        dummy = np.array([[]]*len(self._pois)).T
        if not os.path.isfile(fname):
            print("fit {} produced no output".format(i_job))
            return dummy
        try:
            file_ = uproot.open(fname)
            limit = file_["limit"]
        except BaseException as e:
            print("fit {} produced no fit result.".format(i_job))
            print(e)
            return dummy
        result_arrays = limit.arrays(["trackedParam_{}".format(poi) for poi in self._pois])
        error_arrays = limit.arrays(["trackedError_{}".format(poi) for poi in self._pois])
        # reformat and get rid of duplicate entries
        result_arrays = np.array([result_arrays["trackedParam_{}".format(poi).encode()][::4] for poi in self._pois])
        error_arrays = np.array([error_arrays["trackedError_{}".format(poi).encode()][::4] for poi in self._pois])

        # transpose and reshape to return array of type [poi, toy] instead of [toy, poi]
        n_pois, n_toy_valid = result_arrays.shape

        result_arrays = result_arrays.T.reshape(n_toy_valid, n_pois)
        error_arrays = error_arrays.T.reshape(n_toy_valid, n_pois)
        return result_arrays, error_arrays
        # fname = "{}/fitDiagnostics.{}{}.root".format(self._workdir, self._name, itoy)
        # file_ = ROOT.TFile.Open(fname, "READ")
        # try:
        #     fit_result = file_.Get(fit_result_name)
        #     fit_result_pars = fit_result.floatParsFinal()
        # except BaseException:
        #     print("could not find fit result ({}) in provided file (itoy={}).".format(fit_result_name, itoy))
        #     return None

        # poi_result_tmp = {}
        # for p in fit_result_pars:
        #     if p.GetName() in self._pois or len(self._pois) == 0:
        #         poi_result_tmp[p.GetName()] = [p.getVal(), p.getErrorHi(), p.getErrorLo()]
        # return [np.array([poi_result_tmp[name][i] for name in self._pois]) for i in [0, 1, 2]]

    def plot_toys(self):
        # toy_postfit = np.array([arr for arr in [self.get_parameters(ijob) for ijob in range(self._n_jobs)] if arr])
        results = [self.get_parameters(ijob) for ijob in range(self._n_jobs)]

        mu = np.concatenate([r[0] for r in results if len(r) > 0])
        sigma = np.concatenate([r[0] for r in results if len(r) > 0])
        pull = (mu - 1.0) / sigma
        n_toys = len(pull[:, 0])
        print("extracted fitresults for {}/{} toys".format(n_toys, self._n_toys))

        for ipoi, poi in enumerate(self._pois):
            f, ax = plt.subplots()
            hep.histplot(
                np.histogram(mu[:, ipoi], bins=2000),
                ax=ax,
                lw=2,
                color="k",
                label=r"$N_\mathrm{toys}=%i$ (mean=%.2f, std=%.2f)" % (
                    n_toys, np.mean(mu[:, ipoi]), np.std(mu[:, ipoi]),
                ),
            )
            ax.set_ylabel("toys")
            ax.set_xlabel(poi)

            f.savefig("{}/{}_toys.pdf".format(self._modeldir, poi), bbox_inches="tight")

            # xmin = max(0, np.floor(postfit_results.min()))
            # xmax = min(10, np.ceil(postfit_results.max()))
            # nbins = int(np.ceil(np.sqrt(n_toys)))
            xmin = -2.0
            xmax = 2.0
            nbins = 40

            postfit_results = pull[:, ipoi]
            h = hist.Hist.new.Reg(nbins, xmin, xmax, name=poi).Int64()
            h.fill(postfit_results)

            # fit hist with some distribution
            x = h.axes[0].centers
            y = h.values()
            fit_func = gauss

            try:
                popt, pcov = curve_fit(
                    fit_func, x, y, p0=[min(y), max(y), calc_mean(x, y), calc_sigma(x, y)]
                )
            except RuntimeError:
                print("fit failed for poi: {}".format(poi))

            f, ax = plt.subplots()

            cms_label(exp_label="Work in progress", year=self._config.get("year", "UL17"), ax=ax, fs=20)

            # hep.label._exp_label(
            #     exp="",
            #     llabel="Private work (CMS data/simulation)",
            #     year=self._config.get("year", "UL17"),
            #     ax=ax,
            #     fontsize=20,
            # )

            hep.histplot(
                h,
                label=r"$N_\mathrm{toys}=%i$ (mean=%.2f, width=%.2f, sum=%i)" % (
                    n_toys, calc_mean(x, y), calc_sigma(x, y), y.sum()
                ),
                ax=ax,
                yerr=False,
            )
            # x_fine = np.linspace(xmin, xmax, 1000)
            # ax.plot(x_fine, fit_func(x_fine, *popt), label=r"fit ($\mu_=%.2f, \sigma=%.2f$)" % (popt[2], popt[3]))

            poi_pattern_match = re.search("r_ptgen(?P<ipt>[0-9])_msdgen(?P<imsd>[0-9])", poi)
            if poi_pattern_match:
                x_label = r"$\frac{\hat{\mu}_{{p_{T}^{%s}}}^{{m_{\mathrm{SD}}^{%s}}}- \mu}{\hat{\sigma}}$" % (
                    poi_pattern_match.groupdict()["ipt"],
                    poi_pattern_match.groupdict()["imsd"],
                )
            else:
                x_label = poi
            ax.set_xlabel(x_label)
            ax.set_ylabel("toys")
            ax.legend()
            range_str = "pull_{}to{}".format(str(int(xmin)).replace("-", "m"), str(int(xmax)))
            f.savefig("{}/{}_toys_{}.pdf".format(self._modeldir, poi, range_str), bbox_inches="tight")

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument("-d", "--model", help="model dir containing workspace 'model_combined.root'.")
    parser.add_argument("-w", "--workdir", help="workdir to perform fits in")
    parser.add_argument("-t", "--toys", help="number of toys to produce/fit", type=int, default=500)
    parser.add_argument("-s", "--submit", action="store_true", help="submit toys")
    parser.add_argument("-p", "--plots", action="store_true", help="plot finished toys")
    parser.add_argument(
        "--debug", action="store_true", help="just print cmd and don't execute. (does not affect plotting task)"
    )
    args = parser.parse_args()

    if args.workdir == "" or args.workdir is None:
        args.workdir = args.model+"/CombineToolWorkdir/"

    print(args.model)
    print(args.workdir)

    uct = UnfoldingToyClosure(modeldir=args.model, workdir=args.workdir, name="ToyTest", n_toys=args.toys)
    if args.submit:
        uct.submit_toys(args.debug)
    if args.plots:
        uct.plot_toys()


#!/bin/python3

#######################################
# RPi SSD1306 I2C display service
#
# Taka Kitazume
# Version 0.2
# March 1, 2021
#######################################

import time
import socket
import busio
from PIL import Image, ImageDraw, ImageFont
import adafruit_ssd1306

from flask import Flask, request

# GLOBAL
# I2C 0 interface
SCL    = 1
SDA    = 0
HEIGHT = 32
WIDTH  = 128
TOP    = -2
BOTTOM = HEIGHT - TOP
LEFT   = 2
X0     = 0
Y0     = 0
X1     = WIDTH
Y1     = HEIGHT

i2c   = busio.I2C(SCL, SDA)
disp  = adafruit_ssd1306.SSD1306_I2C(WIDTH, HEIGHT, i2c)
image = Image.new("1", (WIDTH, HEIGHT))
draw  = ImageDraw.Draw(image)
font  = ImageFont.load_default()

# Flask server
app = Flask(__name__)

def getIpAddress():
    ip = None
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.connect(("8.8.8.8", 80))
    ip = s.getsockname()[0]
    s.close()
    return ip

def setText(text, x=0, y=0, font=font):
    draw.text((LEFT + x, TOP + y), text=text, font=font, fill=255)

def setHostnameIP():
    hostname = socket.gethostname()
    ip = getIpAddress()
    setText(hostname)
    setText("IP: " + ip, y=10)

def clearArea(x0=X0, y0=Y0, x1=X1, y1=Y1):
    draw.rectangle((x0, y0, x1, y1), outline=0, fill=0)
    disp.image(image)
    disp.show()

def clearDisplay():
    clearArea()

def showDisplay():
    disp.image(image)
    disp.show()

@app.route('/showmessage', methods=['GET'])
def showMessage():
    clearArea(y0=20)
    text = request.args.get('text','')
    setText(text, y=20)
    showDisplay()
    return "Success"

@app.route('/shutdown', methods=['GET'])
def shutdown():
    clearDisplay()
    return "Success"

def main():
    clearDisplay()
    setHostnameIP()
    showDisplay()

if __name__ == '__main__':
    main()
    app.debug = True # Debug
    app.run(host='127.0.0.1', port='5000') # Accept access from any host


from gevent import monkey;

monkey.patch_all()
import gevent
import time
from multiprocessing import Process
from queue import Queue

from settings import *
from vpsClinet import VpsClient
from proxyPool import ProxyPool

"""
拨号策略：
    定时拨号，间隔拨号
    协程拨号
代理使用策略：
    阈值：超过阈值拨号
"""

class Schedule(object):

    def __init__(self):
        """
        初始化 redis，vps
        """

        self._proxy_pool = ProxyPool()

        self._vps_list = []
        for vps_data in vps_pool:
            vps_cli = VpsClient(**vps_data)
            self._vps_list.append(vps_cli)

    def dial(self, vps: VpsClient) -> str:
        """
        单次拨号, 并保存
        :param vps:
        :return:
        """

        vps.open_ssh()

        old_server = vps.current_server
        vps.adsl_stop()
        vps.adsl_start()
        server = vps.get_ip()

        vps.close_ssh()

        # 清除旧代理
        if self._proxy_pool.isExist(old_server):
            self._proxy_pool.delete(old_server)

        self._proxy_pool.add([server])
        logger.success(f"[vps]: [{vps.vps_name}] replace proxy {server}")

        return server

    def run_interval_dial(self):
        """
        间隔拨号
        :return:
        """

        # 清空池
        self._proxy_pool.delete(all=True)

        while True:

            # 协程拨号
            jobs = []
            for vps in self._vps_list:
                job = gevent.spawn(self.dial, vps)

                jobs.append(job)

            gevent.joinall(jobs)

            time.sleep(dial_interval)

    def run_time_dial(self):
        """
        次数拨号，超过次数阈值则拨号换IP

        :return:
        """

        # 清空池
        self._proxy_pool.delete(all=True)

        while True:
            jobs = []

            if self._proxy_pool.count() == 0:
                logger.info(f"[dial]: gt score threshold [{dial_threshold}] - {len(self._vps_list)}")
                for vps in self._vps_list:
                    job = gevent.spawn(self.dial, vps)
                    jobs.append(job)

            else:
                servers = self._proxy_pool.gt_score_threshold()
                logger.info(f"[dial]: gt score threshold [{dial_threshold}] - {len(servers)}")
                for server in servers:
                    for vps in self._vps_list:
                        if vps.current_server == server:
                            job = gevent.spawn(self.dial, vps)
                            jobs.append(job)

            gevent.joinall(jobs)
            time.sleep(1)

if __name__ == '__main__':
    sche = Schedule()

    sche.run_interval_dial()
    # sche.run_time_dial()

    ...


from .views import ProductListView ,ProductDetailView,deleteProduct,UpdateProductView,add_product, myProductsListView
from django.urls import path
app_name = 'product'

urlpatterns=[
    path('home/',ProductListView.as_view(), name="home"),
    path(r'?P<slug>/',ProductDetailView.as_view(),name="product_detail"),
    path(r'delete/?P<slug>/',deleteProduct,name="product_delete"),
    path(r'update/product/?P<slug>/',UpdateProductView,name="product_update"),
    path(r'product_add/',add_product,name='add_product'),
    path(r'myProduct/',myProductsListView,name="my_products"),

]


from __future__ import division
import math

import torch
import torch.nn as nn
import torch.nn.functional as F

from torch.autograd import Variable
from torch.nn.utils.rnn import pack_padded_sequence as pack
from torch.nn.utils.rnn import pad_packed_sequence as unpack

import onmt
from onmt.Utils import aeq

def rnn_factory(rnn_type, **kwargs):
    # Use pytorch version when available.
    no_pack_padded_seq = False
    if rnn_type == "SRU":
        # SRU doesn't support PackedSequence.
        no_pack_padded_seq = True
        rnn = onmt.modules.SRU(**kwargs)
    else:
        rnn = getattr(nn, rnn_type)(**kwargs)
    return rnn, no_pack_padded_seq

class EncoderBase(nn.Module):
    """
    Base encoder class. Specifies the interface used by different encoder types
    and required by :obj:`onmt.Models.NMTModel`.

    .. mermaid::

       graph BT
          A[Input]
          subgraph RNN
            C[Pos 1]
            D[Pos 2]
            E[Pos N]
          end
          F[Memory_Bank]
          G[Final]
          A-->C
          A-->D
          A-->E
          C-->F
          D-->F
          E-->F
          E-->G
    """
    def _check_args(self, input, lengths=None, hidden=None):
        s_len, n_batch, n_feats = input.size()
        if lengths is not None:
            n_batch_, = lengths.size()
            aeq(n_batch, n_batch_)

    def forward(self, src, lengths=None, encoder_state=None):
        """
        Args:
            src (:obj:`LongTensor`):
               padded sequences of sparse indices `[src_len x batch x nfeat]`
            lengths (:obj:`LongTensor`): length of each sequence `[batch]`
            encoder_state (rnn-class specific):
               initial encoder_state state.

        Returns:
            (tuple of :obj:`FloatTensor`, :obj:`FloatTensor`):
                * final encoder state, used to initialize decoder
                * memory bank for attention, `[src_len x batch x hidden]`
        """
        raise NotImplementedError

class MeanEncoder(EncoderBase):
    """A trivial non-recurrent encoder. Simply applies mean pooling.

    Args:
       num_layers (int): number of replicated layers
       embeddings (:obj:`onmt.modules.Embeddings`): embedding module to use
    """
    def __init__(self, num_layers, embeddings):
        super(MeanEncoder, self).__init__()
        self.num_layers = num_layers
        self.embeddings = embeddings

    def forward(self, src, lengths=None, encoder_state=None):
        "See :obj:`EncoderBase.forward()`"
        self._check_args(src, lengths, encoder_state)

        emb = self.embeddings(src)
        s_len, batch, emb_dim = emb.size()
        mean = emb.mean(0).expand(self.num_layers, batch, emb_dim)
        memory_bank = emb
        encoder_final = (mean, mean)
        return encoder_final, memory_bank

class RNNEncoder(EncoderBase):
    """ A generic recurrent neural network encoder.

    Args:
       rnn_type (:obj:`str`):
          style of recurrent unit to use, one of [RNN, LSTM, GRU, SRU]
       bidirectional (bool) : use a bidirectional RNN
       num_layers (int) : number of stacked layers
       hidden_size (int) : hidden size of each layer
       dropout (float) : dropout value for :obj:`nn.Dropout`
       embeddings (:obj:`onmt.modules.Embeddings`): embedding module to use
    """
    def __init__(self, rnn_type, bidirectional, num_layers,
                 hidden_size, dec_hidden_size, dropout=0.0, embeddings=None,
                 use_bridge=False):
        super(RNNEncoder, self).__init__()
        assert embeddings is not None
        self.dropout = nn.Dropout(dropout)

        num_directions = 2 if bidirectional else 1
        assert hidden_size % num_directions == 0
        hidden_size = hidden_size // num_directions
        self.embeddings = embeddings

        self.rnn, self.no_pack_padded_seq = \
            rnn_factory(rnn_type,
                        input_size=embeddings.embedding_size,
                        hidden_size=hidden_size,
                        num_layers=num_layers,
                        dropout=dropout,
                        bidirectional=bidirectional)

        # Initialize the bridge layer
        self.use_bridge = use_bridge
        if self.use_bridge:
            self._initialize_bridge(rnn_type,
                                    hidden_size,
                                    dec_hidden_size,
                                    num_layers)

    def forward(self, src, lengths=None, encoder_state=None, emb=None):
        "See :obj:`EncoderBase.forward()`"
        self._check_args(src, lengths, encoder_state)

        if emb is None:
            emb = self.dropout(self.embeddings(src))
        s_len, batch, emb_dim = emb.size()
        self.emb_h = emb
        packed_emb = emb
        if lengths is not None and not self.no_pack_padded_seq:
            # Lengths data is wrapped inside a Variable.
            lengths = lengths.view(-1).tolist()
            packed_emb = pack(emb, lengths)

        memory_bank, encoder_final = self.rnn(packed_emb, encoder_state)

        if lengths is not None and not self.no_pack_padded_seq:
            memory_bank = unpack(memory_bank)[0]
        self.enc_h = memory_bank

        if self.use_bridge:
            encoder_final = self._bridge(encoder_final)
        return encoder_final, memory_bank

    def _initialize_bridge(self, rnn_type,
                           hidden_size,
                           output_size,
                           num_layers):

        # LSTM has hidden and cell state, other only one
        number_of_states = 2 if rnn_type == "LSTM" else 1
        # Total number of states
        self.total_hidden_dim = hidden_size * num_layers
        self.total_out_dim = output_size * num_layers

        # Build a linear layer for each
        self.bridge = nn.ModuleList([nn.Linear(self.total_hidden_dim,
                                               self.total_out_dim // number_of_states,
                                               bias=True)
                                     for i in range(number_of_states)])

    def _bridge(self, hidden):
        """
        Forward hidden state through bridge
        """
        def bottle_hidden(linear, states):
            """
            Transform from 3D to 2D, apply linear and return initial size
            """
            size = states.size()
            result = linear(states.view(-1, self.total_hidden_dim))
            #return F.relu(result).view(size)
            return result.view(size[0], size[1], -1)

        if isinstance(hidden, tuple):  # LSTM
            outs = tuple([bottle_hidden(layer, hidden[ix])
                          for ix, layer in enumerate(self.bridge)])
        else:
            outs = bottle_hidden(self.bridge[0], hidden)
        return outs

class RNNDecoderBase(nn.Module):
    """
    Base recurrent attention-based decoder class.
    Specifies the interface used by different decoder types
    and required by :obj:`onmt.Models.NMTModel`.

    .. mermaid::

       graph BT
          A[Input]
          subgraph RNN
             C[Pos 1]
             D[Pos 2]
             E[Pos N]
          end
          G[Decoder State]
          H[Decoder State]
          I[Outputs]
          F[Memory_Bank]
          A--emb-->C
          A--emb-->D
          A--emb-->E
          H-->C
          C-- attn --- F
          D-- attn --- F
          E-- attn --- F
          C-->I
          D-->I
          E-->I
          E-->G
          F---I

    Args:
       rnn_type (:obj:`str`):
          style of recurrent unit to use, one of [RNN, LSTM, GRU, SRU]
       bidirectional_encoder (bool) : use with a bidirectional encoder
       num_layers (int) : number of stacked layers
       hidden_size (int) : hidden size of each layer
       attn_type (str) : see :obj:`onmt.modules.GlobalAttention`
       coverage_attn (str): see :obj:`onmt.modules.GlobalAttention`
       context_gate (str): see :obj:`onmt.modules.ContextGate`
       copy_attn (bool): setup a separate copy attention mechanism
       dropout (float) : dropout value for :obj:`nn.Dropout`
       embeddings (:obj:`onmt.modules.Embeddings`): embedding module to use
    """
    def __init__(self, rnn_type, bidirectional_encoder, num_layers,
                 memory_size, hidden_size, attn_size, attn_type="general",
                 coverage_attn=False, context_gate=None,
                 copy_attn=False, dropout=0.0, embeddings=None,
                 reuse_copy_attn=False):
        super(RNNDecoderBase, self).__init__()

        # Basic attributes.
        self.decoder_type = 'rnn'
        self.bidirectional_encoder = bidirectional_encoder
        self.num_layers = num_layers
        self.memory_size = memory_size
        self.hidden_size = hidden_size
        self.attn_size = attn_size
        self.embeddings = embeddings
        self.dropout = nn.Dropout(dropout)

        # Build the RNN.
        self.rnn = self._build_rnn(rnn_type,
                                   input_size=self._input_size,
                                   hidden_size=hidden_size,
                                   num_layers=num_layers,
                                   dropout=dropout)

        # Set up the context gate.
        self.context_gate = None
        if context_gate is not None:
            self.context_gate = onmt.modules.context_gate_factory(
                context_gate, self._input_size,
                hidden_size, hidden_size, hidden_size
            )

        # Set up the standard attention.
        self._coverage = coverage_attn
        self.attn = onmt.modules.GlobalAttention(
            src_dim = memory_size,
            tgt_dim = hidden_size,
            attn_dim = attn_size,
            coverage=coverage_attn,
            attn_type=attn_type,
        )

        # Set up a separated copy attention layer, if needed.
        self._copy = False
        if copy_attn and not reuse_copy_attn:
            self.copy_attn = onmt.modules.GlobalAttention(
                hidden_size, attn_type=attn_type
            )
        if copy_attn:
            self._copy = True
        self._reuse_copy_attn = reuse_copy_attn

    def forward(self, tgt, memory_bank, state, memory_lengths=None):
        """
        Args:
            tgt (`LongTensor`): sequences of padded tokens
                                `[tgt_len x batch x nfeats]`.
            memory_bank (`FloatTensor`): vectors from the encoder
                 `[src_len x batch x hidden]`.
            state (:obj:`onmt.Models.DecoderState`):
                 decoder state object to initialize the decoder
            memory_lengths (`LongTensor`): the padded source lengths
                `[batch]`.
        Returns:
            (`FloatTensor`,:obj:`onmt.Models.DecoderState`,`FloatTensor`):
                * decoder_outputs: output from the decoder (after attn)
                         `[tgt_len x batch x hidden]`.
                * decoder_state: final hidden state from the decoder
                * attns: distribution over src at each tgt
                        `[tgt_len x batch x src_len]`.
        """
        # Check
        assert isinstance(state, RNNDecoderState)
        tgt_len, tgt_batch, _ = tgt.size()
        _, memory_batch, _ = memory_bank.size()
        aeq(tgt_batch, memory_batch)
        # END

        # Run the forward pass of the RNN.
        decoder_final, decoder_outputs, input_feed, attns = self._run_forward_pass(
            tgt, memory_bank, state, memory_lengths=memory_lengths)

        # Update the state with the result.
        final_output = decoder_outputs[-1]
        coverage = None
        if "coverage" in attns:
            coverage = attns["coverage"][-1].unsqueeze(0)
        state.update_state(decoder_final, input_feed.unsqueeze(0), coverage)

        # Concatenates sequence of tensors along a new dimension.
        decoder_outputs = torch.stack(decoder_outputs)
        for k in attns:
            attns[k] = torch.stack(attns[k])

        return decoder_outputs, state, attns

    def init_decoder_state(self, src, memory_bank, encoder_final):
        def _fix_enc_hidden(h):
            # The encoder hidden is  (layers*directions) x batch x dim.
            # We need to convert it to layers x batch x (directions*dim).
            if self.bidirectional_encoder:
                h = torch.cat([h[0:h.size(0):2], h[1:h.size(0):2]], 2)
            return h

        if isinstance(encoder_final, tuple):  # LSTM
            # Zero out initial hidden state
            L = self.rnn.num_layers
            N = encoder_final[0].size(1)
            H = self.rnn.hidden_size
            return RNNDecoderState(self.hidden_size,
                                   #tuple([_fix_enc_hidden(enc_hid)
                                         #for enc_hid in encoder_final]),
                                   (
                                        torch.zeros(L, N, H).to(encoder_final[0]),
                                        torch.zeros(L, N, H).to(encoder_final[0]),
                                   ),
                                   self.memory_size)
        else:  # GRU
            return RNNDecoderState(self.hidden_size,
                                   _fix_enc_hidden(encoder_final))

class StdRNNDecoder(RNNDecoderBase):
    """
    Standard fully batched RNN decoder with attention.
    Faster implementation, uses CuDNN for implementation.
    See :obj:`RNNDecoderBase` for options.

    Based around the approach from
    "Neural Machine Translation By Jointly Learning To Align and Translate"
    :cite:`Bahdanau2015`

    Implemented without input_feeding and currently with no `coverage_attn`
    or `copy_attn` support.
    """
    def _run_forward_pass(self, tgt, memory_bank, state, memory_lengths=None):
        """
        Private helper for running the specific RNN forward pass.
        Must be overriden by all subclasses.
        Args:
            tgt (LongTensor): a sequence of input tokens tensors
                                 [len x batch x nfeats].
            memory_bank (FloatTensor): output(tensor sequence) from the encoder
                        RNN of size (src_len x batch x hidden_size).
            state (FloatTensor): hidden state from the encoder RNN for
                                 initializing the decoder.
            memory_lengths (LongTensor): the source memory_bank lengths.
        Returns:
            decoder_final (Variable): final hidden state from the decoder.
            decoder_outputs ([FloatTensor]): an array of output of every time
                                     step from the decoder.
            attns (dict of (str, [FloatTensor]): a dictionary of different
                            type of attention Tensor array of every time
                            step from the decoder.
        """
        assert not self._copy  # TODO, no support yet.
        assert not self._coverage  # TODO, no support yet.

        # Initialize local and return variables.
        attns = {}
        emb = self.dropout(self.embeddings(tgt))

        # Run the forward pass of the RNN.
        if isinstance(self.rnn, nn.GRU):
            rnn_output, decoder_final = self.rnn(emb, state.hidden[0])
        else:
            rnn_output, decoder_final = self.rnn(emb, state.hidden)

        # Check
        tgt_len, tgt_batch, _ = tgt.size()
        output_len, output_batch, _ = rnn_output.size()
        aeq(tgt_len, output_len)
        aeq(tgt_batch, output_batch)
        # END

        # Calculate the attention.
        decoder_outputs, p_attn = self.attn(
            rnn_output.transpose(0, 1).contiguous(),
            memory_bank.transpose(0, 1),
            memory_lengths=memory_lengths
        )
        attns["std"] = p_attn

        # Calculate the context gate.
        if self.context_gate is not None:
            decoder_outputs = self.context_gate(
                emb.view(-1, emb.size(2)),
                rnn_output.view(-1, rnn_output.size(2)),
                decoder_outputs.view(-1, decoder_outputs.size(2))
            )
            decoder_outputs = \
                decoder_outputs.view(tgt_len, tgt_batch, self.hidden_size)

        decoder_outputs = self.dropout(decoder_outputs)
        return decoder_final, decoder_outputs, attns

    def _build_rnn(self, rnn_type, **kwargs):
        rnn, _ = rnn_factory(rnn_type, **kwargs)
        return rnn

    @property
    def _input_size(self):
        """
        Private helper returning the number of expected features.
        """
        return self.embeddings.embedding_size

class InputFeedRNNDecoder(RNNDecoderBase):
    """
    Input feeding based decoder. See :obj:`RNNDecoderBase` for options.

    Based around the input feeding approach from
    "Effective Approaches to Attention-based Neural Machine Translation"
    :cite:`Luong2015`

    .. mermaid::

       graph BT
          A[Input n-1]
          AB[Input n]
          subgraph RNN
            E[Pos n-1]
            F[Pos n]
            E --> F
          end
          G[Encoder]
          H[Memory_Bank n-1]
          A --> E
          AB --> F
          E --> H
          G --> H
    """

    def _run_forward_pass(self, tgt, memory_bank, state, memory_lengths=None):
        """
        See StdRNNDecoder._run_forward_pass() for description
        of arguments and return values.
        """
        # Additional args check.
        input_feed = state.input_feed.squeeze(0)
        input_feed_batch, _ = input_feed.size()
        tgt_len, tgt_batch, _ = tgt.size()
        aeq(tgt_batch, input_feed_batch)
        # END Additional args check.

        # Initialize local and return variables.
        decoder_outputs = []
        attns = {"std": []}
        if self._copy:
            attns["copy"] = []
        if self._coverage:
            attns["coverage"] = []

        emb = self.dropout(self.embeddings(tgt))
        assert emb.dim() == 3  # len x batch x embedding_dim

        hidden = state.hidden
        coverage = state.coverage.squeeze(0) \
            if state.coverage is not None else None

        # Input feed concatenates hidden state with
        # input at every time step.
        # DBG
        self.p_attn_score = []
        self.dec_h = []
        self.src_context = []
        self.context = []
        for i, emb_t in enumerate(emb.split(1)):
            emb_t = emb_t.squeeze(0)
            decoder_input = torch.cat([emb_t, input_feed], 1)

            rnn_output, hidden = self.rnn(decoder_input.unsqueeze(0), hidden)
            rnn_output = rnn_output.squeeze(0)
            decoder_output, p_attn, input_feed = self.attn(
                rnn_output,
                memory_bank.transpose(0, 1),
                memory_lengths=memory_lengths)
            # DBG
            self.dec_h.append(rnn_output)
            self.p_attn_score.append(self.attn.p_attn_score)
            self.src_context.append(input_feed)
            self.context.append(decoder_output)

            if self.context_gate is not None:
                # TODO: context gate should be employed
                # instead of second RNN transform.
                decoder_output = self.context_gate(
                    decoder_input, rnn_output, decoder_output
                )
            decoder_output = self.dropout(decoder_output)
            #input_feed = decoder_output

            decoder_outputs += [decoder_output]
            attns["std"] += [p_attn]

            # Update the coverage attention.
            if self._coverage:
                coverage = coverage + p_attn \
                    if coverage is not None else p_attn
                attns["coverage"] += [coverage]

            # Run the forward pass of the copy attention layer.
            if self._copy and not self._reuse_copy_attn:
                _, copy_attn = self.copy_attn(decoder_output,
                                              memory_bank.transpose(0, 1))
                attns["copy"] += [copy_attn]
            elif self._copy:
                attns["copy"] = attns["std"]
        # Return result.
        return hidden, decoder_outputs, input_feed, attns

    def _build_rnn(self, rnn_type, input_size,
                   hidden_size, num_layers, dropout):
        assert not rnn_type == "SRU", "SRU doesn't support input feed! " \
                "Please set -input_feed 0!"
        if rnn_type == "LSTM":
            stacked_cell = onmt.modules.StackedLSTM
        else:
            stacked_cell = onmt.modules.StackedGRU
        return nn.LSTM(num_layers=num_layers, input_size=input_size,
                       hidden_size=hidden_size, dropout=dropout)

    @property
    def _input_size(self):
        """
        Using input feed by concatenating input with attention vectors.
        """
        return self.embeddings.embedding_size + self.memory_size

class NMTModel(nn.Module):
    """
    Core trainable object in OpenNMT. Implements a trainable interface
    for a simple, generic encoder + decoder model.

    Args:
      encoder (:obj:`EncoderBase`): an encoder object
      decoder (:obj:`RNNDecoderBase`): a decoder object
      multi<gpu (bool): setup for multigpu support
    """
    def __init__(self, encoder, decoder, multigpu=False):
        self.multigpu = multigpu
        super(NMTModel, self).__init__()
        self.encoder = encoder
        self.decoder = decoder
        self.mode = None

    def forward(self, src, tgt, lengths, dec_state=None):
        """Forward propagate a `src` and `tgt` pair for training.
        Possible initialized with a beginning decoder state.

        Args:
            src (:obj:`Tensor`):
                a source sequence passed to encoder.
                typically for inputs this will be a padded :obj:`LongTensor`
                of size `[len x batch x features]`. however, may be an
                image or other generic input depending on encoder.
            tgt (:obj:`LongTensor`):
                 a target sequence of size `[tgt_len x batch]`.
            lengths(:obj:`LongTensor`): the src lengths, pre-padding `[batch]`.
            dec_state (:obj:`DecoderState`, optional): initial decoder state
        Returns:
            (:obj:`FloatTensor`, `dict`, :obj:`onmt.Models.DecoderState`):

                 * decoder output `[tgt_len x batch x hidden]`
                 * dictionary attention dists of `[tgt_len x batch x src_len]`
                 * final decoder state
        """
        tgt = tgt[:-1]  # exclude last target from inputs

        enc_final, memory_bank = self.encoder(src, lengths)
        enc_state = \
            self.decoder.init_decoder_state(src, memory_bank, enc_final)

        decoder_outputs, dec_state, attns = \
            self.decoder(tgt, memory_bank,
                         enc_state if dec_state is None
                         else dec_state,
                         memory_lengths=lengths)
        if self.multigpu:
            # Not yet supported on multi-gpu
            dec_state = None
            attns = None
        return decoder_outputs, attns, dec_state, None, None

class DecoderState(object):
    """Interface for grouping together the current state of a recurrent
    decoder. In the simplest case just represents the hidden state of
    the model.  But can also be used for implementing various forms of
    input_feeding and non-recurrent models.

    Modules need to implement this to utilize beam search decoding.
    """
    """
    def detach(self):
        for h in self._all:
            if h is not None:
                h.detach_()
    """
    def detach(self):
        def det(h):
            if h is not None:
                return h.detach()
        return self.__new__(
            type(self),
            det(self.input_feed),
            self.hidden_size,
            tuple(det(x) for x in self.hidden),
        )

    def beam_update(self, idx, positions, beam_size):
        for e in self._all:
            sizes = e.size()
            br = sizes[1]
            if len(sizes) == 3:
                sent_states = e.view(sizes[0], beam_size, br // beam_size,
                                     sizes[2])[:, :, idx]
            else:
                sent_states = e.view(sizes[0], beam_size,
                                     br // beam_size,
                                     sizes[2],
                                     sizes[3])[:, :, idx]

            sent_states.data.copy_(
                sent_states.data.index_select(1, positions))

class RNNDecoderState(DecoderState):
    def __init__(self, hidden_size, rnnstate, context_size=None):
        """
        Args:
            hidden_size (int): the size of hidden layer of the decoder.
            rnnstate: final hidden state from the encoder.
                transformed to shape: layers x batch x (directions*dim).
        """
        if not isinstance(rnnstate, tuple):
            self.hidden = (rnnstate,)
        else:
            self.hidden = rnnstate
        self.coverage = None
        self.hidden_size = hidden_size

        # Init the input feed.
        batch_size = self.hidden[0].size(1)
        h_size = (batch_size, context_size if context_size is not None else hidden_size)
        self.input_feed = Variable(self.hidden[0].data.new(*h_size).zero_(),
                                   requires_grad=False).unsqueeze(0)

    @property
    def _all(self):
        return self.hidden + (self.input_feed,)

    def update_state(self, rnnstate, input_feed, coverage):
        if not isinstance(rnnstate, tuple):
            self.hidden = (rnnstate,)
        else:
            self.hidden = rnnstate
        self.input_feed = input_feed
        self.coverage = coverage

    def repeat_beam_size_times(self, beam_size):
        """ Repeat beam_size times along batch dimension. """
        vars = [Variable(e.data.repeat(1, beam_size, 1), volatile=True)
                for e in self._all]
        self.hidden = tuple(vars[:-1])
        self.input_feed = vars[-1]

class Generator(nn.Module):
    def __init__(self, in_dim, out_dim, mode="sample"):
        super(Generator, self).__init__()
        self.proj = nn.Linear(in_dim, out_dim)
        self.mode = mode

    def logsumexp(self, x, dim=0, keepdim=False):
        m = x.max(dim)[0]
        if keepdim:
            m = m.unsqueeze(dim)
            return m + (x-m).exp().sum(dim, keepdim=keepdim).log()
        else:
            return m + (x-m.unsqueeze(dim)).exp().sum(dim, keepdim=keepdim).log()

    def forward(self, input, log_pa=None, pa=None):
        # log_pa: T x N x S=K
        scores = F.log_softmax(self.proj(input), dim=-1) # target, K, batch, vocab
        if input.dim() == 3:
            # Short-circuit
            return scores
        if scores.size(1) == 1:
            scores = scores.squeeze(1)
        else:
            if self.mode == "exact" and log_pa is not None:
                # for exact marginal
                scores = scores + log_pa.transpose(1,2).unsqueeze(-1)
                scores = self.logsumexp(scores, dim=1, keepdim=False)
            elif self.mode == "enum" and pa is not None:
                # for exact elbo
                scores = scores * pa.transpose(1,2).unsqueeze(-1)
                scores = scores.sum(dim=1, keepdim=False)
            elif self.mode == "wsram":
                return scores
            else:
                scores = self.logsumexp(scores, dim=1, keepdim=False)
                scores = scores - math.log(input.size(1))
        return scores


#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @FileName  :tune.py
# @Time      :2021/11/20 下午7:42
# @Author    :Miracleyin
# @Mail      :miracleyin@live.com
import argparse
import os
from recbole.trainer import HyperTuning
from recbole.quick_start import objective_function
from recbole.utils import init_logger, ensure_dir, get_local_time
from rec_run import objective_run
import multiprocessing

def tune(model=None, dataset=None, params_name=None):
    from itertools import product
    current_path = os.path.dirname(os.path.realpath(__file__))
    # base config file
    overall_init_file = os.path.join(current_path, 'config/overall.yaml')
    # model config file
    model_init_file = os.path.join(current_path, 'config/model/' + model + '.yaml')
    # dataset config file
    dataset_init_file = os.path.join(current_path, 'config/dataset/' + dataset + '.yaml')

    config_file_list = [overall_init_file, model_init_file, dataset_init_file]  # env model dataset

    # hyper tune space
    params_space = os.path.join(current_path, 'config/params/' + params_name + '.params')

    # plz set algo='exhaustive' to use exhaustive search, in this case, max_evals is auto set
    hp = HyperTuning(objective_run, algo='exhaustive',
                     params_file=params_space, fixed_config_file_list=config_file_list)
    hp.run()
    ensure_dir('pgcc_3_7') # 自己手动记录一下实验开始的日期
    hp.export_result(output_file=f'pgcc_3_7/{model}-{dataset}-{params_name}.result')
    print('best params: ', hp.best_params)
    print('best result: ')
    print(hp.params2result[hp.params2str(hp.best_params)])

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument("--model", type=str, action='store', help="model name")
    parser.add_argument("--dataset", type=str, action='store', help="dataset name")
    parser.add_argument('--params', type=str, default=None, help='parameters file')
    args, unknown = parser.parse_known_args()

    model_name = args.model
    dataset_name = args.dataset
    params_name = args.params

    tune(model_name, dataset_name, params_name)


from typing import TextIO

def read(input_file: TextIO) -> list[list[int]]:

    lst1 = []
    x = input_file.readlines()
    for i in x:
        i = i.split()
        if '\n' in i:
            i.remove('\n')
        lst1.append(i)
    return lst1

reader = open('hdbhb', 'r')
print(read(reader))


import pygame
from src.Video.VisualiserOneLiner import VisualiserOneLiner

class CommandModule:

    def __init__(self, x, y, w, h):
        self.current = ""
        self.visualiser = VisualiserOneLiner(x, y, w, h)
        self.visualiser.update_text("> " + self.current)
        self.history = [""]
        self.index_h = 0

    def receive_input(self, events):

        for event in [e for e in events if e.type == pygame.KEYDOWN]:

            if event.key == pygame.K_UP:
                self.index_h = self.index_h - 1 if abs(self.index_h) < len(self.history) else self.index_h
                self.current = self.history[self.index_h]

            elif event.key == pygame.K_DOWN:
                self.index_h = self.index_h + 1 if self.index_h < 0 else self.index_h
                self.current = self.history[self.index_h]

            elif event.key == pygame.K_SPACE:
                self.current += " "
            elif event.key == pygame.K_BACKSPACE:
                self.current = self.current[:-1]

            elif event.key == pygame.K_RETURN:  # ___RETURN
                self.history.append(self.current.strip())
                self.current = ""  # reset
                self.index_h = 0  # reset
                self.visualiser.update_text(self.current)
                if self.history[-1] != "":
                    return self.history[-1]  # return last entry

            elif event.unicode.isalpha():  # This covers all letters and numbers
                self.current += event.unicode

        self.visualiser.update_text(self.current)
        return None

    def render_text(self):
        self.visualiser.render_text()


from django.urls import path
from . import views

urlpatterns = [
    path('index',views.index,name='index'),
    path('categorias',views.categorias,name='categorias'),
    path('periodistas',views.periodistas,name='periodistas'),
    path('contacto',views.contacto,name='contacto'),
    path('noticia1',views.noticia1,name='noticia1'),
    path('noticia2',views.noticia2,name='noticia2'),
    path('noticia3',views.noticia3,name='noticia3'),
    path('noticia4',views.noticia4,name='noticia4'),
    path('noticia5',views.noticia5,name='noticia5'),
    path('noticia6',views.noticia6,name='noticia6'),
    path('noticia7',views.noticia7,name='noticia7'),
    path('noticia8',views.noticia8,name='noticia8'),
    path('noticia9',views.noticia9,name='noticia9'),

]


import numpy as np
np.set_printoptions(linewidth=np.nan)
import matplotlib.pyplot as plt
import cv2
from face_data import load_faces
from plotting import plot_img_with_bbox

def get_bounding_boxes(im, window_size=100, shift_size=50, thresh=0.3):
    H, W = im.shape
    bboxes = {}

    def get_window(j, i):
        return im[j:j+window_size, i:i+window_size]

    for j in range(0, H-window_size, shift_size):
        for i in range(0, W-window_size, shift_size):
            window = get_window(j, i)

            # pred_class, confidence = model.predict(window)
            raise NotImplementedError

            if pred_class == None or \
               confidence < thresh:
                # no bounding box
                continue

            if pred_class:
                if pred_class not in bboxes:
                    bboxes[pred_class] = []
                bbox = (j, i, window_size, window_size)
                bboxes[pred_class].append((confidence, bbox))

    return bboxes

def non_maximal_suppression(bboxes):
    for feat in bboxes:
        if feat == 'eye':
            top_bboxes = sorted(bboxes[feat])[-2:]
        elif feat == 'nose':
            top_bboxes = sorted(bboxes[feat])[-1:]
        elif feat == 'mouth':
            top_bboxes = sorted(bboxes[feat])[-1:]
        elif feat == 'ear':
            top_bboxes = sorted(bboxes[feat])[-2:]

        bboxes[feat] = top_bboxes

    return bboxes

if __name__ == "__main__":
    # get a face image
    faces = load_faces(n=10)

    # for each face
    for face in faces:
        # zero for grayscale
        im = cv2.imread(face, 0)
        cv2.imshow('image', im)
        cv2.waitKey(0)

        continue

        # slide a window over the face and get bounding boxes
        bboxes = get_bounding_boxes(im)

        # non-maximal suppression
        bboxes = non_maximal_suppression(bboxes)

        plot_img_with_bbox(im, bboxes)
        plt.show()

    # convert to SVG
    raise NotImplementedError
    # identify SVG components
    raise NotImplementedError
    # animate
    raise NotImplementedError


"""oxpam URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/1.8/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
Including another URLconf
    1. Add an import:  from blog import urls as blog_urls
    2. Add a URL to urlpatterns:  url(r'^blog/', include(blog_urls))
"""
from django.conf.urls import include, url
from django.contrib import admin
from pam.views import config, devices, update_devices, add_name

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^$', include('pam.urls')),
    url(r'^config$', config, name="config" ),
    url(r'^devices$', devices, name="devices"),
    url(r'^update_devices$', update_devices, name="update devices"),
    url(r'^add_name', add_name, name="add name"),
]