from __future__ import absolute_import, division, print_function

import os
import cv2
import numpy as np
from glob import glob
import torch
from torch.utils.data import DataLoader

from layers import disp_to_depth
from utils import readlines
from options import MonodepthOptions
import datasets
import networks
from layers import *
from auto_morph import *
from bnmorph.bnmorph import BNMorph
cv2.setNumThreads(0)  # This speeds up evaluation 5x on our unix systems (OpenCV 3.3.1)

splits_dir = os.path.join(os.path.dirname(__file__), "splits")

# Models which were trained with stereo supervision were trained with a nominal
# baseline of 0.1 units. The KITTI rig has a baseline of 54cm. Therefore,
# to convert our stereo predictions to real-world scale we multiply our depths by 5.4.
STEREO_SCALE_FACTOR = 5.4

def save_loss(lossarrnp, add):
    scale_fac = 16777216
    lossarrnp_scaled = (lossarrnp * scale_fac).astype(np.int64)
    img_bgr = np.zeros((lossarrnp.shape[0], lossarrnp.shape[1], 3), np.int)
    img_bgr[:, :, 0] = lossarrnp_scaled // 65536
    img_bgr[:, :, 1] = (lossarrnp_scaled % 65536) // 256
    img_bgr[:, :, 2] = (lossarrnp_scaled % 65536) % 256
    cv2.imwrite(add, img_bgr)

def compute_errors(gt, pred):
    """Computation of error metrics between predicted and ground truth depths
    """
    thresh = np.maximum((gt / pred), (pred / gt))
    a1 = (thresh < 1.25     ).mean()
    a2 = (thresh < 1.25 ** 2).mean()
    a3 = (thresh < 1.25 ** 3).mean()

    rmse = (gt - pred) ** 2
    rmse = np.sqrt(rmse.mean())

    rmse_log = (np.log(gt) - np.log(pred)) ** 2
    rmse_log = np.sqrt(rmse_log.mean())

    abs_rel = np.mean(np.abs(gt - pred) / gt)

    sq_rel = np.mean(((gt - pred) ** 2) / gt)

    abs_shift = np.mean(np.abs(gt - pred))

    return abs_rel, sq_rel, rmse, rmse_log, a1, a2, a3, abs_shift

def batch_post_process_disparity(l_disp, r_disp):
    """Apply the disparity post-processing method as introduced in Monodepthv1
    """
    _, h, w = l_disp.shape
    m_disp = 0.5 * (l_disp + r_disp)
    l, _ = np.meshgrid(np.linspace(0, 1, w), np.linspace(0, 1, h))
    l_mask = (1.0 - np.clip(20 * (l - 0.05), 0, 1))[None, ...]
    r_mask = l_mask[:, :, ::-1]
    return r_mask * l_disp + l_mask * r_disp + (1.0 - l_mask - r_mask) * m_disp

def evaluate(opt):
    """Evaluates a pretrained model using a specified test set
    """
    MIN_DEPTH = 1e-3
    MAX_DEPTH = 80
    selfOccluMask = SelfOccluMask().cuda()
    selfOccluMask.th = 0
    if opt.isCudaMorphing and opt.borderMorphLoss:
        bnmorph = BNMorph(height=opt.height, width=opt.width, sparsityRad=2).cuda()
    assert sum((opt.eval_mono, opt.eval_stereo)) == 1, \
        "Please choose mono or stereo evaluation by setting either --eval_mono or --eval_stereo"

    if opt.ext_disp_to_eval is None:

        opt.load_weights_folder = os.path.expanduser(opt.load_weights_folder)

        assert os.path.isdir(opt.load_weights_folder), \
            "Cannot find a folder at {}".format(opt.load_weights_folder)

        filenames = readlines(os.path.join(splits_dir, opt.split_name, opt.appendix_name + ".txt"))
        encoder_path = os.path.join(opt.load_weights_folder, "encoder.pth")
        decoder_path = os.path.join(opt.load_weights_folder, "depth.pth")

        encoder_dict = torch.load(encoder_path)

        dataset = datasets.KITTIRAWDataset(opt.data_path, filenames,
                                           encoder_dict['height'], encoder_dict['width'],
                                           [0, 's'], 4, is_train=False, tag=opt.dataset, img_ext = 'png', load_meta=opt.load_meta, is_load_semantics=opt.use_kitti_gt_semantics, is_predicted_semantics = opt.is_predicted_semantics)

        dataloader = DataLoader(dataset, 2, shuffle=False, num_workers=opt.num_workers, drop_last=False)

        encoder = networks.ResnetEncoder(opt.num_layers, False, num_input_images = 2)
        depth_decoder = networks.DepthDecoder(encoder.num_ch_enc, isSwitch=(opt.switchMode == 'on'), isMulChannel=opt.isMulChannel, outputtwoimage = (opt.outputtwoimage == True))

        model_dict = encoder.state_dict()
        encoder.load_state_dict({k: v for k, v in encoder_dict.items() if k in model_dict})
        depth_decoder.load_state_dict(torch.load(decoder_path))

        encoder.cuda()
        encoder.eval()
        depth_decoder.cuda()
        depth_decoder.eval()

        pred_disps = []
        mergeDisp = Merge_MultDisp(opt.scales, batchSize = opt.batch_size)

        count = 0
        tottime = 0

        if not os.path.isdir(opt.output_dir):
            os.mkdir(opt.output_dir)

        with torch.no_grad():
            for data in dataloader:
                # input_colorl = torch.cat([data[("color", 0, 0)], data[("color", 's', 0)]], dim=1).cuda()
                # input_colorr = torch.cat([data[("color", 's', 0)], data[("color", 0, 0)]], dim=1).cuda()
                # input_color = torch.cat([input_colorl, input_colorr], dim=0)
                start = time.time()
                input_color = torch.cat([data[("color", 0, 0)], data[("color", 's', 0)]], dim=1).cuda()
                # tensor2rgb(input_color[:,0:3,:,:], ind=0).show()
                # tensor2rgb(input_color[:, 3:6, :, :], ind=0).show()
                # tensor2rgb(input_color[:, 0:3, :, :], ind=1).show()

                features = encoder(input_color)
                outputs = dict()
                outputs.update(depth_decoder(features, computeSemantic=False, computeDepth=True))

                mergeDisp(data, outputs, eval=True)

                count = count + 1
                scaled_disp, _ = disp_to_depth(outputs[("disp", 0)], opt.min_depth, opt.max_depth)
                pred_disp = scaled_disp
                pred_disp = pred_disp.cpu()[:, 0].numpy()

                real_scale_disp = scaled_disp * (torch.abs(data[("K", 0)][:, 0, 0] * data["stereo_T"][:, 0, 3]).view(opt.batch_size, 1, 1,1).expand_as(scaled_disp)).cuda()
                SSIMMask = selfOccluMask(real_scale_disp, data["stereo_T"][:, 0, 3].cuda())

                store_path = filenames[data['idx'][0].numpy()].split(' ')
                folder1 = os.path.join(opt.output_dir, store_path[0].split('/')[0])
                folder2 = os.path.join(opt.output_dir, store_path[0])
                folder3 = os.path.join(folder2, 'image_02')
                folder4 = os.path.join(folder2, 'image_03')
                if not os.path.isdir(folder1):
                    os.mkdir(folder1)
                if not os.path.isdir(folder2):
                    os.mkdir(folder2)
                if not os.path.isdir(folder3):
                    os.mkdir(folder3)
                if not os.path.isdir(folder4):
                    os.mkdir(folder4)
                if opt.outputvisualizaiton:
                    folder5 = os.path.join(folder2, 'image_02_compose')
                    folder6 = os.path.join(folder2, 'image_03_compose')
                    if not os.path.isdir(folder5):
                        os.mkdir(folder5)
                    if not os.path.isdir(folder6):
                        os.mkdir(folder6)
                    a = outputs[("disp", 0)] * (1 - SSIMMask)
                    fig1 = tensor2disp(a, ind=0, vmax=0.15)
                    fig2 = tensor2disp(a, ind=1, vmax=0.15)
                    fig1.save(os.path.join(folder5, store_path[1].zfill(10) + '.png'))
                    fig2.save(os.path.join(folder6, store_path[1].zfill(10) + '.png'))
                pathl = os.path.join(folder3, store_path[1].zfill(10) + '.png')
                pathr = os.path.join(folder4, store_path[1].zfill(10) + '.png')

                # fig1 = tensor2disp(outputs[("disp", 0)], ind=1, vmax=0.1)
                # fig2 = tensor2disp(outputs[("disp", 0)] * (1 - SSIMMask), ind=1, vmax=0.1)
                # fig_combined = np.concatenate([np.array(fig1), np.array(fig2)], axis=0)
                # pil.fromarray(fig_combined).show()
                real_scale_disp = real_scale_disp * (1 - SSIMMask)
                stored_disp = real_scale_disp / 960
                save_loss(stored_disp[0, 0, :, :].cpu().numpy(), pathl)
                save_loss(stored_disp[1, 0, :, :].cpu().numpy(), pathr)

                duration = time.time() - start
                tottime = tottime + duration
                print("left time %f hours" % (tottime / count * (len(filenames) - count) / 60 / 60))

if __name__ == "__main__":
    options = MonodepthOptions()
    parsed_command = options.parse()
    if parsed_command.load_weights_folders is not None:
        folders_to_eval = glob(parsed_command.load_weights_folders + '*/')
        to_order = list()
        for i in range(len(folders_to_eval)):
            to_order.append(int(folders_to_eval[i].split('/')[-2].split('_')[1]))
        to_order = np.array(to_order)
        to_order_index = np.argsort(to_order)
        for i in to_order_index:
            print(folders_to_eval[i])
            parsed_command.load_weights_folder = folders_to_eval[i]
            evaluate(parsed_command)
    else:
        # alpha_distance_weight = np.arange(0.1, 2, 0.1)
        # pixel_mulline_distance_weight = np.arange(3, 50, 3)
        # alpha_padding = np.arange(0.1, 3, 0.1)
        #
        # for k in alpha_distance_weight:
        #     evaluate(parsed_command, alpha_distance_weight = k)
        # for k in pixel_mulline_distance_weight:
        #     evaluate(parsed_command, pixel_mulline_distance_weight=k)
        # for k in alpha_padding:
        #     evaluate(parsed_command, alpha_padding=k)
        evaluate(parsed_command)


import pygame
import random

# Inicialização do Pygame
pygame.init()

# Configurações da tela
screen_width = 600
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("Snake Game")

# Cores
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)

# Configurações da cobra
snake_block_size = 10
snake_speed = 15

# Função para desenhar a cobra na tela
def draw_snake(snake_block_size, snake_list):
    for block in snake_list:
        pygame.draw.rect(
            screen, GREEN, [block[0], block[1], snake_block_size, snake_block_size]
        )

# Loop do jogo
def gameLoop():
    game_over = False
    game_close = False

    # Inicialização da posição da cobra
    x1 = screen_width / 2
    y1 = screen_height / 2

    # Inicialização das mudanças de posição
    x1_change = 0
    y1_change = 0

    # Inicialização da lista da cobra
    snake_List = []
    Length_of_snake = 1

    # Posição aleatória da comida
    foodx = round(random.randrange(0, screen_width - snake_block_size) / 10.0) * 10.0
    foody = round(random.randrange(0, screen_height - snake_block_size) / 10.0) * 10.0

    # Loop principal
    while not game_over:
        # Game over
        while game_close == True:
            screen.fill(WHITE)
            font = pygame.font.SysFont("comicsansms", 35)
            message = font.render(
                "Você perdeu! Aperte Q para sair ou C para jogar novamente", True, RED
            )
            screen.blit(message, [screen_width / 6, screen_height / 3])

            # Atualizar a tela
            pygame.display.update()

            # Eventos do Pygame para o game over
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        game_over = True
                        game_close = False
                    if event.key == pygame.K_c:
                        gameLoop()

        # Eventos do Pygame
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_LEFT:
                    x1_change = -snake_block_size
                    y1_change = 0
                elif event.key == pygame.K_RIGHT:
                    x1_change = snake_block_size
                    y1_change = 0
                elif event.key == pygame.K_UP:
                    y1_change = -snake_block_size
                    x1_change = 0
                elif event.key == pygame.K_DOWN:
                    y1_change = snake_block_size
                    x1_change = 0

        # Atualizar posição da cobra
        x1 += x1_change
        y1 += y1_change

        # Desenhar a comida na tela
        screen.fill(WHITE)
        pygame.draw.rect(
            screen, RED, [foodx, foody, snake_block_size, snake_block_size]
        )

        # Atualizar a lista da


import json
from colorsys import hls_to_rgb  # , rgb_to_hls
from math import sqrt

THRESHOLD = 140

def get_luminocity(HSL: tuple, optional=False) -> int:

    # https://alienryderflex.com/hsp.html

    h, s, l = HSL
    r, g, b = hsl_to_rgb(h, s, l)

    optional_values = (.241, .691, .068)
    informed_values = (.299, .587, .114)

    X, Y, Z = informed_values

    if optional:
        X, Y, Z = optional_values

    luminocity = sqrt(
        r * r * X +
        g * g * Y +
        b * b * Z
    )

    return luminocity

def get_luminocity_percetage(luminocity):
    return int((luminocity/255)*100)

def is_light_text(HSL: tuple, threshhold: int = THRESHOLD) -> bool:
    """
    Check if HSL color works with white font. Default brightness threshold is set to `140` (can be safely adjusted between 130 to 145).

    In: `(h,s,l)` tuple;

    Returns `bool`:

    - `True` if text should be bright (white)
    - `False` if text should be dark (black)
    """

    brightness = get_luminocity(HSL)

    if brightness < threshhold:
        return True
    else:
        return False

def is_light_text_rgb(rgb: tuple, threshhold=THRESHOLD) -> bool:
    """
Check if RGB color works with white font. Default brightness threshold is set to `140` (can be safely adjusted between 130 to 145).

In: `(r,g,b)` tuple;

Returns `bool`:
- `True` if text should be bright (white)
- `False` if text should be dark (black)
    """

    r, g, b = rgb

    brightness = sqrt(
        r * r * .241 +
        g * g * .691 +
        b * b * .068
    )

    if brightness < threshhold:
        return True
    else:
        return False

def normalize_color(hue: int) -> tuple:
    """
Normalizes any HSLA `hue` (color, in range from 1 to 360) to the basic settings.

Incoming: `hue` from HSLA system.

Returns a tuple with normalized HSLA:

`hue`: incoming, `saturation`: 100, `lightness`: 50, `opacity`: 1"""

    if hue > 0:
        return (hue, 100, 50, 1)
    else:
        return (hue, 100, 100, 1)

def normalize_to_hundred(value: int) -> int:
    """
    Double-check if saturation or brightness contained within 0 to 100.
    """

    if value > 100:
        return 100
    elif value < 0:
        return 0
    else:
        return value

def degree_correction(hue: int) -> int:
    """
Checks if hue number is more than 360 degrees or less and returns correted number.
    """

    if hue < 0 or hue > 360:
        return hue % 360
    else:
        return hue

def hsl_to_rgb(hue: int, saturation: int, lightness: int) -> tuple:
    """
    *In:* `Hue` (0-360), `saturation` (0-100), `lightness` (0-100) as in HSL model.

    *Out:* `(r, g, b)` normalized to RGB model (tuple).
    """

    H = hue/360
    S = saturation/100
    L = lightness/100

    RGB = hls_to_rgb(H, L, S)

    r, g, b = (int(round(i*255, 0)) for i in RGB)

    return r, g, b

def rgb_to_hex(r, g, b):
    return '#{:02x}{:02x}{:02x}'.format(r, g, b)

# Hue name adjustments
cool = 'cool'
warm = 'warm'
mid = 'mid'

# Lightness name modificators
dark = 'dark'
medium = 'medium'
light = 'light'
q1 = 'darker'
q2 = 'dark'
q3 = 'light'
q4 = 'lighter'

hue_group_names = {
    ('red', cool): range(345, 345+15),
    ('red', mid): range(0, 0+15),
    ('red', warm): range(15, 15+15),
    ('orange', ''): range(30, 30+15),
    ('yellow', warm): range(45, 45+15),
    ('yellow', mid): range(60, 60+15),
    ('yellow', cool): range(75, 75+15),
    ('green', 'yellow'): range(90, 90+15),
    ('green', warm): range(105, 105+15),
    ('green', mid): range(125, 125+15),
    ('green', cool): range(135, 135+15),
    ('cyan', 'green'): range(150, 150+15),
    ('cyan', warm): range(165, 165+15),
    ('cyan', mid): range(180, 180+15),
    ('cyan', cool): range(195, 195+15),
    ('cyan', 'blue'): range(210, 210+15),
    ('blue', cool): range(225, 225+15),
    ('blue', mid): range(240, 240+15),
    ('blue', warm): range(255, 255+15),
    ('violet', ''): range(270, 270+15),
    ('magenta', cool): range(285, 285+15),
    ('magenta', mid): range(300, 300+15),
    ('magenta', warm): range(315, 315+15),
    ('magenta', 'red'): range(330, 330+15),
}

def get_hue_group_name(hue: int) -> str:
    hue = degree_correction(hue)
    for k, v in hue_group_names.items():
        if hue in v:
            if k[1]:
                return(f'{k[0]}-{k[1]}')
            elif not k[1]:
                return(f'{k[0]}')

class Color:

    name = None
    layer = None
    order = None

    def __init__(self, h: int, s=50, l=50, a=1):
        self.h = degree_correction(h)
        self.s = normalize_to_hundred(s)
        self.l = normalize_to_hundred(l)
        self.a = a
        self.hsl = (h, s, l)
        self.opposite = self.get_opposite()
        self.rgb = self.get_rgb()
        self.hex = self.get_hex_value()
        self.bright_text = is_light_text_rgb(self.rgb)
        self.normalized = normalize_color(self.h)
        self.luminocity = get_luminocity(self.hsl)
        self.luminocity_percentage = get_luminocity_percetage(self.luminocity)
        self.set_name()
        self.set_layer()
        self.set_order()

    # Setters

    def set_layer(self, layer: int = None):
        if layer == None:
            self.layer = int(self.l)
        else:
            self.layer = int(layer)

    def set_order(self, order: int = None):
        if self.order == None:
            self.order = 0
        else:
            self.order = int(order)

    def set_name(self, name: str = None):
        if self.l <= 25:
            lighness_title = q1
        elif 25 < self.l <= 50:
            lighness_title = q2
        elif 50 < self.l <= 75:
            lighness_title = q3
        elif 75 < self.l <= 100:
            lighness_title = q4

        if name == None:
            self.name = str(f'{get_hue_group_name(self.h)}-{lighness_title}')
        else:
            self.name = str(name)

    # Getters

    def get_opposite(self, type=None) -> str:
        """
        Get a color that is on the opposite side of the color wheel (hue + 180 degrees).

        Accepts `'rgb'`, `'hsl'` and `'hex'` keys (`hsl` is default and returns an hsl tuple even when argument is empty).

        Example: `get_opposite('rgb')` will return a tuple with red, green, blue values.
        """
        types = ['rgb', 'hex', 'hsl']
        opposite_hue = degree_correction(self.h + 180)
        opposite = (opposite_hue, self.s, self.l)

        if not type or type == 'hsl':
            return opposite

        if type in types:
            if type == 'rgb':
                return hsl_to_rgb(*opposite)
            elif type == 'hex':
                return rgb_to_hex(*hsl_to_rgb(*opposite))

        return opposite

    def get_opposite_class(self):
        opposite_hue = degree_correction(self.h + 180)
        return Color(opposite_hue, self.s, self.l)

    def get_rgb(self):
        return hsl_to_rgb(*self.hsl)

    def get_variable(self):
        if self.name and self.order:
            return f'--{self.name}-{self.order}'
        else:
            return f'--color-{self.l}'

    def get_hsla_value(self):
        return f'hsla({self.h}, {self.s}%, {self.l}%, {self.a})'

    def get_rgba_value(self):
        r, g, b = self.rgb
        return f'rgba({r}, {g}, {b}, {self.a})'

    def get_hex_value(self):
        return rgb_to_hex(*self.rgb)

    def get_light_bg_color(self) -> tuple:
        """
        returns H,S,L values for bg color if it is more than a given threshold, else returns values for white background.
        """

        lightBg = 100;
        lightThresholdStep = 30
        lightThreshold = lightBg - lightThresholdStep

        if self.luminocity_percentage > lightThreshold:
            return self.h, 3, lightBg - self.luminocity_percentage - 5
        else:
            return self.h, 0, 100

    def get_dark_bg_color(self):
        darkBg = 0
        darkThresholdStep = 40
        darkThreshold = darkBg + darkThresholdStep

        if self.luminocity_percentage <= darkThreshold:
            return self.h, 3, 100 - self.luminocity_percentage + 20
        else:
            return self.h, 0, 0

    def get_object(self):
        return {
            "order": self.order,
            "name": self.name,
            "var": self.get_variable(),
            "hex": self.hex,
            "rgb": self.rgb,
            "hsl": self.hsl,
            "bright_text": self.bright_text,
            "luminocity": self.luminocity,
            "luminocity_percentage": self.luminocity_percentage,
            "light_bg": self.get_light_bg_color(),
            "dark_bg": self.get_dark_bg_color(),
        }

    def get_json(self):
        return json.dumps(self.get_object())

    def __repr__(self):
        return f"Color({self.name}-{self.order}: {self.h}, {self.s}, {self.l})"

if __name__ == "__main__":

    color = {
        "h": 220,
        "s": 90,
        "l": 98,
    }

    c = Color(**color)
    print(c.hex)
    print(c.name)
    print(c.opposite)


from odoo import models, _
from odoo.exceptions import AccessError
from odoo.addons.woocommerce_integration.models.tools import do_request

class StockMove(models.Model):
    _inherit = 'stock.move'
    def _action_done(self, cancel_backorder=False):
        moves = super(StockMove, self)._action_done()
        for move in moves:
            # move.is_inventory and move.state == 'done' and
            if move.state == 'done' and move.product_tmpl_id.is_wc_connect and move.product_tmpl_id.wc_id:
                # actualizar inventario en woocommerce
                data = {
                    'stock_quantity': str(move.product_tmpl_id.qty_available)
                }
                # realizar la petición PUT para actualizar stock de producto
                response = do_request('PUT', 'products', data, move.product_tmpl_id.wc_id)
                if not response:
                    raise AccessError(_('No fue posible establecer conexión con WooCommerce'))
        return moves


from oscpy.server import OSCThreadServer
from oscpy.client import OSCClient
from functools import partial
from threading import Timer
import ps_tmcli_tmBase as tmbase

def dumdumdummyfunc(*args):
    return False

class TmOscCommunicator(tmbase.TotalmixBaseClass):

    chLimitReached = False
    checkChannelLimit = False
    lastChName = None

    numberFader = 0
    countFader = True

    timeoutFunction = dumdumdummyfunc
    timeoutT = 0.5

    finishedJobFunction = dumdumdummyfunc

    def __init__(self, ipaddress, port, answer_port, database:dict=None, timeoutFunction=dumdumdummyfunc):

        self.taskStack = list()
        self.currentLayer = ''
        self.currentChIndex = -1
        self.selectedSubmixIndex = -1

        self.currentChannelName = ''

        self.tmpChannelData = {}
        self.tmpChannelDataMode1 = {'vol': [], 'pan': [], 'name': []}

        self.timeout = Timer(10, dumdumdummyfunc)

        self.settingTargetValue = False
        self.targetParameter = ''
        self.targetValue = -1.
        self.targetOsc = ''
        self.targetChannels = set()

        self.timeoutFunction = partial(timeoutFunction, caller=self)

        self.toTM = OSCClient(address=ipaddress, port=port)
        self.oscServer = OSCThreadServer(default_handler=self.oscR_receivedTmData)
        self.oscServer.listen(address='0.0.0.0', port=answer_port, default=True)

        self.setupOscBindings()

    def setupOscBindings(self):

        for i in [1, 2]:
            for bus in ['busInput', 'busPlayback', 'busOutput']:
                oscAddr = ('/' + str(i) + '/' + bus).encode()
                self.oscServer.bind(oscAddr, partial(self.oscR_setLayer, bus, i))

            sOsc = '/{}/labelSubmix'.format(i)
            self.oscServer.bind(sOsc.encode(), partial(self.oscR_selectedSubmix, i))

        self.oscServer.bind(b'/2/trackname', partial(self.oscR_setChannelName))

        for i in range(64):
            sOSc = '/1/labelS{}'.format(i + 1)
            self.oscServer.bind(sOSc.encode(), partial(self.oscR_countRemoteFader, i + 1))
            for _key, _par in self.m1_parameters.items():
                #     if _key, _par in self.m1_parameters.items():
                sOsc = '/1/{}{}'.format(_par, i+1)
                self.oscServer.bind(sOsc.encode(), partial(self.oscR_tmpChannelDataMode1, i, _par))

        for x in range(64):
            sOsc = '/1/micgain{}Val'.format(x+1)
            self.oscServer.bind(sOsc.encode(), self.oscR_checkTmOutMode1complete, x + 1)

    def oscR_receivedTmData(self, address, *args):
        sOsc = address.decode()
        if sOsc[0:2] == '/2':
            _param = sOsc[3:]
            value = args[0] if type(args[0])==float else args[0].decode()
            self.tmpChannelData[_param] = value

            if self.settingTargetValue:
                self.checkTaskStack()
        else:
            return

    def oscR_setLayer(self, layer: str, _mode: int, *args):

        if args[0] == 1.0:
            self.currentLayer = layer

    def oscR_selectedSubmix(self, mode: int, *args):

        global selectedSubmixName, selectedSubmixIndex, changingChannel
        selectedSubmixName = args[0].decode()

        if self.fetchState['properties']:
            selectedSubmixIndex = self.channelNamesByIndex[tmbase.busOutput].index(selectedSubmixName)

        if mode == 2:
            #TM output finished
            self.checkTaskStack()
        elif mode == 1:
            return

    def oscR_setChannelName(self, *args):

        chName = args[0].decode()

        if self.checkChannelLimit:
            self.chLimitReached = self.currentChannelName == chName
        self.currentChannelName = chName

    def oscR_countRemoteFader(self, nFader, *args):
        # Evaluate number of remote fader

        if nFader == 1:
            self.tmpChannelDataMode1.clear()

        while len(self.tmpChannelDataMode1) < nFader:
            for _, _layer in self.tmpChannelDataMode1.items():
                _layer.append(dict())

    def oscR_tmpChannelDataMode1(self, fader:int, key:str, *args):

        value = args[0] if type(args[0]) == float else args[0].decode()

        self.tmpChannelDataMode1[fader][key] = value

        if fader == 1 and key == 'name':
            self.currentChannelName = value
            if value in self.channelNamesByIndex[self.currentLayer]:
                self.currentChIndex = self.channelNamesByIndex[self.currentLayer].index(value)

    def oscR_checkTmOutMode1complete(self, chN:int):
        if chN == self.numberFader:
            self.checkTaskStack()

    def scheduleTimeOut(self, lastFunction=None, t=5):
        return Timer(t, self.timeoutFunction, args=[lastFunction])

    def appendFinishFunction(self, finishFunction):
        self.taskStack.append(partial(finishFunction, caller=self))

    def checkTaskStack(self, *args):

        if self.timeout.is_alive():
            self.timeout.cancel()
        if self.taskStack:
            self.timeout = self.scheduleTimeOut(self.taskStack[0])
            self.timeout.start()
            if self.taskStack[0]:
                self.taskStack.pop(0)()
        else:
            print('no tasks on stack')
            timeout = self.scheduleTimeOut()
            timeout.start()

    def initTmpData(self):
        self.tmpChannelData.clear()
        # self.currentChannelName = ''
        for _, _list in self.tmpChannelDataMode1.items():
            _list.clear()

    def oscS_goToLayer(self, layer: str, mode: int = 2):
        self.initTmpData()
        sOsc = '/{}/{}'.format(mode, layer)
        self.toTM.send_message(sOsc.encode(), [1.0])

    def oscS_goToChannelIndex(self, index: int):
        # self.currentChIndex = index
        self.toTM.send_message(b'/setBankStart', [float(index)])

    def oscS_goToNextChannel(self):
        if self.getDataOfSelectedChannel()['stereo'] == 1.0:
            self.currentChIndex = self.currentChIndex + 2
        else:
            self.currentChIndex = self.currentChIndex + 1
        # self.currentChIndex = self.currentChIndex + indexIncrement

        self.toTM.send_message(b'/2/track+', [1.0])

    def oscS_previousChannel(self, mode:int=2):
        if mode == 2:
            self.toTM.send_message(b'/2/track-', [1.0])
        else:
            self.toTM.send_message(b'/1/track+', [1.0])

    def oscS_goToNextBank(self):
        self.toTM.send_message(b'/1/bank+', [1.0])

    def oscS_previousBank(self):
        self.toTM.send_message(b'/1/bank-', [1.0])

    def oscS_selectSubmix(self, outChannelIdx:int):

        if not self.selectedSubmixIndex == outChannelIdx:
            self.toTM.send_message(b'/setSubmix', [float(outChannelIdx)])
            # global timeout
            if self.timeout.is_alive():
                self.timeout.cancel()
            self.timeout = Timer(self.timeoutT, self.timeoutFunction)
            self.timeout.start()
        else:
            self.checkTaskStack()

    def goToFirstChannel_wBanks(self, mode:int=1):
        if self.currentChannelName == self.lastChName:
            self.checkTaskStack()
        else:
            self.taskStack.insert(0, partial(self.goToFirstChannel_wBanks, mode))
            self.oscS_previousBank()

    def getDataOfSelectedChannel(self) -> dict:
        return self.channelDataByName[self.currentLayer][self.currentChannelName]

    def checkValue(self, parameter, value, oscAddr):
        if parameter:
            if self.tmpChannelData[parameter] == value:
                self.settingTargetValue = False
                self.checkTaskStack()
            else:
                self.settingTargetValue = True
                self.taskStack.insert(0, partial(self.checkValue, parameter, value, oscAddr))
                if self.parameterIsToggle(parameter):
                    self.oscS_toggleValue(oscAddr)
                else:
                    self.oscS_doSetValue(oscAddr, float(self.targetValue))
        else:
            print('no target parameter specified')

    def checkMultipleValues(self):
        #TODO:implement
        pass

    def oscS_doSetValue(self, oscAd, value):
        self.toTM.send_message(oscAd, [value])

    def oscS_toggleValue(self, oscAd):
        self.toTM.send_message(oscAd, [1.0])

    def fetchChannelProperties(self):

        if self.chLimitReached:
            self.chLimitReached = False
            self.checkTaskStack()
        else:
            self.channelNamesByIndex[self.currentLayer].append(self.currentChannelName)
            if self.tmpChannelData['stereo'] == 1.0:
                self.channelNamesByIndex[self.currentLayer].append(self.currentChannelName)
            self.channelDataByName[self.currentLayer][self.currentChannelName] = self.tmpChannelData.copy()

            self.initTmpData()
            self.checkChannelLimit = True

            self.taskStack.insert(0, self.fetchChannelProperties)

            self.oscS_goToNextChannel()

    #TODO: Reimplement fetch Channel Volume
    def initiateVolumeFetch(self):
        print('caution volume fetching not implemented yet')

    def initiateLayoutFetch(self, fastFetch:bool=False, finishFunction=dumdumdummyfunc):
        print('initiating Layout Fetch')

        self.setInitiateData()

        self.scheduleLayoutFetch(fastFetch)
        self.taskStack.append(partial(finishFunction, caller=self))

        self.checkTaskStack()

    #TODO: implement "Fast Fetch" without Properties, layermode=1
    def scheduleLayoutFetch(self, fastFetch:bool=False):
        if fastFetch:
            print('ERROR: fast fetch not implemented yet')
            return False
        else:
            for _layer in [tmbase.busOutput, tmbase.busInput, tmbase.busPlayback]:
                self.taskStack.append(partial(self.oscS_goToLayer, _layer))
                self.taskStack.append(partial(self.oscS_goToChannelIndex, 0))
                self.taskStack.append(self.fetchChannelProperties)

    def scheduleJob(self):
        pass


from django.urls import path
from .apps import APP_NAME
from . import views,apis
app_name=APP_NAME
urlpatterns = [
    path('',views.BasicViews().home,name="home"),
    path('scenario/<int:pk>/',views.BasicViews().scenario,name='scenario'),
    path('feeder/<int:pk>/',views.BasicViews().feeder,name='feeder'),
    path('run_scenario/',apis.BasicApi().run_scenario,name='run_scenario'),
    path('logs/',views.BasicViews().logs,name='logs'),
    path('get_logs/',apis.BasicApi().get_logs,name='get_logs'),
    path('command/<int:pk>/',views.BasicViews().command,name='command'),
    path('relay/<int:pk>/',views.BasicViews().relay,name='relay'),
    path('export/',apis.BasicApi().export,name="export"),
    path('execute_command/',apis.BasicApi().execute_command,name="execute_command"),
    path('add_log_from_client/',apis.add_log_from_client,name="add_log_from_client"),
]


from collections import defaultdict
MAX, need = 70000000, 30000000
limit = 100000

data = open("2022day7.txt").read().splitlines()
graph = defaultdict(list)
mp = defaultdict(int)
st = []
for command in data:
	command = command.split()
	if command[0] == '$':
		if command[1] == 'ls':
			continue
		elif command[2] == '..':
			st.pop(-1)
		else:
			st.append('.'.join(st + [command[2]]))
	else:
		if command[0] == 'dir':
			graph[st[-1]].append('.'.join(st + [command[1]]))
		else:
			mp[st[-1]] += int(command[0])

def dfs(node):
	for x in graph[node]:
		mp[node] += dfs(x)
	return mp[node]

# print(graph)
# print(mp)

dfs('/')

# Part 1
res1 = 0
for x, y in mp.items():
	if y <= limit:
		res1 += y

print(res1)

# Part 2
tot = mp['/']
res2 = float('inf')
for y in mp.values():
	if MAX - tot + y >= need:
		res2 = min(res2, y)

print(res2)

# print(res1, res2, sep = '\n')


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

# This file is part of pk15 Orakel
#
# pk15 Orakel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pk15 Orakel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License

import uuid
from django.contrib.auth.decorators import login_required

from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import render
from django.utils.translation import ugettext as _
from django.views.generic import ListView, DetailView
from django.views.generic import DeleteView
from django.views.generic import CreateView
from django.views.generic import UpdateView

from utils.generic_views import WgerFormMixin
from utils.generic_views import WgerDeleteMixin
from utils.generic_views import WgerPermissionMixin

from oracle.models import Coordinate

@login_required
def show(request, lat, lon, question):
    '''
    Small view that simply outputs two coordinates
    '''

    context = {'lat': lat,
               'lon': lon,
               'question': int(question)}
    return render(request, 'coordinate/show.html', context)

class CoordinateListView(WgerPermissionMixin, ListView):
    '''
    Overview of all available licenses
    '''
    model = Coordinate
    permission_required = 'oracle.add_coordinate'
    template_name = 'coordinate/list.html'

class CoordinateDetaillView(WgerPermissionMixin, DetailView):
    '''
    Deatail view for a question
    '''
    model = Coordinate
    permission_required = 'oracle.add_coordinate'
    template_name = 'coordinate/detail.html'

    def get_context_data(self, **kwargs):
        '''
        Send some additional data to the template
        '''
        context = super(CoordinateDetaillView, self).get_context_data(**kwargs)
        relative_url = reverse('oracle:check-step', kwargs={'uuid': self.object.uuid})
        context['qr_url'] = self.request.build_absolute_uri(relative_url)
        return context

class CoordinateAddView(WgerFormMixin, CreateView, WgerPermissionMixin):
    '''
    View to add a new team
    '''

    model = Coordinate
    title = u'Frage hinzufügen'
    form_action = reverse_lazy('oracle:coordinate-add')
    permission_required = 'oracle.add_coordinate'

    def get_initial(self):
        return {'uuid': uuid.uuid4()}

class CoordinateUpdateView(WgerFormMixin, UpdateView, WgerPermissionMixin):
    '''
    View to update an existing team
    '''

    model = Coordinate
    title = u'Team bearbeiten'
    #success_url = reverse_lazy('oracle:coordinate-list')
    permission_required = 'oracle.change_coordinate'

    def get_context_data(self, **kwargs):
        '''
        Send some additional data to the template
        '''
        context = super(CoordinateUpdateView, self).get_context_data(**kwargs)
        context['form_action'] = reverse('oracle:coordinate-edit', kwargs={'pk': self.object.id})
        context['title'] = _(u'Edit {0}'.format(self.object))
        return context

class CoordinateDeleteView(WgerDeleteMixin, DeleteView, WgerPermissionMixin):
    '''
    View to delete an existing team
    '''

    model = Coordinate
    success_url = reverse_lazy('oracle:coordinate-list')
    permission_required = 'oracle.delete_coordinate'

    def get_context_data(self, **kwargs):
        '''
        Send some additional data to the template
        '''
        context = super(CoordinateDeleteView, self).get_context_data(**kwargs)
        context['title'] = 'Koordinate "{0}" löschen?'.format(self.object)
        context['form_action'] = reverse('oracle:coordinate-delete', kwargs={'pk': self.kwargs['pk']})
        return context


""" Parse URL and scrape relevant links. """
import re
import argparse
from typing import List, Tuple

import requests
from bs4 import BeautifulSoup

from gitir_downloader.utils.tidy_names import tidy_names

def parse_url(args: argparse.Namespace) -> Tuple[str, List]:
    """
    Parse url and extract links
    """
    url = args.link

    try:
        page = requests.get(url)
    except requests.exceptions.RequestException as e:
        raise SystemExit(e)

    soup = BeautifulSoup(page.content, "html.parser")

    split_url = url.split("/")[-2:]
    folder_title: str = split_url[0]

    LINKS_STORE: List[str] = []

    for link in soup.findAll(
        "a", attrs={"href": re.compile("^https://cdn(\d?)+.git.ir/")}
    ):
        LINKS_STORE.append(link.get("href"))

    # names = tidy_names(LINKS_STORE)

    return folder_title, LINKS_STORE


import torch

def find_intersection(boxes1, boxes2):
    """ Find the intersection of every box combination between two sets of boxes that are in boundary coordinates.
    Args:
        boxes1: set 1, a tensor of dimensions (n1, 4)
        boxes2: set 2, a tensor of dimensions (n2, 4)
    Returns:
        Intersection of each of the boxes in set 1 with respect to each of the boxes in set 2, a tensor of dimensions (n1, n2)
    """

    # PyTorch auto-broadcasts singleton dimensions
    lower_bounds = torch.max(boxes1[:, :2].unsqueeze(1), boxes2[:, :2].unsqueeze(0))  # (n1, n2, 2)
    upper_bounds = torch.min(boxes1[:, 2:].unsqueeze(1), boxes2[:, 2:].unsqueeze(0))  # (n1, n2, 2)
    intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0)  # (n1, n2, 2)
    return intersection_dims[:, :, 0] * intersection_dims[:, :, 1]  # (n1, n2)

def compute_iou(boxes1, boxes2):
    """ Find the Jaccard Overlap (IoU) of every box combination between two sets of boxes that are in boundary coordinates.
    Args:
        boxes1: set 1, a tensor of dimensions (n1, 4)
        boxes2: set 2, a tensor of dimensions (n2, 4)
    Returns:
        Jaccard Overlap of each of the boxes in set 1 with respect to each of the boxes in set 2, a tensor of dimensions (n1, n2)
    """

    intersection = find_intersection(boxes1, boxes2)  # (n1, n2)

    areas1 = (boxes1[:, 2] - boxes1[:, 0]) * (boxes1[:, 3] - boxes1[:, 1])  # (n1)
    areas2 = (boxes2[:, 2] - boxes2[:, 0]) * (boxes2[:, 3] - boxes2[:, 1])  # (n2)

    union = areas1.unsqueeze(1) + areas2.unsqueeze(0) - intersection  # (n1, n2)

    return intersection / (union + 1e-8)


import cv2
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.image as mpimg

# Constants section
bi = 255
ai = 0
colors = ('b' , 'g' ,'r')
pixPercent = 0.005

# Inicio del Programa
src = cv2.imread('images/imagenpdi1.jpg')

hist1 = cv2.calcHist([src],[0],None,[256],[0,256])
hist2 = cv2.calcHist([src],[1],None,[256],[0,256])
hist3 = cv2.calcHist([src],[2],None,[256],[0,256])

fig_size = plt.rcParams["figure.figsize"]
fig_size[0] = 12
fig_size[1] = 9

srcRgb = cv2.cvtColor(src, cv2.COLOR_BGR2RGB)

plt.subplot(221)
plt.imshow(srcRgb)
plt.title('Imagen Original')
plt.ylabel('Vertical pixels')
plt.xlabel('Horizontal pixels')
plt.subplot(222)
plt.plot(hist1, color = colors[0])
plt.title('Histograma Azul')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(223)
plt.plot(hist2, color = colors[1])
plt.title('Histograma Verde')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(224)
plt.plot(hist3, color = colors[2])
plt.title('Histograma Rojo')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.suptitle('Imagen inicial y sus histogramas', fontsize=16)

b, g, r = cv2.split(src)
b = np.array(b,dtype = int)
g = np.array(g,dtype = int)
r = np.array(r,dtype = int)
width , height, depth = src.shape

pixTotalStr = int(width*height*pixPercent)
print(pixTotalStr)

pix_accb = 0
pix_accg = 0
pix_accr = 0
for i in range(0,256,1):
    if (pix_accb < pixTotalStr):
        pix_accb += int(hist1[i])
        if pix_accb >= pixTotalStr:
            cb = i
    if (pix_accg < pixTotalStr):
        pix_accg += int(hist2[i])
        if pix_accg >= pixTotalStr:
            cg = i
    if (pix_accr < pixTotalStr):
        pix_accr += int(hist3[i])
        if pix_accr >= pixTotalStr:
            cr = i

print(cb)
print(cg)
print(cr)

pix_accb = 0
pix_accg = 0
pix_accr = 0
for i in range(255,-1,-1):
    if (pix_accb < pixTotalStr):
        pix_accb += int(hist1[i])
        if pix_accb >= pixTotalStr:
            db = i
    if (pix_accg < pixTotalStr):
        pix_accg += int(hist2[i])
        if pix_accg >= pixTotalStr:
            dg = i
    if (pix_accr < pixTotalStr):
        pix_accr += int(hist3[i])
        if pix_accr >= pixTotalStr:
            dr = i

print(db)
print(dg)
print(dr)

for i in range (0, width, 1):
    for j in range (0, height, 1):
        b[i,j] = ((b[i,j] - cb)*(bi-ai)//(db-cb)) + ai
        if b[i,j] > 255:
            b[i,j] = 255
        elif b[i,j] < 0:
            b[i,j] = 0
        g[i,j] = ((g[i,j] - cg)*(bi-ai)//(dg-cg)) + ai
        if g[i,j] > 255:
            g[i,j] = 255
        elif g[i,j] < 0:
            g[i,j] = 0
        r[i,j] = ((r[i,j] - cr)*(bi-ai)//(dr-cr)) + ai
        if r[i,j] > 255:
            r[i,j] = 255
        elif r[i,j] < 0:
            r[i,j] = 0

b = np.array(b,dtype = np.uint8)
g = np.array(g,dtype = np.uint8)
r = np.array(r,dtype = np.uint8)

histb = cv2.calcHist([b], [0], None, [256], [0, 256])
histg = cv2.calcHist([g], [0], None, [256], [0, 256])
histr = cv2.calcHist([r], [0], None, [256], [0, 256])

image_proc = cv2.merge((b,g,r))

image_procRgb = cv2.cvtColor(image_proc, cv2.COLOR_BGR2RGB)

plt.figure()
plt.subplot(221)
plt.imshow(image_procRgb)
plt.title('Imagen Procesada')
plt.ylabel('Vertical pixels')
plt.xlabel('Horizontal pixels')
plt.subplot(222)
plt.plot(histb, color = colors[0])
plt.title('Histograma Azul')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(223)
plt.plot(histg, color = colors[1])
plt.title('Histograma Verde')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(224)
plt.plot(histr, color = colors[2])
plt.title('Histograma Rojo')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.suptitle('Estiramiento de histogramas en el espacio RGB', fontsize=16)

image_hsv = cv2.cvtColor(image_proc, cv2.COLOR_BGR2HSV)
hc, sc, vc = cv2.split(image_hsv)

hist1 = cv2.calcHist([image_hsv],[0],None,[256],[0,256])
hist2 = cv2.calcHist([image_hsv],[1],None,[256],[0,256])
hist3 = cv2.calcHist([image_hsv],[2],None,[256],[0,256])

image_hsv_RGB = cv2.cvtColor(image_hsv, cv2.COLOR_BGR2RGB)

plt.figure()
plt.subplot(221)
plt.imshow(image_hsv_RGB)
plt.title('Imagen Inicial en HSV')
plt.ylabel('Vertical pixels')
plt.xlabel('Horizontal pixels')
plt.subplot(222)
plt.plot(hist1, color = colors[0])
plt.title('Histograma Hue')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(223)
plt.plot(hist2, color = colors[1])
plt.title('Histograma Saturation')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(224)
plt.plot(hist3, color = colors[2])
plt.title('Histograma Value')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.suptitle('Imagen en el espacio HSV', fontsize=16)

pix_acch = 0
pix_accs = 0
pix_accv = 0
for i in range(0,256,1):
    if (pix_acch < pixTotalStr):
        pix_acch += int(hist1[i])
        if pix_acch >= pixTotalStr:
            ch = i
    if (pix_accs < pixTotalStr):
        pix_accs += int(hist2[i])
        if pix_accs >= pixTotalStr:
            cs = i
    if (pix_accv < pixTotalStr):
        pix_accv += int(hist3[i])
        if pix_accv >= pixTotalStr:
            cv = i

print(ch)
print(cs)
print(cv)

pix_acch = 0
pix_accs = 0
pix_accv = 0
for i in range(255,-1,-1):
    if (pix_acch < pixTotalStr):
        pix_acch += int(hist1[i])
        if pix_acch >= pixTotalStr:
            dh = i
    if (pix_accs < pixTotalStr):
        pix_accs += int(hist2[i])
        if pix_accs >= pixTotalStr:
            ds = i
    if (pix_accv < pixTotalStr):
        pix_accv += int(hist3[i])
        if pix_accv >= pixTotalStr:
            dv = i

print(db)
print(dg)
print(dr)

sc = np.array(sc,dtype = int)
vc = np.array(vc,dtype = int)

for i in range (0, width, 1):
    for j in range (0, height, 1):
        sc[i,j] = ((sc[i,j] - cs)*(bi-ai)/(ds-cs)) + ai
        if sc[i,j] > 255:
            sc[i,j] = 255
        elif sc[i,j] < 0:
            sc[i,j] = 0
        vc[i,j] = ((vc[i,j] - cv)*(bi-ai)/(dv-cv)) + ai
        if vc[i,j] > 255:
            vc[i,j] = 255
        elif vc[i,j] < 0:
            vc[i,j] = 0

sc = np.array(sc,dtype = np.uint8)
vc = np.array(vc,dtype = np.uint8)

image_proc2 = cv2.merge((hc, sc, vc))

hist1 = cv2.calcHist([image_proc2],[0],None,[256],[0,256])
hist2 = cv2.calcHist([image_proc2],[1],None,[256],[0,256])
hist3 = cv2.calcHist([image_proc2],[2],None,[256],[0,256])

image_proc2Rgb = cv2.cvtColor(image_proc2, cv2.COLOR_BGR2RGB)

plt.figure()
plt.subplot(221)
plt.imshow(image_proc2Rgb)
plt.title('Imagen Procesada en HSV')
plt.ylabel('Vertical pixels')
plt.xlabel('Horizontal pixels')
plt.subplot(222)
plt.plot(hist1, color = colors[0])
plt.title('Histograma Hue')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(223)
plt.plot(hist2, color = colors[1])
plt.title('Histograma Saturation')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(224)
plt.plot(hist3, color = colors[2])
plt.title('Histograma Value')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.suptitle('Imagen procesada en el espacio HSV', fontsize=16)

image_final = cv2.cvtColor(image_proc2, cv2.COLOR_HSV2BGR)
cv2.imwrite('images/proceso1.jpg', image_final)

hist1 = cv2.calcHist([image_final],[0],None,[256],[0,256])
hist2 = cv2.calcHist([image_final],[1],None,[256],[0,256])
hist3 = cv2.calcHist([image_final],[2],None,[256],[0,256])

image_finalRgb = cv2.cvtColor(image_final, cv2.COLOR_BGR2RGB)

plt.figure()
plt.subplot(221)
plt.imshow(image_finalRgb)
plt.title('Imagen Final en RGB')
plt.ylabel('Vertical pixels')
plt.xlabel('Horizontal pixels')
plt.subplot(222)
plt.plot(hist1, color = colors[0])
plt.title('Histograma Azul')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(223)
plt.plot(hist2, color = colors[1])
plt.title('Histograma Verde')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.subplot(224)
plt.plot(hist3, color = colors[2])
plt.title('Histograma Rojo')
plt.ylabel('Number of pixels')
plt.xlabel('Intensity')
plt.suptitle('Imagen Final del Proceso 1', fontsize=16)

plt.show()


from django.urls import path, include
from rest_framework.routers import DefaultRouter

from questionary import views

router = DefaultRouter()
router.register('quiz-types', views.QuizTypeViewSet)
router.register('question-types', views.QuestionTypeViewSet)
router.register('quizzes', views.QuizViewSet)

app_name = 'questionary'

urlpatterns = [
    path('', include(router.urls))
]


from services.proto import actors_pb2
from services.proto import database_pb2

class ActorsServicer:

    def __init__(self, db_stub, logger, users_util, activities_util, host_name):
        self._db_stub = db_stub
        self._logger = logger
        self._users_util = users_util
        self._activities_util = activities_util
        self._host_name = host_name

    def _generate_key_id(self, _id):
        return '{}#main-key'.format(_id)

    def _fetch_public_key_from_database(self, handle):
        user = database_pb2.UsersEntry(
            handle=handle,
            host_is_null=True,
        )
        req = database_pb2.UsersRequest(
            match=user,
            request_type=database_pb2.UsersRequest.FIND
        )
        resp = self._db_stub.Users(req)
        if (resp.result_type != database_pb2.UsersResponse.OK or
                len(resp.results) == 0):
            self._logger.error('No user found in database')
            return None
        return resp.results[0].public_key

    def _get_public_key(self, handle):
        _id = self._activities_util.build_actor(handle, self._host_name)
        public_key = self._fetch_public_key_from_database(handle)
        return actors_pb2.PublicKey(
            id=self._generate_key_id(_id),
            owner=_id,
            public_key_pem=public_key,
        )

    def _create_actor(self, username):
        user = self._users_util.get_user_from_db(handle=username,
                                                 host_is_null=True)
        if user is None:
            self._logger.warning('Could not find user in database.')
            return None

        handle = user.handle
        inbox_url = self._activities_util.build_inbox_url(handle,
                                                          self._host_name)
        actor_url = self._activities_util.build_actor(handle, self._host_name)
        following_url = actor_url + "/following"
        followers_url = actor_url + "/followers"

        public_key = self._get_public_key(username)
        return actors_pb2.ActorObject(
            type='Person',
            id=actor_url,
            preferredUsername=handle,
            name=user.display_name,
            inbox=inbox_url,
            # TODO(iandioch): Create outbox URL when we have outboxes.
            outbox=None,
            following=following_url,
            followers=followers_url,
            global_id=user.global_id,
            public_key=public_key,
            summary=user.bio
        )

    def Get(self, request, context):
        self._logger.debug('Actor requested for {}'.format(request.username))
        actor = self._create_actor(request.username)
        return actors_pb2.ActorResponse(actor=actor)

    def GetArticle(self, req, context):
        self._logger.debug(
            'Article object requested for id: {} by user: {}'.format(
                req.article_id, req.username))
        user = self._users_util.get_user_from_db(handle=req.username,
                                                 host_is_null=True)
        if user is None:
            self._logger.warning('Could not find user in database.')
            return actors_pb2.ArticleResponse()

        actor_url = self._activities_util.build_actor(
            user.handle, self._host_name)
        articleEntry = database_pb2.PostsEntry(
            global_id=req.article_id
        )
        article_id = self._activities_util.build_article_ap_id(
            user, articleEntry)
        article_url = self._activities_util.build_local_article_url(
            user, articleEntry)
        article, err = self._activities_util.get_article_by_ap_id(
            article_id)
        if err is not None:
            self._logger.warning(
                'Could not find article in database: {}'.format(err))
            self._logger.warning(
                'ArticleId: {}'.format(article_id))
            return actors_pb2.ArticleResponse()

        publish_time = self._activities_util.timestamp_to_rfc(
            article.creation_datetime)

        return actors_pb2.ArticleResponse(
            actor=actor_url,
            content=article.body,
            published=publish_time,
            summary=article.summary,
            title=article.title,
            ap_id=article_id,
            article_url=article_url
        )


# -*- coding: utf-8 -*-
# Tag to display comments
# Tag to display sidebar with the following content:
## Most visited (by visits)
## Trending topics (by #comments)

from django import template
from contingut import models

register = template.Library()

@register.inclusion_tag('parts/comments.html', takes_context = True)
def show_comments(context):
    instance = context.get("instance")
    comments = models.Comment.objects.get_comments_by_article(instance)
    return {
        'comments': comments
        }

@register.inclusion_tag('parts/menu.html', takes_context = True)
def show_menu(context):
    categories = models.Category.objects.get_parents()
    return {
        'categories': categories
        }

@register.inclusion_tag('parts/sidebar.html', takes_context = True)
def show_sidebar(context):
    return {
        }

@register.inclusion_tag('parts/advertising.html', takes_context = True)
def show_advertising(context):
    ads = models.Ad.objects.all().order_by("?")[0:3]
    return {
        'ads': ads
        }

@register.inclusion_tag('parts/most_visited.html', takes_context = True)
def show_most_visited(context):
    articles = models.Article.objects.get_most_visited()
    return {
        'articles': articles
        }

@register.inclusion_tag('parts/trending_topics.html', takes_context = True)
def show_trending_topics(context):
    articles = models.Article.objects.get_trending_topics()
    return {
        'articles': articles
        }


# Fungsi untuk membagi sampai array terkecil
def merge_sort(arr):

    # list harus memiliki elemen
    if len(arr) > 1:

        # Mencari titik tengah list
        mid = len(arr) // 2

        # membagi list menjadi dua bagian
        # kiri
        L = arr[:mid]

        # dan kanan
        R = arr[mid:]

        # mengulangi pembagian list sampai menjadi terkecil
        merge_sort(L)
        merge_sort(R)

        # menggabungkan kembali
        merge(arr, L, R)

# fungsi untuk menggabungkan kembali list yang terpecah
def merge(arr, L, R):

    # index mulai dari 0
    i = j = k = 0

    # jika kedua list, list L dan R memiliki element
    while i < len(L) and j < len(R):
        if L[i] < R[j]:
            arr[k] = L[i]
            i += 1
        else:
            arr[k] = R[j]
            j += 1

        k += 1

    # Sisa dari while di atas
    while i < len(L):
        arr[k] = L[i]
        i += 1
        k += 1

    while j < len(L):
        arr[k] = R[j]
        j += 1
        k += 1

L = [5,3,2, 1, 8]
merge_sort(L)
print("final", L)
print("test", merge_sort(L))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Distance metric learning for large margin nearest neighbor classification
# Kilian Q.Weinberger, Lawrence K. Saul
# Journal of Machine Learning Research
# 2009

from sklearn.utils.validation import check_array
from sklearn.utils.validation import check_random_state
from sklearn.metrics.pairwise import euclidean_distances
from sklearn.utils import gen_batches
from scipy import optimize
import numpy as np
from scipy import sparse

def sum_outer_products(X, weights, remove_zero=False):
    weights_sym = weights + weights.T
    if remove_zero:
        _, cols = weights_sym.nonzero()
        ind = np.unique(cols)
        weights_sym = weights_sym.tocsc()[:, ind].tocsr()[ind, :]
        X = X[ind]

    n = weights_sym.shape[0]
    diag = sparse.spdiags(weights_sym.sum(axis=0), 0, n, n)
    laplacian = diag.tocsr() - weights_sym
    sodw = X.T.dot(laplacian.dot(X))
    return sodw

class LargeMarginNearestNeighbor():
    def __init__(self, k=3, mu=0.5, nFtsOut=None, maxCst=int(1e7),
                 randomState=None, maxiter=100, margin=1):
        self.k_ = k
        self.mu = mu
        self.nFtsOut_ = nFtsOut
        self.maxCst = maxCst
        self.randomState = randomState
        self.maxiter = maxiter
        self.margin = margin

    def fit(self, X, y):
        self.X_ = X
        self.y = y
        # Store the appearing classes and the class index for each sample
        self.labels_, self.y_ = np.unique(y, return_inverse=True)
        self.classes_ = np.arange(len(self.labels_))
        # Check that the number of neighbors is achievable for all classes
        min_class_size = np.bincount(self.y_).min()
        max_neighbors = min_class_size - 1
        self.k_ = min(self.k_, max_neighbors)
        # Initialize matrix L
        cov_ = np.cov(self.X_, rowvar=False)
        evals, evecs = np.linalg.eigh(cov_)
        self.L_ = np.fliplr(evecs).T  # Initialize to eigenvectors
        if self.nFtsOut_ is None:
            self.nFtsOut_ = self.L_.shape[0]
        nFtsIn = self.X_.shape[1]
        if self.nFtsOut_ > nFtsIn:
            self.nFtsOut_ = nFtsIn
        if self.L_.shape[0] > self.nFtsOut_:
            self.L_ = self.L_[:self.nFtsOut_]
        # Find target neighbors (fixed)
        self.targets_ = np.empty((self.X_.shape[0], self.k_), dtype=int)
        for class_ in self.classes_:
            class_ind, = np.where(np.equal(self.y_, class_))
            dist = euclidean_distances(self.X_[class_ind], squared=True)
            np.fill_diagonal(dist, np.inf)
            nghIdx = np.argpartition(dist, self.k_ - 1, axis=1)
            nghIdx = nghIdx[:, :self.k_]
            # argpartition doesn't guarantee sorted order, so we sort again but
            # only the k neighbors
            rowIdx = np.arange(len(class_ind))[:, None]
            nghIdx = nghIdx[rowIdx, np.argsort(dist[rowIdx, nghIdx])]
            self.targets_[class_ind] = class_ind[nghIdx]
        # Compute gradient component of target neighbors (constant)
        n, k = self.targets_.shape
        rows = np.repeat(np.arange(n), k)
        cols = self.targets_.flatten()
        targets_sparse = sparse.csr_matrix((np.ones(n * k),
                                           (rows, cols)), shape=(n, n))

        self.gradStatic = sum_outer_products(self.X_, targets_sparse)
        # Call optimizer
        L, loss, details = optimize.fmin_l_bfgs_b(
                                 func=self._loss_grad, x0=self.L_, maxiter=self.maxiter)
        # Reshape result from optimizer
        self.L_ = L.reshape(self.nFtsOut_, L.size // self.nFtsOut_)
        return self

    def transform(self, X=None):
        if X is None:
            X = self.X_
        else:
            X = check_array(X)
        return X.dot(self.L_.T)

    def _loss_grad(self, L):
        n, nFtsIn = self.X_.shape
        self.L_ = L.reshape(self.nFtsOut_, nFtsIn)
        Lx = self.transform()

        # Compute distances to target neighbors under L (plus margin 1)
        dist_tn = np.zeros((n, self.k_))
        for k in range(self.k_):
            dist_tn[:, k] = np.sum(np.square(Lx - Lx[self.targets_[:, k]]),
                                   axis=1) + self.margin
        margin_radii = dist_tn[:, -1]

        # Compute distances to impostors under L
        imp1, imp2, dist_imp = self._find_impostors(Lx, margin_radii)

        loss = 0
        A0 = sparse.csr_matrix((n, n))
        for k in reversed(range(self.k_)):
            loss1 = np.maximum(dist_tn[imp1, k] - dist_imp, 0)
            act, = np.where(loss1 != 0)
            A1 = sparse.csr_matrix((2*loss1[act], (imp1[act], imp2[act])),
                                   (n, n))
            loss2 = np.maximum(dist_tn[imp2, k] - dist_imp, 0)
            act, = np.where(loss2 != 0)
            A2 = sparse.csr_matrix((2*loss2[act], (imp1[act], imp2[act])),
                                   (n, n))
            vals = np.squeeze(np.asarray(A2.sum(0) + A1.sum(1).T))
            A0 = A0 - A1 - A2 + sparse.csr_matrix(
                               (vals, (range(n), self.targets_[:, k])), (n, n))
            loss = loss + np.sum(loss1 ** 2) + np.sum(loss2 ** 2)
        grad_new = sum_outer_products(self.X_, A0, remove_zero=True)
        df = self.L_.dot((1-self.mu)*self.gradStatic + self.mu*grad_new)
        df *= 2
        loss = ((1-self.mu)*(self.gradStatic*(self.L_.T.dot(self.L_))).sum() +
                self.mu*loss)
        return loss, df.flatten()

    def _find_impostors(self, Lx, margin_radii):
        n = Lx.shape[0]
        impostors = sparse.csr_matrix((n, n), dtype=np.int8)
        for class_ in self.classes_[:-1]:
            imp1, imp2 = [], []
            ind_in, = np.where(np.equal(self.y_, class_))
            ind_out, = np.where(np.greater(self.y_, class_))
            # Subdivide idx_out x idx_in to chunks of a size that is
            # fitting in memory
            ii, jj = self._find_impostors_batch(Lx[ind_out], Lx[ind_in],
                                                margin_radii[ind_out],
                                                margin_radii[ind_in])
            if len(ii):
                imp1.extend(ind_out[ii])
                imp2.extend(ind_in[jj])
                new_imps = sparse.csr_matrix(([1] * len(imp1), (imp1, imp2)),
                                             shape=(n, n), dtype=np.int8)
                impostors = impostors + new_imps
        imp1, imp2 = impostors.nonzero()
        if impostors.nnz > self.maxCst:  # subsample constraints if too many
            randomState = check_random_state(self.randomState)
            ind_subsample = randomState.choice(impostors.nnz,
                                               self.maxCst, replace=False)
            imp1, imp2 = imp1[ind_subsample], imp2[ind_subsample]
        dist = np.zeros(len(imp1))
        for chunk in gen_batches(len(imp1), 500):
            dist[chunk] = np.sum(np.square(Lx[imp1[chunk]] - Lx[imp2[chunk]]),
                                 axis=1)
        return imp1, imp2, dist

    @staticmethod
    def _find_impostors_batch(x1, x2, t1, t2, batch_size=500):
        n = len(t1)
        imp1, imp2 = [], []
        for chunk in gen_batches(n, batch_size):
            dist_out_in = euclidean_distances(x1[chunk], x2, squared=True)
            i1, j1 = np.where(dist_out_in < t1[chunk, None])
            i2, j2 = np.where(dist_out_in < t2[None, :])
            if len(i1):
                imp1.extend(i1 + chunk.start)
                imp2.extend(j1)
            if len(i2):
                imp1.extend(i2 + chunk.start)
                imp2.extend(j2)
        return imp1, imp2


import warnings

import numpy as np
import scipy
import scipy.fftpack
import scipy.optimize
import scipy.stats

import stingray.lightcurve as lightcurve
import stingray.utils as utils
from stingray.crossspectrum import Crossspectrum, AveragedCrossspectrum
from stingray.gti import bin_intervals_from_gtis, check_gtis
from stingray.stats import pds_probability
from .events import EventList
from .gti import cross_two_gtis

try:
    from tqdm import tqdm as show_progress
except ImportError:
    def show_progress(a, **kwargs):
        return a

__all__ = ["Powerspectrum", "AveragedPowerspectrum", "DynamicalPowerspectrum"]

class Powerspectrum(Crossspectrum):
    """
    Make a :class:`Powerspectrum` (also called periodogram) from a (binned) light curve.
    Periodograms can be normalized by either Leahy normalization, fractional rms
    normalizaation, absolute rms normalization, or not at all.

    You can also make an empty :class:`Powerspectrum` object to populate with your
    own fourier-transformed data (this can sometimes be useful when making
    binned power spectra).

    Parameters
    ----------
    data: :class:`stingray.Lightcurve` object, optional, default ``None``
        The light curve data to be Fourier-transformed.

    norm: {``leahy`` | ``frac`` | ``abs`` | ``none`` }, optional, default ``frac``
        The normaliation of the power spectrum to be used. Options are
        ``leahy``, ``frac``, ``abs`` and ``none``, default is ``frac``.

    Other Parameters
    ----------------
    gti: 2-d float array
        ``[[gti0_0, gti0_1], [gti1_0, gti1_1], ...]`` -- Good Time intervals.
        This choice overrides the GTIs in the single light curves. Use with
        care!

    Attributes
    ----------
    norm: {``leahy`` | ``frac`` | ``abs`` | ``none`` }
        the normalization of the power spectrun

    freq: numpy.ndarray
        The array of mid-bin frequencies that the Fourier transform samples

    power: numpy.ndarray
        The array of normalized squared absolute values of Fourier
        amplitudes

    power_err: numpy.ndarray
        The uncertainties of ``power``.
        An approximation for each bin given by ``power_err= power/sqrt(m)``.
        Where ``m`` is the number of power averaged in each bin (by frequency
        binning, or averaging power spectrum). Note that for a single
        realization (``m=1``) the error is equal to the power.

    df: float
        The frequency resolution

    m: int
        The number of averaged powers in each bin

    n: int
        The number of data points in the light curve

    nphots: float
        The total number of photons in the light curve

    """
    def __init__(self, data=None, norm="frac", gti=None,
                 dt=None, lc=None):
        if lc is not None:
            warnings.warn("The lc keyword is now deprecated. Use data "
                          "instead", DeprecationWarning)
        if data is None:
            data = lc

        Crossspectrum.__init__(self, data1=data, data2=data, norm=norm, gti=gti,
                               dt=dt)
        self.nphots = self.nphots1
        self.dt = dt

    def rebin(self, df=None, f=None, method="mean"):
        """
        Rebin the power spectrum.

        Parameters
        ----------
        df: float
            The new frequency resolution

        Other Parameters
        ----------------
        f: float
            the rebin factor. If specified, it substitutes ``df`` with ``f*self.df``

        Returns
        -------
        bin_cs = :class:`Powerspectrum` object
            The newly binned power spectrum.
        """
        bin_ps = Crossspectrum.rebin(self, df=df, f=f, method=method)
        bin_ps.nphots = bin_ps.nphots1

        return bin_ps

    def compute_rms(self, min_freq, max_freq, white_noise_offset=0.):
        """
        Compute the fractional rms amplitude in the power spectrum
        between two frequencies.

        Parameters
        ----------
        min_freq: float
            The lower frequency bound for the calculation

        max_freq: float
            The upper frequency bound for the calculation

        Other parameters
        ----------------
        white_noise_offset : float, default 0
            This is the white noise level, in Leahy normalization. In the ideal
            case, this is 2. Dead time and other instrumental effects can alter
            it. The user can fit the white noise level outside this function
            and it will get subtracted from powers here.

        Returns
        -------
        rms: float
            The fractional rms amplitude contained between ``min_freq`` and
            ``max_freq``

        rms_err: float
            The error on the fractional rms amplitude

        """
        minind = self.freq.searchsorted(min_freq)
        maxind = self.freq.searchsorted(max_freq)
        powers = self.power[minind:maxind]
        nphots = self.nphots

        if self.norm.lower() == 'leahy':
            powers_leahy = powers.copy()
        elif self.norm.lower() == "frac":
            powers_leahy = \
                self.unnorm_power[minind:maxind].real * 2 / nphots
        else:
            raise TypeError("Normalization not recognized!")

        rms = np.sqrt(np.sum(powers_leahy - white_noise_offset) / nphots)
        rms_err = self._rms_error(powers_leahy)

        return rms, rms_err

    def _rms_error(self, powers):
        """
        Compute the error on the fractional rms amplitude using error
        propagation.
        Note: this uses the actual measured powers, which is not
        strictly correct. We should be using the underlying power spectrum,
        but in the absence of an estimate of that, this will have to do.

        .. math::

           r = \sqrt{P}

        .. math::

           \delta r = \\frac{1}{2 * \sqrt{P}} \delta P

        Parameters
        ----------
        powers: iterable
            The list of powers used to compute the fractional rms amplitude.

        Returns
        -------
        delta_rms: float
            The error on the fractional rms amplitude
        """
        nphots = self.nphots
        p_err = scipy.stats.chi2(2.0 * self.m).var() * powers / self.m / nphots

        rms = np.sum(powers) / nphots
        pow = np.sqrt(rms)

        drms_dp = 1 / (2 * pow)

        sq_sum_err = np.sqrt(np.sum(p_err**2))
        delta_rms = sq_sum_err * drms_dp
        return delta_rms

    def classical_significances(self, threshold=1, trial_correction=False):
        """
        Compute the classical significances for the powers in the power
        spectrum, assuming an underlying noise distribution that follows a
        chi-square distributions with 2M degrees of freedom, where M is the
        number of powers averaged in each bin.

        Note that this function will *only* produce correct results when the
        following underlying assumptions are fulfilled:

        1. The power spectrum is Leahy-normalized
        2. There is no source of variability in the data other than the
           periodic signal to be determined with this method. This is important!
           If there are other sources of (aperiodic) variability in the data, this
           method will *not* produce correct results, but instead produce a large
           number of spurious false positive detections!
        3. There are no significant instrumental effects changing the
           statistical distribution of the powers (e.g. pile-up or dead time)

        By default, the method produces ``(index,p-values)`` for all powers in
        the power spectrum, where index is the numerical index of the power in
        question. If a ``threshold`` is set, then only powers with p-values
        *below* that threshold with their respective indices. If
        ``trial_correction`` is set to ``True``, then the threshold will be corrected
        for the number of trials (frequencies) in the power spectrum before
        being used.

        Parameters
        ----------
        threshold : float, optional, default ``1``
            The threshold to be used when reporting p-values of potentially
            significant powers. Must be between 0 and 1.
            Default is ``1`` (all p-values will be reported).

        trial_correction : bool, optional, default ``False``
            A Boolean flag that sets whether the ``threshold`` will be corrected
            by the number of frequencies before being applied. This decreases
            the ``threshold`` (p-values need to be lower to count as significant).
            Default is ``False`` (report all powers) though for any application
            where `threshold`` is set to something meaningful, this should also
            be applied!

        Returns
        -------
        pvals : iterable
            A list of ``(index, p-value)`` tuples for all powers that have p-values
            lower than the threshold specified in ``threshold``.

        """
        if not self.norm == "leahy":
            raise ValueError("This method only works on "
                             "Leahy-normalized power spectra!")

        if trial_correction:
            ntrial = self.power.shape[0]
        else:
            ntrial = 1

        if np.size(self.m) == 1:
            # calculate p-values for all powers
            # leave out zeroth power since it just encodes the number of photons!
            pv = pds_probability(self.power, n_summed_spectra=self.m,
                                 ntrial=ntrial)
        else:
            pv = np.array([pds_probability(power, n_summed_spectra=m,
                                           ntrial=ntrial)
                           for power, m in zip(self.power, self.m)])

        # need to add 1 to the indices to make up for the fact that
        # we left out the first power above!
        indices = np.where(pv < threshold)[0]

        pvals = np.vstack([pv[indices], indices])

        return pvals

class AveragedPowerspectrum(AveragedCrossspectrum, Powerspectrum):
    """
    Make an averaged periodogram from a light curve by segmenting the light
    curve, Fourier-transforming each segment and then averaging the
    resulting periodograms.

    Parameters
    ----------
    data: :class:`stingray.Lightcurve`object OR iterable of :class:`stingray.Lightcurve` objects OR :class:`stingray.EventList` object
        The light curve data to be Fourier-transformed.

    segment_size: float
        The size of each segment to average. Note that if the total
        duration of each :class:`Lightcurve` object in lc is not an integer multiple
        of the ``segment_size``, then any fraction left-over at the end of the
        time series will be lost.

    norm: {``leahy`` | ``frac`` | ``abs`` | ``none`` }, optional, default ``frac``
        The normaliation of the periodogram to be used.

    Other Parameters
    ----------------
    gti: 2-d float array
        ``[[gti0_0, gti0_1], [gti1_0, gti1_1], ...]`` -- Good Time intervals.
        This choice overrides the GTIs in the single light curves. Use with
        care!

    silent : bool, default False
         Do not show a progress bar when generating an averaged cross spectrum.
         Useful for the batch execution of many spectra

    dt: float
        The time resolution of the light curve. Only needed when constructing
        light curves in the case where data is of :class:EventList

    Attributes
    ----------
    norm: {``leahy`` | ``frac`` | ``abs`` | ``none`` }
        the normalization of the periodogram

    freq: numpy.ndarray
        The array of mid-bin frequencies that the Fourier transform samples

    power: numpy.ndarray
        The array of normalized squared absolute values of Fourier
        amplitudes

    power_err: numpy.ndarray
        The uncertainties of ``power``.
        An approximation for each bin given by ``power_err= power/sqrt(m)``.
        Where ``m`` is the number of power averaged in each bin (by frequency
        binning, or averaging powerspectrum). Note that for a single
        realization (``m=1``) the error is equal to the power.

    df: float
        The frequency resolution

    m: int
        The number of averaged periodograms

    n: int
        The number of data points in the light curve

    nphots: float
        The total number of photons in the light curve

    """
    def __init__(self, data=None, segment_size=None, norm="frac", gti=None,
                 silent=False, dt=None, lc=None):

        self.type = "powerspectrum"
        if lc is not None:
            warnings.warn("The lc keyword is now deprecated. Use data "
                          "instead", DeprecationWarning)
        # Backwards compatibility: user might have supplied lc instead
        if data is None:
            data = lc

        if segment_size is None and data is not None:
            raise ValueError("segment_size must be specified")
        if segment_size is not None and not np.isfinite(segment_size):
            raise ValueError("segment_size must be finite!")

        self.dt = dt

        if isinstance(data, EventList):
            lengths = data.gti[:, 1] - data.gti[:, 0]
            good = lengths >= segment_size
            data.gti = data.gti[good]

        self.segment_size = segment_size
        self.show_progress = not silent
        Powerspectrum.__init__(self, data, norm, gti=gti, dt=dt)

        return

    def _make_segment_spectrum(self, lc, segment_size):
        """
        Split the light curves into segments of size ``segment_size``, and
        calculate a power spectrum for each.

        Parameters
        ----------
        lc  : :class:`stingray.Lightcurve` objects\
            The input light curve

        segment_size : ``numpy.float``
            Size of each light curve segment to use for averaging.

        Returns
        -------
        power_all : list of :class:`Powerspectrum` objects
            A list of power spectra calculated independently from each light curve segment

        nphots_all : ``numpy.ndarray``
            List containing the number of photons for all segments calculated from ``lc``
        """
        if not isinstance(lc, lightcurve.Lightcurve):
            raise TypeError("lc must be a lightcurve.Lightcurve object")

        current_gtis = lc.gti

        if self.gti is None:
            self.gti = lc.gti
        else:
            if not np.all(lc.gti == self.gti):
                self.gti = np.vstack([self.gti, lc.gti])

        check_gtis(self.gti)

        start_inds, end_inds = \
            bin_intervals_from_gtis(current_gtis, segment_size, lc.time, dt=lc.dt)

        power_all = []
        nphots_all = []

        local_show_progress = show_progress
        if not self.show_progress:
            local_show_progress = lambda a: a

        for start_ind, end_ind in \
                local_show_progress(zip(start_inds, end_inds)):
            time = lc.time[start_ind:end_ind]
            counts = lc.counts[start_ind:end_ind]
            counts_err = lc.counts_err[start_ind: end_ind]

            if np.sum(counts) == 0:
                warnings.warn(
                    "No counts in interval {}--{}s".format(time[0], time[-1]))
                continue

            lc_seg = lightcurve.Lightcurve(time, counts, err=counts_err,
                                           err_dist=lc.err_dist.lower(),
                                           skip_checks=True, dt=lc.dt)

            power_seg = Powerspectrum(lc_seg, norm=self.norm)
            power_all.append(power_seg)
            nphots_all.append(np.sum(lc_seg.counts))

        return power_all, nphots_all

class DynamicalPowerspectrum(AveragedPowerspectrum):
    """
    Create a dynamical power spectrum, also often called a *spectrogram*.

    This class will divide a :class:`Lightcurve` object into segments of
    length ``segment_size``, create a power spectrum for each segment and store
    all powers in a matrix as a function of both time (using the mid-point of each
    segment) and frequency.

    This is often used to trace changes in period of a (quasi-)periodic signal over
    time.

    Parameters
    ----------
    lc : :class:`stingray.Lightcurve` object
        The time series of which the Dynamical powerspectrum is
        to be calculated.

    segment_size : float, default 1
         Length of the segment of light curve, default value is 1 (in whatever units
         the ``time`` array in the :class:`Lightcurve`` object uses).

    norm: {``leahy`` | ``frac`` | ``abs`` | ``none`` }, optional, default ``frac``
        The normaliation of the periodogram to be used.

    Other Parameters
    ----------------
    gti: 2-d float array
        ``[[gti0_0, gti0_1], [gti1_0, gti1_1], ...]`` -- Good Time intervals.
        This choice overrides the GTIs in the single light curves. Use with
        care!

    Attributes
    ----------
    segment_size: float
        The size of each segment to average. Note that if the total
        duration of each Lightcurve object in lc is not an integer multiple
        of the ``segment_size``, then any fraction left-over at the end of the
        time series will be lost.

    dyn_ps : np.ndarray
        The matrix of normalized squared absolute values of Fourier
        amplitudes. The axis are given by the ``freq``
        and ``time`` attributes

    norm: {``leahy`` | ``frac`` | ``abs`` | ``none``}
        the normalization of the periodogram

    freq: numpy.ndarray
        The array of mid-bin frequencies that the Fourier transform samples

    df: float
        The frequency resolution

    dt: float
        The time resolution
    """

    def __init__(self, lc, segment_size, norm="frac", gti=None, dt=None):
        if segment_size < 2 * lc.dt:
            raise ValueError("Length of the segment is too short to form a "
                             "light curve!")
        elif segment_size > lc.tseg:
            raise ValueError("Length of the segment is too long to create "
                             "any segments of the light curve!")
        AveragedPowerspectrum.__init__(self, lc=lc,
                                       segment_size=segment_size, norm=norm,
                                       gti=gti, dt=dt)
        self._make_matrix(lc)

    def _make_matrix(self, lc):
        """
        Create a matrix of powers for each time step (rows) and each frequency step (columns).

        Parameters
        ----------
        lc : :class:`Lightcurve` object
            The :class:`Lightcurve` object from which to generate the dynamical power spectrum
        """
        ps_all, _ = AveragedPowerspectrum._make_segment_spectrum(
            self, lc, self.segment_size)
        self.dyn_ps = np.array([ps.power for ps in ps_all]).T

        self.freq = ps_all[0].freq
        current_gti = lc.gti
        if self.gti is not None:
            current_gti = cross_two_gtis(self.gti, current_gti)

        start_inds, end_inds = \
            bin_intervals_from_gtis(current_gti, self.segment_size, lc.time,
                                    dt=lc.dt)

        tstart = lc.time[start_inds]
        tend = lc.time[end_inds]

        self.time = tstart + 0.5*(tend - tstart)

        # Assign length of lightcurve as time resolution if only one value
        if len(self.time) > 1:
            self.dt = self.time[1] - self.time[0]
        else:
            self.dt = lc.n

        # Assign biggest freq. resolution if only one value
        if len(self.freq) > 1:
            self.df = self.freq[1] - self.freq[0]
        else:
            self.df = 1 / lc.n

    def rebin_frequency(self, df_new, method="sum"):
        """
        Rebin the Dynamic Power Spectrum to a new frequency resolution. Rebinning is
        an in-place operation, i.e. will replace the existing ``dyn_ps`` attribute.

        While the new resolution need not be an integer multiple of the
        previous frequency resolution, be aware that if it is not, the last
        bin will be cut off by the fraction left over by the integer division.

        Parameters
        ----------
        df_new: float
            The new frequency resolution of the Dynamical Power Spectrum.
            Must be larger than the frequency resolution of the old Dynamical
            Power Spectrum!

        method: {``sum`` | ``mean`` | ``average``}, optional, default ``sum``
            This keyword argument sets whether the counts in the new bins
            should be summed or averaged.
        """
        dynspec_new = []
        for data in self.dyn_ps.T:
            freq_new, bin_counts, bin_err, _ = \
                utils.rebin_data(self.freq, data, dx_new=df_new,
                                 method=method)
            dynspec_new.append(bin_counts)

        self.freq = freq_new
        self.dyn_ps = np.array(dynspec_new).T
        self.df = df_new

    def trace_maximum(self, min_freq=None, max_freq=None, sigmaclip=False):
        """
        Return the indices of the maximum powers in each segment :class:`Powerspectrum`
        between specified frequencies.

        Parameters
        ----------
        min_freq: float, default ``None``
            The lower frequency bound.

        max_freq: float, default ``None``
            The upper frequency bound.

        Returns
        -------
        max_positions : np.array
            The array of indices of the maximum power in each segment having
            frequency between ``min_freq`` and ``max_freq``.
        """
        if min_freq is None:
            min_freq = np.min(self.freq)
        if max_freq is None:
            max_freq = np.max(self.freq)

        max_positions = []
        for ps in self.dyn_ps.T:
            indices = np.logical_and(self.freq <= max_freq,
                                     min_freq <= self.freq)
            max_power = np.max(ps[indices])
            max_positions.append(np.where(ps == max_power)[0][0])

        return np.array(max_positions)

    def rebin_time(self, dt_new, method='sum'):

        """
        Rebin the Dynamic Power Spectrum to a new time resolution.
        While the new resolution need not be an integer multiple of the
        previous time resolution, be aware that if it is not, the last bin
        will be cut off by the fraction left over by the integer division.

        Parameters
        ----------
        dt_new: float
            The new time resolution of the Dynamical Power Spectrum.
            Must be larger than the time resolution of the old Dynamical Power
            Spectrum!

        method: {"sum" | "mean" | "average"}, optional, default "sum"
            This keyword argument sets whether the counts in the new bins
            should be summed or averaged.

        Returns
        -------
        time_new: numpy.ndarray
            Time axis with new rebinned time resolution.

        dynspec_new: numpy.ndarray
            New rebinned Dynamical Power Spectrum.
        """

        if dt_new < self.dt:
            raise ValueError("New time resolution must be larger than "
                             "old time resolution!")

        dynspec_new = []
        for data in self.dyn_ps:
            time_new, bin_counts, bin_err, _ = \
                utils.rebin_data(self.time, data, dt_new,
                                 method=method)
            dynspec_new.append(bin_counts)

        self.time = time_new
        self.dyn_ps = np.array(dynspec_new)
        self.dt = dt_new


from django.shortcuts import render
from .models import Car
from .forms import CarForm
from .forms import EditCarForm
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseRedirect
from django.core.paginator import Paginator

def auth_username(request):
    auth_user_name = request.user
    return auth_user_name

def all_cars_and_search(request):
    cars = get_all_cars()
    if "q" in request.GET:
        cars = search(request, cars)
    if "year_min" in request.GET and "year_max" in request.GET:
        cars = search_cars_by_year(request, cars)
    if "min" in request.GET and "max" in request.GET:
        cars = price(request, cars)
    if "wheel_left" in request.GET or "wheel_right" in request.GET:
        cars = checkbox_wheel(request)
    paginator = Paginator(cars, 4)
    page_number = request.GET.get("page")
    page_obj = paginator.get_page(page_number)
    return render(request, "car/all_cars.html", {"page_obj": page_obj,
                                                 "q": request.GET.get("q"),
                                                 "year_min": request.GET.get("year_min"),
                                                 "year_max": request.GET.get("year_max"),
                                                 "min": request.GET.get("min"),
                                                 "max": request.GET.get("max"),
                                                 "auth_username": auth_username(request), })

def get_all_cars():
    cars = Car.objects.all()
    return cars

def search(request, cars):
    if request.GET["q"]:
        cars = cars.filter(car_model__icontains=request.GET["q"])
    return cars

def search_cars_by_year(request, cars):
    if "year_min" in request.GET and "year_max" in request.GET:
        if request.GET.get("year_min") and request.GET.get("year_max"):
            cars = cars.filter(year__range=(request.GET["year_min"], request.GET["year_max"]))
        else:
            if request.GET["year_min"]:
                cars = cars.filter(year__gte=request.GET['year_min'])
            if request.GET["year_max"]:
                cars = cars.filter(year__lte=request.GET['year_max'])
    return cars

def price(request, cars):
    if "min" in request.GET and "max" in request.GET:
        if request.GET.get("min") and request.GET.get("max"):
            cars = cars.filter(price__range=(request.GET["min"], request.GET["max"]))
        else:
            if request.GET["min"]:
                cars = cars.filter(price__gte=request.GET['min'])
            if request.GET["max"]:
                cars = cars.filter(price__lte=request.GET['max'])
    return cars

def get_car_data(request, id):
    car = Car.objects.get(id=id)
    return render(request, 'car/car_data.html', {'car': car, "auth_username": auth_username(request)})

def checkbox_wheel(request):
    if "wheel_left" in request.GET or "wheel_right" in request.GET:
        if request.GET.get("wheel_left"):
            cars = Car.objects.filter(wheel="Ձախ")
        else:
            cars = Car.objects.filter(wheel="Աջ")
    return cars

# def create_car(request):
#     if request.method == "POST":
#         form = CarForm(request.POST, request.FILES)
#         if form.is_valid():
#             Car.author = auth_username(request)
#             print("Y+Y+Y+Y+Y+Y+Y+Y", Car.author)
#             form.save()
#             print("Y+Y+Y+Y+Y+Y+Y+Y", Car.author)
#             return HttpResponseRedirect('/')
#     else:
#         form = CarForm()
#     return render(request, "car/create_car.html", {"form": form, "auth_username": auth_username(request)})

@login_required
def create_car(request):
    if request.method == "POST":
        form = CarForm(request.POST, request.FILES)
        if form.is_valid():
            car_obj = form.save()
            car_obj.author = auth_username(request)
            car_obj.save()
            return HttpResponseRedirect('/account/user_page')
    else:
        form = CarForm()
    return render(request, "car/create_car.html", {"form": form, "auth_username": auth_username(request)})

@login_required
def edit_car(request, id):
    car = Car.objects.get(id=id)
    if request.method == 'POST':
        form = EditCarForm(request.POST, request.FILES, instance=car)
        if form.is_valid():
            form.save()
            return HttpResponseRedirect('/account/user_page')
    else:
        form = EditCarForm(instance=car)
    return render(request, 'car/edit_car.html', {"form": form})

@login_required
def del_car(request, id):
    car = Car.objects.get(id=id)
    car.delete()
    return HttpResponseRedirect('/account/user_page')


import os
import click
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torch.optim.lr_scheduler import StepLR, CosineAnnealingLR
from torch import optim
from dotenv import find_dotenv, load_dotenv
from tqdm import tqdm
from datetime import datetime

from ifcclouds.data.dataset import IfcCloudDs
from ifcclouds.models.dgcnn import DGCNN_semseg
from ifcclouds.visualization.visualize import plot_loss

@click.command()
@click.argument('model_type', default='dgcnn')
@click.argument('checkpoint_path', default='models/')
@click.option('--epochs', default=100, type=int)
@click.option('--lr', default=0.001, type=float)
@click.option('--momentum', default=0.9, type=float)
@click.option('--use_sgd', default=False, type=bool)
@click.option('--step', default=False, type=bool)
@click.option('--cuda', default=True, type=bool)
def main(model_type, checkpoint_path, epochs, lr, momentum, use_sgd, step, cuda):
  """ Runs model training """
  device = torch.device("cuda" if cuda and torch.cuda.is_available() else "cpu")

  dataset = IfcCloudDs(partition='train', num_points=4096)
  dataloader = DataLoader(dataset, batch_size=2, shuffle=True, num_workers=2)
  model = None
  if model_type == 'dgcnn':
    model = DGCNN_semseg(dataset.num_classes).to(device)
  elif model_type == 'pointnet':
    raise NotImplementedError('Model not implemented')
  else:
    raise NotImplementedError('Model not implemented')

  #print(str(model))

  if use_sgd:
    opt = optim.SGD(model.parameters(), lr=lr*100, momentum=momentum, weight_decay=1e-4)
  else:
    opt = optim.Adam(model.parameters(), lr=lr, weight_decay=1e-4)

  scheduler = StepLR(opt, step_size=20, gamma=0.5, last_epoch=-1) if step else CosineAnnealingLR(opt, epochs, eta_min=1e-3)

  model = nn.DataParallel(model)

  if checkpoint_path != 'models/':
    model.load_state_dict(torch.load(checkpoint_path))

  total_loss = []
  train_acc = 0.0

  for epoch in tqdm(range(epochs)):
    model.train()
    for pointcloud, seg in dataloader:
      pointcloud = pointcloud.to(device)
      seg = seg.to(device)

      opt.zero_grad()
      pred = model(pointcloud)
      pred = pred.permute(0, 2, 1).contiguous()

      loss = F.cross_entropy(pred.view(-1, dataset.num_classes), seg.view(-1,1).squeeze(), reduction='mean')
      loss.backward()
      opt.step()
      loss_item = loss.item()
      total_loss.append(loss_item)

      seg_pred = pred.max(dim=2)[1]
      correct = seg_pred.eq(seg).sum().item()
      train_acc += correct / (seg.size()[0] * seg.size()[1])
    if step:
      if opt.param_groups[0]['lr'] > 1e-5:
        scheduler.step()
      if opt.param_groups[0]['lr'] < 1e-5:
        for param_group in opt.param_groups:
          param_group['lr'] = 1e-5
    else:
      scheduler.step()

  timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
  checkpoint_file = timestamp + '_%s.pth' % model_type
  torch.save(model.state_dict(keep_vars=True), os.path.join(os.path.dirname(checkpoint_path), checkpoint_file))
  print('Train Accuracy: %f' % (train_acc / (len(dataloader) * epochs)))
  plot_loss(total_loss)

  os.system('python3 -m ifcclouds.models.test_model %s' % os.path.join(os.path.dirname(checkpoint_path), checkpoint_file))

if __name__ == '__main__':
  load_dotenv(find_dotenv())
  main()