import os, sys
import torch
import torchvision as tv
import cv2
import numpy as np
from matplotlib import pyplot as plt

from dataset import coco_labels

def box_cxcywh_to_xyxy(box):
    """
    Convert bounding box from center-size to xyxy format.
    :param box: bounding box in center-size format
    :return: bounding box in xyxy format
    """
    x_c, y_c, w, h = box[...,0], box[...,1], box[...,2], box[...,3]
    b = [
        (x_c - (w*0.5)), (y_c - (h*0.5)),
        (x_c + (w*0.5)), (y_c + (h*0.5)),
    ]
    return torch.stack(b, dim=-1)

def box_iou(b1, b2):
    """returns the iou between the set of boxes 1 and boxes 2

    assumes that b1 and b2 are in xyxy format

    Args:
        b1 (torch.Tensor): first set of boxes
        b2 (torch.Tensor): second set of boxes
    Returns:
        tuple(torch.Tensor, torch.Tensor): the iou and the union of the boxes in [N, M] format
    """

    area1 = (b1[...,2] - b1[...,0]) * (b1[...,3] - b1[...,1])
    area2 = (b2[...,2] - b2[...,0]) * (b2[...,3] - b2[...,1])

    lt = torch.max(b1[:, None, :2], b2[:, :2]) # [N,M,2]
    rb = torch.min(b1[:, None, 2:], b2[:, 2:]) # [N,M,2]

    wh = (rb - lt).clamp(min=0) # [N,M,2]

    inter = wh[...,0] * wh[...,1] # [N,M]
    union = area1[:, None] + area2 - inter # [N,1]

    iou = inter / (union + 1e-6)
    return iou, union

def box_giou(b1, b2):
    """returns the giou between the set of boxes 1 and boxes 2

    assumes that b1 and b2 are in xyxy format

    Args:
        b1 (torch.Tensor): first set of boxes
        b2 (torch.Tensor): second set of boxes
    Returns:
        tuple(torch.Tensor, torch.Tensor): the giou and iou of the boxes in [N, M] format
    """

    iou, union = box_iou(b1, b2)

    lt = torch.min(b1[:, None, :2], b2[:, :2]) # [N,M,2]
    rb = torch.max(b1[:, None, 2:], b2[:, 2:]) # [N,M,2]

    wh = (rb - lt).clamp(min=0) # [N,M,2]
    area = wh[...,0] * wh[...,1] # [N,M]

    giou = iou - (area - union) / (area + 1e-6)

    return giou, iou


from rest_framework.fields import IntegerField
from rest_framework.serializers import ModelSerializer, Serializer

from post.models import Post, PostLike

class PostCreateSerializer(ModelSerializer):
    class Meta:
        model = Post
        fields = (
            'id',
            'text',
            'author_id',
        )
        extra_kwargs = {
            'author_id': {'write_only': True},
        }

class PostLikeSerializer(ModelSerializer):
    class Meta:
        model = PostLike
        fields = (
            'id',
            'post',
            'user_id',
        )
        extra_kwargs = {
            'user_id': {'write_only': True},
        }

class PostLikeAnalyticsSerializer(Serializer):
    number_of_likes = IntegerField()


# coding = utf-8

import os
import torch
from torch.utils.data import (DataLoader, RandomSampler, SequentialSampler, TensorDataset, Dataset)

class InputExample(object):
    """A single training/test example for simple sequence classification."""

    def __init__(self, guid, _input, _output = None):
        """Constructs a InputExample.
        Args:
            Input: Prompt
            Output: Generated text
        """
        self.guid = guid
        self.input = _input
        self.output = _output

class DiaDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __getitem__(self, index):
        return self.data[index]

    def __len__(self):
        return len(self.data)

class DataProcessor(object):
    """Base class for data converters for sequence classification data sets."""
    def __init__(self, data_dir, dataset):
        self.data_dir = data_dir
        self.dataset = dataset

    def get_train_examples(self):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(self.data_dir, "train.txt")), "train")

    def get_dev_examples(self):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(self.data_dir, "dev.txt")), "dev")

    def get_test_examples(self):
        """See base class."""
        return self._create_examples(
            self._read_txt(os.path.join(self.data_dir, "test.txt")), "test")

    def _create_examples(self, lines, set_type):
        examples = []
        input_ = lines[0][0]
        output_ = lines[0][1]
        length = len(input_)
        for i in range(length):
            guid = "%s-%s" % (set_type, i+1)
            _input = input_[i]
            _output = output_[i]
            examples.append(InputExample(guid=guid, _input=_input, _output=_output))
        return examples

    @classmethod
    def _read_txt(cls, input_file):
        '''
        read file
        return format :
        '''
        if os.path.exists(input_file) is False:
            return []
        data = []
        input_text = []
        output_text = []

        with open(input_file, "r", encoding = "utf-8") as f:
            for line in f:
                if len(line) == 0:
                    continue
                splits = line.strip().split('\t')

                input_text.append(splits[0])
                output_text.append(splits[1])

            if len(input_text) > 0:
                data.append((input_text, output_text))
        return data

    def convert_to_feature(self, tokenizer, examples, max_seq_length=16):

        # entities
        features = []
        for ex_index, example in enumerate(examples):

            labels = []
            valid_ids1 = []
            valid_ids2 = []

            input_text = example.input
            output_text = example.output

            token1 = tokenizer.tokenize(input_text)
            token2 = tokenizer.tokenize(output_text)

            token1 = token1[:max_seq_length - 2]
            token2 = token2[:max_seq_length - 2]

            for m in range(len(token1)):
                if m == 0:
                    valid_ids1.append(1)
                else:
                    valid_ids1.append(0)
            for m in range(len(token2)):
                if m == 0:
                    valid_ids2.append(1)
                else:
                    valid_ids2.append(0)

            tokens1 = ["[CLS]"] + token1 + ["[SEP]"]
            tokens2 = ["[CLS]"] + token2 + ["[SEP]"]
            valid_ids1 = [1] + valid_ids1 + [1]
            valid_ids2 = [1] + valid_ids2 + [1]
            input_ids1 = tokenizer.convert_tokens_to_ids(tokens1)
            input_ids2 = tokenizer.convert_tokens_to_ids(tokens2)

            segment_ids = [0] * max_seq_length

            if len(input_ids1) < max_seq_length:
                input_ids1 += [0] * (max_seq_length - len(input_ids1))
                valid_ids1 += [0] * (max_seq_length - len(valid_ids1))

            if len(input_ids2) < max_seq_length:
                input_ids2 += [0] * (max_seq_length - len(input_ids2))
                valid_ids2 += [0] * (max_seq_length - len(valid_ids2))

            assert len(input_ids1) == max_seq_length
            assert len(input_ids2) == max_seq_length
            assert len(segment_ids) == max_seq_length
            assert len(valid_ids1) == max_seq_length
            assert len(valid_ids2) == max_seq_length

            features.append({
                "input_ids": torch.tensor(input_ids1, dtype=torch.long),
                "output_ids": torch.tensor(input_ids2, dtype=torch.long),
                "segment_ids": torch.tensor(segment_ids, dtype=torch.long),
                "valid_ids1": torch.tensor(valid_ids1, dtype=torch.long),
                "valid_ids2": torch.tensor(valid_ids2, dtype=torch.long),
                "input_text": example.input,
                "output_text": example.output
            })

        return features

    def get_dataloader(self, features, batch_size, mode='train', rank=0,  world_size=1):
        if mode == "train" and world_size > 1:
            features = features[rank::world_size]

        data_set = DiaDataset(features)
        sampler = RandomSampler(data_set)
        return DataLoader(data_set, sampler=sampler, batch_size=batch_size)

    def get_all_dataloader(self, tokenizer, args):
        #train
        train_examples = self.get_train_examples()
        train_features = self.convert_to_feature(tokenizer, train_examples, args.max_seq_len)
        train_dataloader = self.get_dataloader(train_features, mode="train", rank=args.rank,
                                                    world_size=args.world_size, batch_size=args.batch_size)

        #test
        test_examples = self.get_test_examples()
        test_features = self.convert_to_feature(tokenizer, test_examples, args.max_seq_len)
        test_dataloader = self.get_dataloader(test_features, mode="test", batch_size=1)

        #dev
        dev_examples = self.get_dev_examples()
        dev_features = self.convert_to_feature(tokenizer, dev_examples, args.max_seq_len)
        dev_dataloader = self.get_dataloader(dev_features, mode="dev", batch_size=1)

        return train_dataloader, dev_dataloader, test_dataloader


import datetime
from util.util import CGreeks, CSingleOptHolding
from util.COption import COption
import pandas as pd

class COptHolding(object):
    """期权持仓类"""

    def __init__(self, str_logfile_path=None):
        """
        初始化持仓数据及持仓汇总希腊字母
        class attributes:
        holdings: {'code': CSingleOptHolding}
        mode: 模式，逐步加仓、恒定gamma
        status: 状态，onposition=建仓，positioned=建仓完成，onliquidation=平仓
        cashinflow: 资金流入(卖出开仓、卖出平仓)
        cashoutflow: 资金流出(买入开仓、买入平仓)
        commission: 佣金
        pandl: 盈亏
        capital: 规模
        gammaexposure: gamma暴露
        greeks: CGreeks类
        logger: 日志类
        logfilepath: 日志文件路径
        """
        self.holdings = {}
        self.mode = ''
        self.status = ''
        self.cashinflow = 0.0
        self.cashoutflow = 0.0
        self.commission = 0.0
        self.pandl = 0.0
        self.capital = 0.0
        self.gammaexposure = 0.0
        self.greeks = CGreeks()
        # self.logger = None
        self.logfilepath = str_logfile_path

    def load_holdings(self, holding_filename):
        """加载持仓数据,同时加载期权的分钟行情数据"""
        # 先清空持仓数据
        self.holdings = {}
        with open(holding_filename, 'rt') as f:
            bIsPandLSection = False
            bIsHoldingSection = False
            while True:
                strline = f.readline().strip('\n')
                if not strline:
                    break
                if strline == '[begin of P&L]':
                    bIsPandLSection = True
                    strline = f.readline().strip('\n')
                if strline == '[end of P&L]':
                    bIsPandLSection = False
                    strline = f.readline().strip('\n')
                if strline == '[begin of holdings]':
                    bIsHoldingSection = True
                    strline = f.readline().strip('\n')
                if strline == '[end of holdings]':
                    bIsHoldingSection = False

                if bIsPandLSection:
                    ind_name, ind_value = strline.split(sep='=')
                    if ind_name == 'mode':
                        self.mode = ind_value
                    elif ind_name == 'status':
                        if self.status != 'onliquidation':
                            self.status = ind_value
                    elif ind_name == 'cashinflow':
                        self.cashinflow = float(ind_value)
                    elif ind_name == 'cashoutflow':
                        self.cashoutflow = float(ind_value)
                    elif ind_name == 'commission':
                        self.commission = float(ind_value)
                    elif ind_name == 'pandl':
                        self.pandl = float(ind_value)
                    elif ind_name == 'capital':
                        self.capital = float(ind_value)
                    elif ind_name == 'gammaexposure':
                        self.gammaexposure = float(ind_value)
                if bIsHoldingSection:
                    holding_data = strline.split(sep='|')
                    dict_holding = {}
                    for data in holding_data:
                        ind_name, ind_value = data.split(sep='=')
                        dict_holding[ind_name] = ind_value
                    if dict_holding['code'] not in self.holdings:
                        holding_side = int(dict_holding['holdingside'])
                        holding_vol = int(dict_holding['holdingvol'])
                        holding_opt = COption(dict_holding['code'])
                        self.holdings[dict_holding['code']] = CSingleOptHolding(holding_side, holding_vol, holding_opt)
                    else:
                        # strmsg = "持仓文件：" + holding_filename + "中期权" + dict_holding['code'] + "的持仓数据重复\n"
                        strmsg = "holding data of option:%s in holding file: %s was duplicated.\n" % (dict_holding['code'], holding_filename)
                        # self.logger.error(strmsg)
                        with open(self.logfilepath, 'at') as f:
                            f.write(strmsg)

    def save_holdings(self, holding_filename):
        """保存持仓数据"""
        # 如果当前的状态为onliquidation，那么把状态改为onposition
        if self.status == 'onliquidation':
            self.status = 'onposition'
        with open(holding_filename, 'wt') as f:
            f.write('[begin of P&L]\n')
            f.write('mode=%s\n' % self.mode)
            f.write('status=%s\n' % self.status)
            f.write('cashinflow=%0.2f\n' % self.cashinflow)
            f.write('cashoutflow=%0.2f\n' % self.cashoutflow)
            f.write('commission=%0.2f\n' % self.commission)
            f.write('pandl=%0.2f\n' % self.pandl)
            f.write('capital=%0.2f\n' % self.capital)
            f.write('gammaexposure=%0.2f\n' % self.gammaexposure)
            f.write('gamma_mv=%0.2f\n' % self.greeks.gamma_mv)
            f.write('delta_mv=%0.2f\n' % self.greeks.delta_mv)
            f.write('total_margin=%0.2f\n' % self.total_margin())
            f.write('[end of P&L]\n\n')

            f.write('[begin of holdings]\n')
            for opt_code, opt_holding in self.holdings.items():
                f.write('code=%s|holdingside=%d|holdingvol=%d\n' % (opt_code, opt_holding.holdingside, opt_holding.holdingvol))
            f.write('[end of holdings]\n')

    def calc_greeks(self, underlying_price, risk_free, dividend_rate, vol, calc_datetime=datetime.datetime.now()):
        """计算期权持仓的汇总希腊字母值"""
        self.greeks.delta = 0.0
        self.greeks.gamma = 0.0
        self.greeks.vega = 0.0
        self.greeks.delta_mv = 0.0
        self.greeks.gamma_mv = 0.0
        # 遍历持仓，先计算每个持仓期权的希腊字母，然后汇总希腊字母值
        for code, holding in self.holdings.items():
            holding.COption.calc_greeks(underlying_price, risk_free, dividend_rate, vol, calc_datetime)
            self.greeks.delta += (holding.COption.greeks.delta * holding.COption.multiplier *
                                  holding.holdingside * holding.holdingvol)
            self.greeks.gamma += (holding.COption.greeks.gamma * holding.COption.multiplier *
                                  holding.holdingside * holding.holdingvol)
            self.greeks.vega += (holding.COption.greeks.vega * holding.COption.multiplier *
                                 holding.holdingside * holding.holdingvol)
            self.greeks.delta_mv += (holding.COption.greeks.delta_mv * holding.holdingside * holding.holdingvol)
            self.greeks.gamma_mv += (holding.COption.greeks.gamma_mv * holding.holdingside * holding.holdingvol)

    def calc_margin(self, trading_day):
        """
        计算持仓期权的开仓保证金
        :param trading_day: 日期（类型=datetime.date）
        :return:
        """
        # 1.读取标的日K线时间序列
        underlying_quote = pd.read_csv('../data/underlying_daily_quote.csv', index_col=0, parse_dates=[0])
        underlying_pre_close = float(underlying_quote.ix[trading_day, 'pre_close'])
        # 2.读取样本期权的日行情
        strdate = trading_day.strftime('%Y-%m-%d')
        strfilepath = '../../opt_quote/' + strdate + '/50OptionDailyQuote.csv'
        opts_quote = pd.read_csv(strfilepath, usecols=range(1, 14), parse_dates=[0], encoding='gb18030', dtype={'option_code':str})
        opts_quote.set_index(keys='option_code', inplace=True)
        # 3.计算持仓期权的开仓保证金
        for optcode, holding in self.holdings.items():
            if optcode in opts_quote.index:
                opt_pre_settle = float(opts_quote.ix[optcode, 'pre_settle'])
                holding.COption.calc_margin(opt_pre_settle, underlying_pre_close)
            else:
                holding.COption.margin = 3000.0

    def total_margin(self):
        """返回持仓期权的合计保证金"""
        fmargin = 0.0
        for optcode, optholding in self.holdings.items():
            if optholding.holdingside == -1:
                fmargin += optholding.COption.margin * optholding.holdingvol
        return fmargin

    def margin_ratio(self):
        """返回保证金占资金规模的比例"""
        return self.total_margin() / self.capital

    def get_least_timevalue_opts(self, underlying_price, trading_datetime, exclusions=None):
        """
        取得持仓中时间价值最小的认购、认沽期权
        :param underlying_price: 标的最新价格
        :param trading_datetime: 交易时间(类型=datetime.datetime)
        :param exclusions: 需要排除的代码列表
        :return: tuple(认购COption,认沽COption)
        """
        if exclusions is None:
            exclusions = []
        opt_call = None
        opt_put = None
        for optcode, holding in self.holdings.items():
            if holding.holdingvol > 0 and optcode not in exclusions:
                if holding.COption.opt_type == "Call":
                    if opt_call is None:
                        opt_call = holding.COption
                    else:
                        if holding.COption.time_value(underlying_price, trading_datetime) < \
                                opt_call.time_value(underlying_price, trading_datetime):
                            opt_call = holding.COption
                else:
                    if opt_put is None:
                        opt_put = holding.COption
                    else:
                        if holding.COption.time_value(underlying_price, trading_datetime) < \
                                opt_put.time_value(underlying_price, trading_datetime):
                            opt_put = holding.COption
        return opt_call, opt_put

    def get_minmax_gamma_opts(self, exclusions=None):
        """
        取得持仓中gamma值最大的认购认沽期权，以及gamma值最小的认购认沽期权
        :param exclusions: 需要排除的代码列表
        :return: dict{'min'/'max':tuple(COption of call, COption of put)}
        """
        if exclusions is None:
            exclusions = []
        min_gamma_call = None
        min_gamma_put = None
        max_gamma_call = None
        max_gamma_put = None
        for optcode, holding in self.holdings.items():
            if holding.holdingvol > 0 and optcode not in exclusions:
                if holding.COption.opt_type == 'Call':
                    if min_gamma_call is None:
                        min_gamma_call = holding.COption
                    if max_gamma_call is None:
                        max_gamma_call = holding.COption
                    if holding.COption.greeks.gamma < min_gamma_call.greeks.gamma:
                        min_gamma_call = holding.COption
                    if holding.COption.greeks.gamma > max_gamma_call.greeks.gamma:
                        max_gamma_call = holding.COption
                else:
                    if min_gamma_put is None:
                        min_gamma_put = holding.COption
                    if max_gamma_put is None:
                        max_gamma_put = holding.COption
                    if holding.COption.greeks.gamma < min_gamma_put.greeks.gamma:
                        min_gamma_put = holding.COption
                    if holding.COption.greeks.gamma > max_gamma_put.greeks.gamma:
                        max_gamma_put = holding.COption
        return {'min': (min_gamma_call, min_gamma_put), 'max': (max_gamma_call, max_gamma_put)}

    def verify_update_tradedata(self, tradedata):
        """
        校验单条交易记录的有效性，若有效，那么同时更新交易数据至持仓数据
        :param tradedata: 单条期权交易记录
        :return: 校验通过并返回True，校验错误返回False
        """
        if tradedata.code in self.holdings:
            if tradedata.tradeside == 'buy' and tradedata.openclose == 'open':
                if self.holdings[tradedata.code].holdingside == 1:
                    self.holdings[tradedata.code].holdingvol += tradedata.tradevol
                    self.cashoutflow += tradedata.tradevalue
                    self.commission += tradedata.commission
                    # return True
                else:
                    return False
            elif tradedata.tradeside == 'buy' and tradedata.openclose == 'close':
                if self.holdings[tradedata.code].holdingside == -1:
                    self.holdings[tradedata.code].holdingvol -= tradedata.tradevol
                    self.cashoutflow += tradedata.tradevalue
                    self.commission += tradedata.commission
                    # return True
                else:
                    return False
            elif tradedata.tradeside == 'sell' and tradedata.openclose == 'open':
                if self.holdings[tradedata.code].holdingside == -1:
                    self.holdings[tradedata.code].holdingvol += tradedata.tradevol
                    self.cashinflow += tradedata.tradevalue
                    self.commission += tradedata.commission
                    # return True
                else:
                    return False
            elif tradedata.tradeside == 'sell' and tradedata.openclose == 'close':
                if self.holdings[tradedata.code].holdingside == 1:
                    self.holdings[tradedata.code].holdingvol -= tradedata.tradevol
                    self.cashinflow += tradedata.tradevalue
                    self.commission += tradedata.commission
                    # return True
                else:
                    return False
        else:
            if tradedata.openclose == 'close':
                return False
            elif tradedata.tradeside == 'buy' and tradedata.openclose == 'open':
                # holding = CSingleOptHolding(side=1, vol=tradedata.tradevol, opt=COption(tradedata.code))
                holding = CSingleOptHolding(side=1, vol=tradedata.tradevol, opt=tradedata.opt)
                self.cashoutflow += tradedata.tradevalue
                self.commission += tradedata.commission
                self.holdings[tradedata.code] = holding
                # return True
            elif tradedata.tradeside == 'sell' and tradedata.openclose == 'open':
                # holding = CSingleOptHolding(side=-1, vol=tradedata.tradevol, opt=COption(tradedata.code))
                holding = CSingleOptHolding(side=-1, vol=tradedata.tradevol, opt=tradedata.opt)
                self.cashinflow += tradedata.tradevalue
                self.commission += tradedata.commission
                self.holdings[tradedata.code] = holding
                # return True
            else:
                return
        # 如果该交易记录对应期权持仓量=0，那么删除该条期权持仓
        if self.holdings[tradedata.code].holdingvol == 0:
            del_holding = self.holdings.pop(tradedata.code)
            if del_holding is not None:
                # self.logger.info("期权%s的持仓量等于0，该持仓已删除。" % tradedata.code)
                # strmsg = "期权%s的持仓量等于0，该持仓已删除。\n" % tradedata.code
                strmsg = "holding vol of option: %s is equal to 0, the holding data was deleted.\n" % tradedata.code
            else:
                # self.logger.error("删除期权%s持仓数据失败！" % tradedata.code)
                # strmsg = "删除期权%s持仓数据失败！\n" % tradedata.code
                strmsg = "failed to delete holding data of option: %s\n" % tradedata.code
            with open(self.logfilepath, 'at') as f:
                f.write(strmsg)
        return True

    def update_holdings(self, tradedatas):
        """
        根据给定的期权交易数据列表，更新持仓数据
        :param tradedatas: 期权交易数据(COptTradeData)列表
        :return: 无
        """
        # 遍历期权交易数据列表，更新每条期权交易数据
        with open(self.logfilepath, 'at') as f:
            for tradedata in tradedatas:
                log_msg = "trade info: time=%s,code=%s,tradeside=%s,openclose=%s,price=%f,vol=%d,value=%f,commission=%f\n" % \
                          (tradedata.time.strftime('%H:%M:%S'), tradedata.code, tradedata.tradeside, tradedata.openclose,
                           tradedata.tradeprice, tradedata.tradevol, tradedata.tradevalue, tradedata.commission)
                # self.logger.info(log_msg)
                f.write(log_msg)
                if not self.verify_update_tradedata(tradedata):
                    # print('%s,期权%s的交易数据出错\n' % (tradedata.time.strftime('%H:%M:%S'), tradedata.code))
                    # self.logger.error('上一条交易数据错误')
                    f.write('the last trading data was wrong.\n')
        # 更新持仓期权的保证金
        if tradedatas:
            self.calc_margin(tradedatas[0].time.date())

    def holding_mv(self, trading_datetime):
        """
        计算持仓期权的总市值
        :param trading_datetime: 计算时间，类型=datetime.datetime
        :return:
        """
        opt_mv = 0.0
        for optcode, optholding in self.holdings.items():
            opt_price = optholding.COption.quote_1min.ix[trading_datetime, 'close']
            opt_mv += optholding.holdingside * optholding.holdingvol * opt_price * optholding.COption.multiplier
        return opt_mv

    def p_and_l(self, trading_datetime):
        """
        计算给定时间点时的盈亏
        :param trading_datetime: 计算时间，类型=datetime.datetime
        :return:
        """
        self.pandl = self.holding_mv(trading_datetime) + self.cashinflow - self.cashoutflow - self.commission
        return self.pandl


import torch
import cv2
import numpy as np
import math

from sklearn.metrics import f1_score
from torch.autograd import Variable
from matplotlib.image import imread

# function for colorizing a label image:
def label_img_to_color(img: torch.Tensor):
    # label_to_color = {
    #     0: [128, 64,128],
    #     1: [244, 35,232],
    #     2: [ 70, 70, 70],
    #     3: [102,102,156],
    #     4: [190,153,153],
    #     5: [153,153,153],
    #     6: [250,170, 30],
    #     7: [220,220,  0],
    #     8: [107,142, 35],
    #     9: [152,251,152],
    #     10: [ 70,130,180],
    #     11: [220, 20, 60],
    #     12: [255,  0,  0],
    #     13: [  0,  0,142],
    #     14: [  0,  0, 70],
    #     15: [  0, 60,100],
    #     16: [  0, 80,100],
    #     17: [  0,  0,230],
    #     18: [119, 11, 32],
    #     19: [81,  0, 81]
    #     }

    label_to_color = {
        0: [255, 255, 255],
        1: [0, 0, 0],
        }

    label_to_color = {
        0: [0, 0, 0],
        1: [255, 255, 255],
        }

    img = img.squeeze(0)
    img = img.unsqueeze(-1)
    img = img.repeat(1, 1, 3)  # repeat 3 channel on grey scale image

    img_color = torch.where(img[:, :, :, ] == torch.tensor([1, 1, 1]), torch.tensor(label_to_color[1]), torch.tensor(label_to_color[0]))    # temp version

    return img_color

def grey_to_heatmap(img):
    heatmap = cv2.applyColorMap(img, cv2.COLORMAP_JET)

    return heatmap

def otsu_threshold(src):
    """
    src: Tensor (1, 1, w, h) or (1, w, h) or (w, h)

    return: ndarray (1, w, h)
    """

    output_np = src.cpu().detach().numpy().squeeze(0).squeeze(0) * 255
    _, output_th = cv2.threshold(output_np.astype(np.uint8), -1, 1, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
    output_th = output_th.reshape((1, output_th.shape[0], output_np.shape[1]))

    return output_th

def threshold(src, th):
    """
    src: Tensor (1, 1, w, h) or (1, w, h) or (w, h)

    return: ndarray (1, w, h)
    """

    output_np = src.cpu().detach().numpy().squeeze(0) * 255
    th = th * 255

    _, output_th = cv2.threshold(output_np.astype(np.uint8), th, 1, cv2.THRESH_BINARY)
    output_th = output_th.reshape((1, output_th.shape[0], output_np.shape[1]))

    return output_th

def resize_img_by_resolution(img, maximum_resolution=None):
    if maximum_resolution is None:
        maximum_resolution = 1280 * 720
    else:
        maximum_resolution = int(maximum_resolution[0] * maximum_resolution[1])

    img_width = img.width
    img_height = img.height
    img_definition = img_width * img_height

    if img_definition > maximum_resolution:
        reduction_ratio = img_definition / maximum_resolution

        reduction_ratio = math.sqrt(reduction_ratio)

        img_width_r = int(img_width / reduction_ratio)
        img_height_r = int(img_height / reduction_ratio)

        img = img.resize((img_width_r, img_height_r))

    return img

# https://github.com/VainF/DeepLabV3Plus-Pytorch/blob/master/metrics/stream_metrics.py
class _StreamMetrics(object):
    def __init__(self):
        """ Overridden by subclasses """
        raise NotImplementedError()

    def update(self, gt, pred):
        """ Overridden by subclasses """
        raise NotImplementedError()

    def get_results(self):
        """ Overridden by subclasses """
        raise NotImplementedError()

    def to_str(self, metrics):
        """ Overridden by subclasses """
        raise NotImplementedError()

    def reset(self):
        """ Overridden by subclasses """
        raise NotImplementedError()

class StreamSegMetrics(_StreamMetrics):
    """
    Stream Metrics for Semantic Segmentation Task
    """

    def __init__(self, n_classes):
        self.n_classes = n_classes
        self.confusion_matrix = np.zeros((n_classes, n_classes))

    def update(self, label_trues, label_preds):
        for lt, lp in zip(label_trues, label_preds):
            self.confusion_matrix += self._fast_hist(lt.flatten(), lp.flatten())

    @staticmethod
    def to_str(results):
        string = "\n"
        for k, v in results.items():
            if k != "Class IoU":
                string += "%s: %f\n" % (k, v)

        # string+='Class IoU:\n'
        # for k, v in results['Class IoU'].items():
        #    string += "\tclass %d: %f\n"%(k, v)
        return string

    def _fast_hist(self, label_true, label_pred):
        mask = (label_true >= 0) & (label_true < self.n_classes)
        hist = np.bincount(
            self.n_classes * label_true[mask].astype(int) + label_pred[mask],
            minlength=self.n_classes ** 2,
        ).reshape(self.n_classes, self.n_classes)
        return hist

    def get_results(self):
        """Returns accuracy score evaluation result.
            - overall accuracy
            - mean accuracy
            - mean IU
            - fwavacc
        """
        hist = self.confusion_matrix
        acc = np.diag(hist).sum() / hist.sum()
        acc_cls = np.diag(hist) / hist.sum(axis=1)
        acc_cls = np.nanmean(acc_cls)
        iu = np.diag(hist) / (hist.sum(axis=1) + hist.sum(axis=0) - np.diag(hist))
        mean_iu = np.nanmean(iu)
        freq = hist.sum(axis=1) / hist.sum()
        fwavacc = (freq[freq > 0] * iu[freq > 0]).sum()
        cls_iu = dict(zip(range(self.n_classes), iu))

        return {
            "Overall Acc": acc,
            "Mean Acc": acc_cls,
            "FreqW Acc": fwavacc,
            "Mean IoU": mean_iu,
            "Class IoU": cls_iu,
        }

    def reset(self):
        self.confusion_matrix = np.zeros((self.n_classes, self.n_classes))

class AverageMeter(object):
    """Computes average values"""

    def __init__(self):
        self.book = dict()

    def reset_all(self):
        self.book.clear()

    def reset(self, id):
        item = self.book.get(id, None)
        if item is not None:
            item[0] = 0
            item[1] = 0

    def update(self, id, val):
        record = self.book.get(id, None)
        if record is None:
            self.book[id] = [val, 1]
        else:
            record[0] += val
            record[1] += 1

    def get_results(self, id):
        record = self.book.get(id, None)
        assert record is not None
        return record[0] / record[1]

class ImageProcessing(object):
    '''
    @issue
    'hsv_to_rgb' and 'rgb_to_hsv' convert the image with H 180 value to 0, resulting blue color to red color

    '''

    @staticmethod
    def rgb_to_lab(img, is_training=True):
        """ PyTorch implementation of RGB to LAB conversion: https://docs.opencv.org/3.3.0/de/d25/imgproc_color_conversions.html
        Based roughly on a similar implementation here: https://github.com/affinelayer/pix2pix-tensorflow/blob/master/pix2pix.py
        :param img: image to be adjusted
        :returns: adjusted image
        :rtype: Tensor

        """
        img = img.permute(2, 1, 0)
        shape = img.shape
        img = img.contiguous()
        img = img.view(-1, 3)

        img = (img / 12.92) * img.le(0.04045).float() + (((torch.clamp(img,
                                                                       min=0.0001) + 0.055) / 1.055) ** 2.4) * img.gt(
            0.04045).float()

        rgb_to_xyz = Variable(torch.FloatTensor([  # X        Y          Z
            [0.412453, 0.212671, 0.019334],  # R
            [0.357580, 0.715160, 0.119193],  # G
            [0.180423, 0.072169,
             0.950227],  # B
        ]), requires_grad=False).cuda()

        img = torch.matmul(img, rgb_to_xyz)
        img = torch.mul(img, Variable(torch.FloatTensor(
            [1 / 0.950456, 1.0, 1 / 1.088754]), requires_grad=False).cuda())

        epsilon = 6 / 29

        img = ((img / (3.0 * epsilon ** 2) + 4.0 / 29.0) * img.le(epsilon ** 3).float()) + \
              (torch.clamp(img, min=0.0001) **
               (1.0 / 3.0) * img.gt(epsilon ** 3).float())

        fxfyfz_to_lab = Variable(torch.FloatTensor([[0.0, 500.0, 0.0],  # fx
                                                    # fy
                                                    [116.0, -500.0, 200.0],
                                                    # fz
                                                    [0.0, 0.0, -200.0],
                                                    ]), requires_grad=False).cuda()

        img = torch.matmul(img, fxfyfz_to_lab) + Variable(
            torch.FloatTensor([-16.0, 0.0, 0.0]), requires_grad=False).cuda()

        img = img.view(shape)
        img = img.permute(2, 1, 0)

        '''
        L_chan: black and white with input range [0, 100]
        a_chan/b_chan: color channels with input range ~[-110, 110], not exact
        [0, 100] => [0, 1],  ~[-110, 110] => [0, 1]
        '''
        img[0, :, :] = img[0, :, :] / 100
        img[1, :, :] = (img[1, :, :] / 110 + 1) / 2
        img[2, :, :] = (img[2, :, :] / 110 + 1) / 2

        img[(img != img).detach()] = 0

        img = img.contiguous()

        return img.cuda()

    @staticmethod
    def lab_to_rgb(img, is_training=True):
        """ PyTorch implementation of LAB to RGB conversion: https://docs.opencv.org/3.3.0/de/d25/imgproc_color_conversions.html
        Based roughly on a similar implementation here: https://github.com/affinelayer/pix2pix-tensorflow/blob/master/pix2pix.py
        :param img: image to be adjusted
        :returns: adjusted image
        :rtype: Tensor
        """
        img = img.permute(2, 1, 0)
        shape = img.shape
        img = img.contiguous()
        img = img.view(-1, 3)
        img_copy = img.clone()

        img_copy[:, 0] = img[:, 0] * 100
        img_copy[:, 1] = ((img[:, 1] * 2) - 1) * 110
        img_copy[:, 2] = ((img[:, 2] * 2) - 1) * 110

        img = img_copy.clone().cuda()
        del img_copy

        lab_to_fxfyfz = Variable(torch.FloatTensor([  # X Y Z
            [1 / 116.0, 1 / 116.0, 1 / 116.0],  # R
            [1 / 500.0, 0, 0],  # G
            [0, 0, -1 / 200.0],  # B
        ]), requires_grad=False).cuda()

        img = torch.matmul(
            img + Variable(torch.cuda.FloatTensor([16.0, 0.0, 0.0])), lab_to_fxfyfz)

        epsilon = 6.0 / 29.0

        img = (((3.0 * epsilon ** 2 * (img - 4.0 / 29.0)) * img.le(epsilon).float()) +
               ((torch.clamp(img, min=0.0001) ** 3.0) * img.gt(epsilon).float()))

        # denormalize for D65 white point
        img = torch.mul(img, Variable(
            torch.cuda.FloatTensor([0.950456, 1.0, 1.088754])))

        xyz_to_rgb = Variable(torch.FloatTensor([  # X Y Z
            [3.2404542, -0.9692660, 0.0556434],  # R
            [-1.5371385, 1.8760108, -0.2040259],  # G
            [-0.4985314, 0.0415560, 1.0572252],  # B
        ]), requires_grad=False).cuda()

        img = torch.matmul(img, xyz_to_rgb)

        img = (img * 12.92 * img.le(0.0031308).float()) + ((torch.clamp(img,
                                                                        min=0.0001) ** (
                                                                        1 / 2.4) * 1.055) - 0.055) * img.gt(
            0.0031308).float()

        img = img.view(shape)
        img = img.permute(2, 1, 0)

        img = img.contiguous()
        img[(img != img).detach()] = 0

        return img

    @staticmethod
    def swapimdims_3HW_HW3(img):
        """Move the image channels to the first dimension of the numpy
        multi-dimensional array

        :param img: numpy nd array representing the image
        :returns: numpy nd array with permuted axes
        :rtype: numpy nd array

        """
        if img.ndim == 3:
            return np.swapaxes(np.swapaxes(img, 1, 2), 0, 2)
        elif img.ndim == 4:
            return np.swapaxes(np.swapaxes(img, 2, 3), 1, 3)

    @staticmethod
    def swapimdims_HW3_3HW(img):
        """Move the image channels to the last dimensiion of the numpy
        multi-dimensional array

        :param img: numpy nd array representing the image
        :returns: numpy nd array with permuted axes
        :rtype: numpy nd array

        """
        if img.ndim == 3:
            return np.swapaxes(np.swapaxes(img, 0, 2), 1, 2)
        elif img.ndim == 4:
            return np.swapaxes(np.swapaxes(img, 1, 3), 2, 3)

    @staticmethod
    def load_image(img_filepath, normaliser):
        """Loads an image from file as a numpy multi-dimensional array

        :param img_filepath: filepath to the image
        :returns: image as a multi-dimensional numpy array
        :rtype: multi-dimensional numpy array

        """
        img = ImageProcessing.normalise_image(
            imread(img_filepath), normaliser)  # NB: imread normalises to 0-1
        return img

    @staticmethod
    def normalise_image(img, normaliser):
        """Normalises image data to be a float between 0 and 1

        :param img: Image as a numpy multi-dimensional image array
        :returns: Normalised image as a numpy multi-dimensional image array
        :rtype: Numpy array

        """
        img = img.astype('float32') / normaliser
        return img

    @staticmethod
    def compute_mse(original, result):
        """Computes the mean squared error between to RGB images represented as multi-dimensional numpy arrays.

        :param original: input RGB image as a numpy array
        :param result: target RGB image as a numpy array
        :returns: the mean squared error between the input and target images
        :rtype: float

        """
        return ((original - result) ** 2).mean()

    @staticmethod
    def compute_psnr(image_batchA, image_batchB, max_intensity):
        """Computes the PSNR for a batch of input and output images

        :param image_batchA: numpy nd-array representing the image batch A of shape Bx3xWxH
        :param image_batchB: numpy nd-array representing the image batch A of shape Bx3xWxH
        :param max_intensity: maximum intensity possible in the image (e.g. 255)
        :returns: average PSNR for the batch of images
        :rtype: float

        """
        num_images = image_batchA.shape[0]
        psnr_val = 0.0

        for i in range(0, num_images):
            imageA = image_batchA[i, 0:3, :, :]
            imageB = image_batchB[i, 0:3, :, :]
            imageB = np.maximum(0, np.minimum(imageB, max_intensity))
            psnr_val += 10 * \
                        np.log10(max_intensity ** 2 /
                                 ImageProcessing.compute_mse(imageA, imageB))

        return psnr_val / num_images

    @staticmethod
    def hsv_to_rgb(img):
        """Converts a HSV image to RGB
        PyTorch implementation of RGB to HSV conversion: https://docs.opencv.org/3.3.0/de/d25/imgproc_color_conversions.html
        Based roughly on a similar implementation here: http://code.activestate.com/recipes/576919-python-rgb-and-hsv-conversion/

        :param img: HSV image
        :returns: RGB image
        :rtype: Tensor

        """
        img = torch.clamp(img, 0, 1)
        img = img.permute(2, 1, 0)

        m1 = 0
        m2 = (img[:, :, 2] * (1 - img[:, :, 1]) - img[:, :, 2]) / 60
        m3 = 0
        m4 = -1 * m2
        m5 = 0

        r = img[:, :, 2] + torch.clamp(img[:, :, 0] * 360 - 0, 0, 60) * m1 + torch.clamp(img[:, :, 0] * 360 - 60, 0,
                                                                                60) * m2 + torch.clamp(
            img[:, :, 0] * 360 - 120, 0, 120) * m3 + torch.clamp(img[:, :, 0] * 360 - 240, 0, 60) * m4 + torch.clamp(
            img[:, :, 0] * 360 - 300, 0, 60) * m5

        m1 = (img[:, :, 2] - img[:, :, 2] * (1 - img[:, :, 1])) / 60
        m2 = 0
        m3 = -1 * m1
        m4 = 0

        g = img[:, :, 2] * (1 - img[:, :, 1]) + torch.clamp(img[:, :, 0] * 360 - 0, 0, 60) * m1 + torch.clamp(
            img[:, :, 0] * 360 - 60,
            0, 120) * m2 + torch.clamp(img[:, :, 0] * 360 - 180, 0, 60) * m3 + torch.clamp(img[:, :, 0] * 360 - 240, 0,
                                                                                120) * m4

        m1 = 0
        m2 = (img[:, :, 2] - img[:, :, 2] * (1 - img[:, :, 1])) / 60
        m3 = 0
        m4 = -1 * m2

        b = img[:, :, 2] * (1 - img[:, :, 1]) + torch.clamp(img[:, :, 0] * 360 - 0, 0, 120) * m1 + torch.clamp(
            img[:, :, 0] * 360 -
            120, 0, 60) * m2 + torch.clamp(img[:, :, 0] * 360 - 180, 0, 120) * m3 + torch.clamp(
            img[:, :, 0] * 360 - 300, 0, 60) * m4

        img = torch.stack((r, g, b), 2)
        img[(img != img).detach()] = 0

        img = img.permute(2, 1, 0)
        img = img.contiguous()
        img = torch.clamp(img, 0, 1)

        return img

    @staticmethod
    def rgb_to_hsv(img):
        """Converts an RGB image to HSV
        PyTorch implementation of RGB to HSV conversion: https://docs.opencv.org/3.3.0/de/d25/imgproc_color_conversions.html
        Based roughly on a similar implementation here: http://code.activestate.com/recipes/576919-python-rgb-and-hsv-conversion/

        :param img: RGB image
        :returns: HSV image
        :rtype: Tensor

        """
        img = torch.clamp(img, 0.000000001, 1)

        img = img.permute(2, 1, 0)
        # 3, H, W
        shape = img.shape

        img = img.contiguous()
        img = img.view(-1, 3)

        mx = torch.max(img, 1)[0]
        mn = torch.min(img, 1)[0]

        ones = Variable(torch.FloatTensor(
            torch.ones((img.shape[0])))).cuda()
        zero = Variable(torch.FloatTensor(torch.zeros(shape[0:2]))).cuda()

        img = img.view(shape)

        ones1 = ones[0:math.floor((ones.shape[0] / 2))]
        ones2 = ones[math.floor(ones.shape[0] / 2):(ones.shape[0])]

        mx1 = mx[0:math.floor((ones.shape[0] / 2))]
        mx2 = mx[math.floor(ones.shape[0] / 2):(ones.shape[0])]
        mn1 = mn[0:math.floor((ones.shape[0] / 2))]
        mn2 = mn[math.floor(ones.shape[0] / 2):(ones.shape[0])]

        df1 = torch.add(mx1, torch.mul(ones1 * -1, mn1))
        df2 = torch.add(mx2, torch.mul(ones2 * -1, mn2))

        df = torch.cat((df1, df2), 0)
        del df1, df2
        df = df.view(shape[0:2]) + 1e-10
        mx = mx.view(shape[0:2])

        img = img.cuda()
        df = df.cuda()
        mx = mx.cuda()

        g = img[:, :, 1].clone().cuda()
        b = img[:, :, 2].clone().cuda()
        r = img[:, :, 0].clone().cuda()

        img_copy = img.clone()

        img_copy[:, :, 0] = (((g - b) / df) * r.eq(mx).float() + (2.0 + (b - r) / df)
                             * g.eq(mx).float() + (4.0 + (r - g) / df) * b.eq(mx).float())
        img_copy[:, :, 0] = img_copy[:, :, 0] * 60.0

        zero = zero.cuda()
        img_copy2 = img_copy.clone()

        img_copy2[:, :, 0] = img_copy[:, :, 0].lt(zero).float(
        ) * (img_copy[:, :, 0] + 360) + img_copy[:, :, 0].ge(zero).float() * (img_copy[:, :, 0])

        img_copy2[:, :, 0] = img_copy2[:, :, 0] / 360

        del img, r, g, b

        img_copy2[:, :, 1] = mx.ne(zero).float() * (df / mx) + \
                             mx.eq(zero).float() * (zero)
        img_copy2[:, :, 2] = mx

        img_copy2[(img_copy2 != img_copy2).detach()] = 0

        img = img_copy2.clone()

        img = img.permute(2, 1, 0)
        img = torch.clamp(img, 0.000000001, 1)

        return img

    @staticmethod
    def apply_curve(img, C, slope_sqr_diff, channel_in, channel_out,
                    clamp=True, same_channel=True):
        """Applies a peicewise linear curve defined by a set of knot points to
        an image channel

        :param img: image to be adjusted
        :param C: predicted knot points of curve
        :returns: adjusted image
        :rtype: Tensor

        """
        slope = Variable(torch.zeros((C.shape[0] - 1))).cuda()
        curve_steps = C.shape[0] - 1
        '''
        Compute the slope of the line segments
        '''
        for i in range(0, C.shape[0] - 1):
            slope[i] = C[i + 1] - C[i]

        '''
        Compute the squared difference between slopes
        '''
        for i in range(0, slope.shape[0] - 1):
            slope_sqr_diff += (slope[i + 1] - slope[i]) * (slope[i + 1] - slope[i])

        '''
        Use predicted line segments to compute scaling factors for the channel
        '''
        scale = float(C[0])
        for i in range(0, slope.shape[0] - 1):
            if clamp:
                scale += float(slope[i]) * (torch.clamp(img[:, :, channel_in] * curve_steps - i, 0, 1))
                # scale += float(slope[i]) * (torch.clamp(img[:, :, channel_in], 0, 1))
            else:
                scale += float(slope[i]) * (img[:, :, channel_in] * curve_steps - i)
        img_copy = img.clone()

        if same_channel:
            # channel in and channel out are the same channel
            img_copy[:, :, channel_out] = img[:, :, channel_in] * scale
        else:
            # otherwise
            img_copy[:, :, channel_out] = img[:, :, channel_out] * scale

        img_copy = torch.clamp(img_copy, 0, 1)

        return img_copy, slope_sqr_diff

    @staticmethod
    def adjust_hsv(img, S):
        """Adjust the HSV channels of a HSV image using learnt curves

        :param img: image to be adjusted
        :param S: predicted parameters of piecewise linear curves
        :returns: adjust image, regularisation term
        :rtype: Tensor, float

        """
        img = img.squeeze(0).permute(2, 1, 0)
        shape = img.shape
        img = img.contiguous()

        S1 = torch.exp(S[0:int(S.shape[0] / 4)])
        S2 = torch.exp(S[(int(S.shape[0] / 4)):(int(S.shape[0] / 4) * 2)])
        S3 = torch.exp(S[(int(S.shape[0] / 4) * 2):(int(S.shape[0] / 4) * 3)])
        S4 = torch.exp(S[(int(S.shape[0] / 4) * 3):(int(S.shape[0] / 4) * 4)])

        slope_sqr_diff = Variable(torch.zeros(1) * 0.0).cuda()

        '''
        Adjust Hue channel based on Hue using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img, S1, slope_sqr_diff, channel_in=0, channel_out=0)

        '''
        Adjust Saturation channel based on Hue using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, S2, slope_sqr_diff, channel_in=0, channel_out=1, same_channel=False)

        '''
        Adjust Saturation channel based on Saturation using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, S3, slope_sqr_diff, channel_in=1, channel_out=1)

        '''
        Adjust Value channel based on Value using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, S4, slope_sqr_diff, channel_in=2, channel_out=2)

        img = img_copy.clone()
        del img_copy

        img[(img != img).detach()] = 0

        img = img.permute(2, 1, 0)
        img = img.contiguous()

        return img, slope_sqr_diff

    @staticmethod
    def adjust_sv(img, S):
        """Adjust the HSV channels of a HSV image using learnt curves

        :param img: image to be adjusted
        :param S: predicted parameters of piecewise linear curves
        :returns: adjust image, regularisation term
        :rtype: Tensor, float

        """
        img = img.squeeze(0).permute(2, 1, 0)
        img = img.contiguous()

        S3 = torch.exp(S[(int(S.shape[0] / 2) * 0):(int(S.shape[0] / 2) * 1)])
        S4 = torch.exp(S[(int(S.shape[0] / 2) * 1):(int(S.shape[0] / 2) * 2)])

        slope_sqr_diff = Variable(torch.zeros(1) * 0.0).cuda()

        '''
        Adjust Saturation channel based on Saturation using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img, S3, slope_sqr_diff, channel_in=1, channel_out=1)

        '''
        Adjust Value channel based on Value using the predicted curve
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, S4, slope_sqr_diff, channel_in=2, channel_out=2)

        img = img_copy.clone()
        del img_copy

        img[(img != img).detach()] = 0

        img = img.permute(2, 1, 0)
        img = img.contiguous()

        return img, slope_sqr_diff

    @staticmethod
    def adjust_rgb(img, R):
        """Adjust the RGB channels of a RGB image using learnt curves

        :param img: image to be adjusted
        :param S: predicted parameters of piecewise linear curves
        :returns: adjust image, regularisation term
        :rtype: Tensor, float

        """
        img = img.squeeze(0).permute(2, 1, 0)
        shape = img.shape
        img = img.contiguous()

        '''
        Extract the parameters of the three curves
        '''
        R1 = torch.exp(R[0:int(R.shape[0] / 3)])
        R2 = torch.exp(R[(int(R.shape[0] / 3)):(int(R.shape[0] / 3) * 2)])
        R3 = torch.exp(R[(int(R.shape[0] / 3) * 2):(int(R.shape[0] / 3) * 3)])

        '''
        Apply the curve to the R channel
        '''
        slope_sqr_diff = Variable(torch.zeros(1) * 0.0).cuda()

        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img, R1, slope_sqr_diff, channel_in=0, channel_out=0)

        '''
        Apply the curve to the G channel
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, R2, slope_sqr_diff, channel_in=1, channel_out=1)

        '''
        Apply the curve to the B channel
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, R3, slope_sqr_diff, channel_in=2, channel_out=2)

        img = img_copy.clone()
        del img_copy

        img[(img != img).detach()] = 0

        img = img.permute(2, 1, 0)
        img = img.contiguous()

        return img, slope_sqr_diff

    @staticmethod
    def adjust_lab(img, L):
        """Adjusts the image in LAB space using the predicted curves

        :param img: Image tensor
        :param L: Predicited curve parameters for LAB channels
        :returns: adjust image, and regularisation parameter
        :rtype: Tensor, float

        """
        img = img.permute(2, 1, 0)

        shape = img.shape
        img = img.contiguous()

        '''
        Extract predicted parameters for each L,a,b curve
        '''
        L1 = torch.exp(L[0:int(L.shape[0] / 3)])
        L2 = torch.exp(L[(int(L.shape[0] / 3)):(int(L.shape[0] / 3) * 2)])
        L3 = torch.exp(L[(int(L.shape[0] / 3) * 2):(int(L.shape[0] / 3) * 3)])

        slope_sqr_diff = Variable(torch.zeros(1) * 0.0).cuda()

        '''
        Apply the curve to the L channel
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img, L1, slope_sqr_diff, channel_in=0, channel_out=0)

        '''
        Now do the same for the a channel
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, L2, slope_sqr_diff, channel_in=1, channel_out=1)

        '''
        Now do the same for the b channel
        '''
        img_copy, slope_sqr_diff = ImageProcessing.apply_curve(
            img_copy, L3, slope_sqr_diff, channel_in=2, channel_out=2)

        img = img_copy.clone()
        del img_copy

        img[(img != img).detach()] = 0

        img = img.permute(2, 1, 0)
        img = img.contiguous()

        return img, slope_sqr_diff


"""Transforms for preprocessing images during data loading"""
import PIL
import torch
import copy
import numpy as np

def img_pad(img, mode='warp', size=224):
    """
    Pads a given image.
    Crops and/or pads a image given the boundries of the box needed
    img: the image to be coropped and/or padded
    bbox: the bounding box dimensions for cropping
    size: the desired size of output
    mode: the type of padding or resizing. The modes are,
        warp: crops the bounding box and resize to the output size
        same: only crops the image
        pad_same: maintains the original size of the cropped box  and pads with zeros
        pad_resize: crops the image and resize the cropped box in a way that the longer edge is equal to
        the desired output size in that direction while maintaining the aspect ratio. The rest of the image is
        padded with zeros
        pad_fit: maintains the original size of the cropped box unless the image is biger than the size in which case
        it scales the image down, and then pads it
    """
    assert (mode in ['same', 'warp', 'pad_same', 'pad_resize', 'pad_fit']), 'Pad mode %s is invalid' % mode
    image = img.copy()
    if mode == 'warp':
        warped_image = image.resize((size, size), PIL.Image.BICUBIC)
        return warped_image
    elif mode == 'same':
        return image
    elif mode in ['pad_same', 'pad_resize', 'pad_fit']:
        img_size = image.size  # size is in (width, height)
        ratio = float(size) / max(img_size)
        if mode == 'pad_resize' or \
                (mode == 'pad_fit' and (img_size[0] > size or img_size[1] > size)):
            img_size = tuple([int(img_size[0] * ratio), int(img_size[1] * ratio)])
            image = image.resize(img_size, PIL.Image.BICUBIC)
        padded_image = PIL.Image.new("RGB", (size, size))
        padded_image.paste(image, ((size - img_size[0]) // 2,
                                   (size - img_size[1]) // 2))
        return padded_image

def squarify_bbox(bbox, squarify_ratio, img_width):
    width = abs(bbox[0] - bbox[2])
    height = abs(bbox[1] - bbox[3])
    width_change = height * squarify_ratio - width
    bbox[0] = bbox[0] - width_change / 2
    bbox[2] = bbox[2] + width_change / 2
    # Squarify is applied to bounding boxes in Matlab coordinate starting from 1
    if bbox[0] < 0:
        bbox[0] = 0
    # check whether the new bounding box goes beyond image boarders
    # If this is the case, the bounding box is shifted back
    if bbox[2] > img_width:
        # bbox[1] = str(-float(bbox[3]) + img_dimensions[0])
        bbox[0] = bbox[0] - bbox[2] + img_width
        bbox[2] = img_width

    return bbox

def bbox_sanity_check(img, bbox):
    """
    This is to confirm that the bounding boxes are within image boundaries.
    If this is not the case, modifications is applied.
    This is to deal with inconsistencies in the annotation tools
    """
    img_width, img_heigth = img.size
    if bbox[0] < 0:
        bbox[0] = 0.0
    if bbox[1] < 0:
        bbox[1] = 0.0
    if bbox[2] >= img_width:
        bbox[2] = img_width - 1
    if bbox[3] >= img_heigth:
        bbox[3] = img_heigth - 1
    return bbox

def jitter_bbox(img, bbox, mode, ratio):
    """
    This method jitters the position or dimentions of the bounding box.
    mode: 'same' returns the bounding box unchanged
          'enlarge' increases the size of bounding box based on the given ratio.
          'random_enlarge' increases the size of bounding box by randomly sampling a value in [0,ratio)
          'move' moves the center of the bounding box in each direction based on the given ratio
          'random_move' moves the center of the bounding box in each direction
          by randomly sampling a value in [-ratio,ratio)
    ratio: The ratio of change relative to the size of the bounding box. For modes 'enlarge' and 'random_enlarge'
           the absolute value is considered.
    Note: Tha ratio of change in pixels is calculated according to the smaller dimension of the bounding box.
    """
    assert (mode in ['same', 'enlarge', 'move', 'random_enlarge', 'random_move']), \
        'mode %s is invalid.' % mode

    if mode == 'same':
        return bbox

    if mode in ['random_enlarge', 'enlarge']:
        jitter_ratio = abs(ratio)
    else:
        jitter_ratio = ratio

    if mode == 'random_enlarge':
        jitter_ratio = np.random.random_sample() * jitter_ratio
    elif mode == 'random_move':
        # for ratio between (-jitter_ratio, jitter_ratio)
        # for sampling the formula is [a,b), b > a,
        # random_sample * (b-a) + a
        jitter_ratio = np.random.random_sample() * jitter_ratio * 2 - jitter_ratio

    if len(bbox) == 4:
        b = copy.deepcopy(bbox)
        bbox_width = b[2] - b[0]
        bbox_height = b[3] - b[1]

        width_change = bbox_width * jitter_ratio
        height_change = bbox_height * jitter_ratio

        if width_change < height_change:
            height_change = width_change
        else:
            width_change = height_change

        if mode in ['enlarge', 'random_enlarge']:
            b[0] = b[0] - width_change // 2
            b[1] = b[1] - height_change // 2
        else:
            b[0] = b[0] + width_change // 2
            b[1] = b[1] + height_change // 2

        b[2] = b[2] + width_change // 2
        b[3] = b[3] + height_change // 2

        # Checks to make sure the bbox is not exiting the image boundaries
        jit_box = bbox_sanity_check(img, b)

    return jit_box

def crop_and_rescale(image, bbox, cropping_ratio, width, height):
    """
    Crop the top 1/n of the image and resize the image to desired size.
    The bbox are preprocessed accordingly.
    """
    w, h = image.size
    image = image.crop((0, h * cropping_ratio, w, h))
    # rescale
    image_new = image.resize((width, height), PIL.Image.BICUBIC)
    # compute new bbox
    scale_x = width / w
    scale_y = height / h / (1 - cropping_ratio)
    x1 = bbox[0] * scale_x
    y1 = (bbox[1] - h * cropping_ratio) * scale_y
    x2 = bbox[2] * scale_x
    y2 = (bbox[3] - h * cropping_ratio) * scale_y
    bbox_new = [int(x1), int(y1), int(x2), int(y2)]

    return image_new, bbox_new

def random_flip(image, bbox, probability):
    """
    Flip the image horizontally with given probability.
    When the image is flipped,
    the bbox annotation wll also be transformed correspondingly.
    """
    if float(torch.rand(1).item()) < probability:
        image = image.transpose(PIL.Image.FLIP_LEFT_RIGHT)
        w, h = image.size
        # box_w = abs(bbox[0] - bbox[2])
        x_max = w - bbox[0]
        x_min = w - bbox[2]
        bbox[0] = x_min
        bbox[2] = x_max

    return image, bbox


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Nov 16 17:57:13 2019

@author: jcunanan
"""
#######################################
#      Problem  Description
#Bob the Adventurer is one step away from solving the mystery of an ancient Mayan tomb.
#He just approched the secret chamber where the secret Mayan scriptures are locked in a chest.
#There are N ancient statues in the room.
#After long thought, Bob figured out that in order to open the treasure chest
#he needs to stand in the middle of the room and hit every statue with a laser ray at the same time.
#Bob is a highly experienced adventurer, so setting multiple laser rays at the same time is not a problem for him.
#Moreover, every ray that he creates is perfectly straight and never changes direction at all.

#The middle of the room, where Bob is standing, has coordinates (0, 0).
#Every statue is located at some point with coordinates (x, y).
#Each statue is made of pure glass, so that if any ray hits it, it does not stop,
#but goes through the statue and continues beyond in the same, unchanged direction.
#Bob wonders how he can hit every ancient statue in the room using the fewest rays possible.

#Assume that the following declarations are given:

#class Point2D { public int x; public int y; }

#Write a function

#class Solution { public int solution(Point2D[] A); }

#that, given an array of points A, representing the locations of the statues,
#returns the minimal number of rays that Bob must set in order to hit every statue in the room.

#For example, given an array A

#A[0].x = -1 A[0].y = -2 (statue 0) A[1].x = 1 A[1].y = 2 (statue 1) A[2].x = 2 A[2].y = 4 (statue 2) A[3].x = -3 A[3].y = 2 (statue 3) A[4].x = 2 A[4].y = -2 (statue 4)
#your function should return 4.

 #https://i.stack.imgur.com/ad5gc.png

#As is shown in the image, it is possible to create four rays in such a way that:

#the first will hit statue 0;
#the second will hit statues 1 and 2;
#the third will hit statue 3;
#the fourth will hit statue 4.
#Assume that:

#N is an integer within the range [1..100,000];
#the coordinates of each point in array A are integers within the range [−1,000,000,000..1,000,000,000];
#the elements of A are all distinct;
#Array A does not contain point (0,0).
#Complexity:

#expected worst-case time complexity is O(N);
#expected worst-case space complexity is O(N*log(N)), beyond input storage (not counting the storage required for input arguments).

#from extratypes import Point2D  # library with types used in the task
from sympy import Point2D

def solution(A):
    # write your code in Python 3.6
    N = len(A)
    slopeE = []
    slopeW = []
    sN = 0
    sS = 0

    for i in range(N):

        if Point2D(A[i]).x == 0 and Point2D(A[i]).y > 0:
            sN = 1

        elif Point2D(A[i]).x == 0 and Point2D(A[i]).y < 0:
            sS = 1

        if Point2D(A[i]).x >0 :
            if Point2D(A[i]).y / Point2D(A[i]).x not in slopeE:
                slopeE.append(Point2D(A[i]).y / Point2D(A[i]).x)

        if Point2D(A[i]).x < 0:
            if Point2D(A[i]).y / Point2D(A[i]).x not in slopeW:
                slopeW.append(Point2D(A[i]).y / Point2D(A[i]).x)

    return len(slopeE)+len(slopeW)+sN+sS


import base64
import logging
import sys
from string import digits, ascii_uppercase
import traceback
import time

from ins import *
from disasm import *
import gpu

class Tape:
    '''
    Tape is just a looped array of instructions.
    '''

    @classmethod
    def from_inss(cls, inss):
        '''Create tape from instructions'''
        ans = cls()
        ans.data = inss
        return ans

    @classmethod
    def from_bytes(cls, rom):
        ans = cls()
        # Each instruction is 24 bits, which is 3 bytes
        ans.data = []
        for i in range(0, len(rom), 3):
            ins_bytes = rom[i: i + 3]
            ans.data.append(Ins.from_bytes(ins_bytes))

        return ans

    def to_bytes(self):
        ans = [ins.to_bytes() for ins in self.data]
        return b''.join(ans)

    def __getitem__(self, i):
        return self.data[i]

    def __len__(self):
        return len(self.data)

    def __repr__(self):
        return str(self.data)

class Regs:
    '''
    64 registers that are 6-bit elements each.
    '''

    def __init__(self):
        self.data = [0] * 64

    def dump(self):
        n = 8
        for i in range(0, len(self.data), n):
            xs = self.data[i:i + n]
            xs = [str(x).rjust(2) for x in xs]
            print(' '.join(xs))

    def __getitem__(self, i):
        assert 0 <= self.data[i] < 64
        return self.data[i]

    def __setitem__(self, i, v):
        if i != 0:
            self.data[i] = v & 0o77

    def __len__(self):
        return len(self.data)

class Mem:
    '''
    2**18 16-bit words.
    '''

    def __init__(self):
        self.data = [0] * (2**18)
        self.addr = 0  # 18-bit index for `self.data`

    def dump(self):
        # Warning: very big
        n = 8
        for i in range(0, len(self.data), n):
            xs = self.data[i:i + n]
            xs = [str(x).rjust(2) for x in xs]
            print(' '.join(xs))

    def __getitem__(self, i):
        assert 0 <= self.data[i] < 64
        return self.data[i]

    def __setitem__(self, i, v):
        self.data[i] = v & 0o77

    def __len__(self):
        return len(self.data)

def twos_comp(n, bits=6):
    mask = (1 << bits) - 1
    neg = n ^ mask
    return (neg + 1) & mask

def to_int(n, bits=6):
    '''Turn a two's complement number into a Python int'''
    if (n & (1 << (bits - 1))) != 0:
        n = n - (1 << bits)
    return n

def bit_string(n, bits=6):
    return format(n % (1 << bits), '0{}b'.format(bits))

def from_int(n, bits=6):
    '''Turn a Python int to a two''s complement number'''
    return int(bit_string(n), 2)

def sar(n, d, bits=6):
    '''Arithmetic right shift'''
    sign = n & (1 << (bits - 1))
    if sign == 0:
        return n >> d
    else:
        inv_s = max(bits - d, 0)
        sign_mask = (1 << bits) - (1 << inv_s)
        return (n >> d) | sign_mask

def rol(n, d, bits=6):
    '''Rotate bits left'''
    s = bit_string(n)
    d = d % bits
    s = s[-d:] + s[:-d]
    return int(s, 2)

# `x` is a placeholder for an invalid character
serial_dict = digits + ascii_uppercase + ' +-*/<=>()[]{}#$_?|^&!~,.:\nx'

def chr_from_serial(n):
    return serial_dict[n]

def str_from_serial(ns):
    return ''.join([chr_from_serial(n) for n in ns])

def serial_from_chr(c):
    return serial_dict.index(c)

def serial_from_str(s):
    return bytearray([serial_from_chr(c) for c in s])

class Emu:
    def __init__(self, use_gpu=False):
        self.regs = Regs()
        self.mem = Mem()

        self.use_gpu = use_gpu
        if use_gpu:
            self.gpu = gpu.Gpu()
            self.gpu.start()

        self.pc = 0
        self.halted = False
        self.cf = False
        self.clock = 0

        self.buffer = ''
        self.out = sys.stdout

    @classmethod
    def from_filename(cls, filename, use_gpu=False):
        ans = cls(use_gpu)
        s = open(filename).read().strip()
        s = base64.b64decode(s)
        ans.tape = Tape.from_bytes(s)
        return ans

    # Ops

    def op_hlt(self, ins):
        self.halted = True

    # Arithmetic and logic

    def op_add(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] + self.regs[rb]

    def op_addi(self, ins):
        rd, ra, ib = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] + ib

    def op_sub(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] + twos_comp(self.regs[rb])

    def op_or(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] | self.regs[rb]

    def op_ori(self, ins):
        rd, ra, ib = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] | ib

    def op_xor(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] ^ twos_comp(self.regs[rb])

    def op_xori(self, ins):
        rd, ra, ib = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] ^ ib

    def op_and(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] & twos_comp(self.regs[rb])

    def op_andi(self, ins):
        rd, ra, ib = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] & ib

    def op_shl(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] << self.regs[rb]

    def op_shr(self, ins):
        rd, ra, rb = ins.a, ins.b, ins.c
        self.regs[rd] = self.regs[ra] >> self.regs[rb]

    # Comparison

    def op_cmp(self, ins):
        (cmp_type, cm, a, b) = ins.as_cmp()

        # Determine left and right sides of comparison
        if cmp_type == CmpType.RA_RB:
            left = self.regs[a]
            right = self.regs[b]
        elif cmp_type == CmpType.RB_RA:
            left = self.regs[b]
            right = self.regs[a]
        elif cmp_type == CmpType.RA_IB:
            left = self.regs[a]
            right = b
        elif cmp_type == CmpType.IA_RB:
            left = a
            right = self.regs[b]

        # Do comparison
        if cm == Cm.TR:
            self.cf = True
        elif cm == Cm.FA:
            self.cf = False
        elif cm == Cm.EQ:
            self.cf = left == right
        elif cm == Cm.NE:
            self.cf = left != right
        elif cm == Cm.SL:
            self.cf = to_int(left) < to_int(right)
        elif cm == Cm.SG:
            self.cf = to_int(left) > to_int(right)
        elif cm == Cm.UL:
            self.cf = left < right
        elif cm == Cm.UG:
            self.cf = left > right
        else:
            raise ValueError('Unhandled cm: {}'.format(cm))

    def op_shi(self, ins):
        (shi_type, rd, ra, ib) = ins.as_shi()

        if shi_type == ShiType.SHLI:
            self.regs[rd] = self.regs[ra] << ib
        elif shi_type == ShiType.SHRI:
            self.regs[rd] = self.regs[ra] >> ib
        elif shi_type == ShiType.SARI:
            self.regs[rd] = sar(self.regs[ra], ib)
        elif shi_type == ShiType.ROLI:
            self.regs[rd] = rol(self.regs[ra], ib)
        else:
            raise ValueError('Unhandled shi_type: {}'.format(shi_type))

    def op_ld(self, ins):
        rd, ra, ib = ins.a, ins.b, ins.c
        rs = (self.regs[ra] + ib) & 0o77
        self.regs[rd] = self.regs[rs]

    def op_st(self, ins):
        rs, ra, ib = ins.a, ins.b, ins.c
        rd = (self.regs[ra] + ib) & 0o77
        self.regs[rd] = self.regs[rs]

    def op_fm(self, ins):
        (fm_type, pr, rd, ra) = ins.as_fm()

        # 12 bit buffer
        ans = (self.regs[rd] * self.regs[ra]) & 0o7777
        if fm_type == FmType.U:
            ans = ans >> pr
        else:
            ans = sar(ans, pr, bits=12)

        # `ans` will be deduce back to 6 bits when stored
        self.regs[rd] = ans

    def op_lbl(self, ins):
        # Do nothing
        pass

    def op_jup(self, ins, reverse=False):
        key = (ins.partial_jump_key(), self.regs[ins.c])

        # Search for the label matching key
        start = self.pc
        i = start
        while True:
            if reverse:
                i = (i + 1) % len(self.tape)
            else:
                i = (i - 1) % len(self.tape)

            if i == start:
                raise ValueError('Couldn''t find label: {}'.format(key))

            ch_ins = self.tape[i]  # Check instruction
            if ch_ins.op == Op.LBL and \
                    self.should_execute(ch_ins) and \
                    ch_ins.label_key() == key:
                # We always increment the PC after executing an instruction.
                # To offset that, we subtract 1 here.
                self.pc = i - 1
                return

    def op_jdn(self, ins):
        self.op_jup(ins, reverse=True)

    def get_input(self):
        s = input('> ')
        self.buffer += s

    def op_io_serial_incoming(self, ins):
        (rd, ix_, rs_) = ins.as_io()

        # Optionally read more input if we don't have any in the buffer
        if len(self.buffer) == 0:
            self.get_input()

        # Send the length of buffer
        self.regs[rd] = from_int(len(self.buffer))

    def op_io_serial_read(self, ins):
        (rd, ix_, rs_) = ins.as_io()

        # Optionally read more input if we don't have any in the buffer
        if len(self.buffer) == 0:
            self.get_input()

        # Pop the first char and send it
        c = self.buffer[0]
        self.buffer = self.buffer[1:]
        self.regs[rd] = from_int(serial_from_chr(c))

    def op_io_serial_write(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        c = chr_from_serial(self.regs[rs])
        self.out.write(c)
        self.out.flush()

    def reset_clock(self):
        self.start = time.time()
        self.clock == 0
        if self.use_gpu:
            should_halt = self.gpu.update()
            if should_halt:
                self.halted = True

    def op_io_clock_lo_cs(self, ins):
        (rd, ix_, rs) = ins.as_io()
        if rs == 0:
            # Get lower 6 bits of clock
            self.regs[rd] = self.clock & 0o77
        else:
            self.reset_clock()

    def op_io_clock_hi_cs(self, ins):
        (rd, ix_, rs) = ins.as_io()
        if rs == 0:
            # Get upper 6 bits of clock
            self.regs[rd] = (self.clock & 0o7700) >> 6
        else:
            self.reset_clock()

    def op_io_mem_addr_lo(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        # Clear lower bits without clearing upper bits
        mask = (0o77 << 6) + (0o77 << (6 * 2))
        self.mem.addr &= mask
        self.mem.addr |= self.regs[rs]

    def op_io_mem_addr_mid(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        mask = 0o77 + (0o77 << (6 * 2))
        self.mem.addr &= mask
        self.mem.addr |= (self.regs[rs] << 6)

    def op_io_mem_addr_hi(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        mask = 0o77 + (0o77 << 6)
        self.mem.addr &= mask
        self.mem.addr |= (self.regs[rs] << (6 * 2))

    def op_io_mem_read(self, ins):
        (rd, ix_, rs_) = ins.as_io()
        self.regs[rd] = self.mem[self.mem.addr]
        self.mem.addr = (self.mem.addr + 1) % len(self.mem)

    def op_io_mem_write(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        self.mem[self.mem.addr] = self.regs[rs]
        self.mem.addr = (self.mem.addr + 1) % len(self.mem)

    def op_gpu_x(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        self.gpu.set_x(self.regs[rs])

    def op_gpu_y(self, ins):
        (rd_, ix_, rs) = ins.as_io()
        self.gpu.set_y(self.regs[rs])

    def op_gpu_draw(self, ins):
        (rd_, ix, rs) = ins.as_io()
        self.gpu.draw(self.regs[rs])

    op_io_switch = {
        IoDevice.SERIAL_INCOMING: op_io_serial_incoming,
        IoDevice.SERIAL_READ: op_io_serial_read,
        IoDevice.SERIAL_WRITE: op_io_serial_write,
        IoDevice.CLOCK_LO_CS: op_io_clock_lo_cs,
        IoDevice.CLOCK_HI_CS: op_io_clock_hi_cs,
        IoDevice.MEM_ADDR_HI: op_io_mem_addr_hi,
        IoDevice.MEM_ADDR_MID: op_io_mem_addr_mid,
        IoDevice.MEM_ADDR_LO: op_io_mem_addr_lo,
        IoDevice.MEM_READ: op_io_mem_read,
        IoDevice.MEM_WRITE: op_io_mem_write,
        IoDevice.GPU_X: op_gpu_x,
        IoDevice.GPU_Y: op_gpu_y,
        IoDevice.GPU_DRAW: op_gpu_draw,
    }

    def op_io(self, ins):
        (rd_, ix, rs_) = ins.as_io()
        if ix in Emu.op_io_switch:
            op_io_func = Emu.op_io_switch[ix]
            op_io_func(self, ins)
        else:
            logging.warning('Unknown IO device')
            self.halted = True

    op_switch = {
        Op.HLT: op_hlt,
        Op.ADD: op_add,
        Op.ADDI: op_addi,
        Op.SUB: op_sub,
        Op.OR: op_or,
        Op.ORI: op_ori,
        Op.XOR: op_xor,
        Op.XORI: op_xori,
        Op.AND: op_and,
        Op.ANDI: op_andi,
        Op.SHL: op_shl,
        Op.SHR: op_shr,
        Op.CMP: op_cmp,
        Op.SHI: op_shi,
        Op.LD: op_ld,
        Op.ST: op_st,
        Op.FM: op_fm,
        Op.LBL: op_lbl,
        Op.JUP: op_jup,
        Op.JDN: op_jdn,
        Op.IO: op_io,
    }

    def should_execute(self, ins):
        return \
            (ins.cond == Cond.UN) or \
            (ins.cond == Cond.TR and self.cf) or \
            (ins.cond == Cond.FA and not self.cf)

    def execute(self, ins):
        if self.should_execute(ins):
            if ins.op in Emu.op_switch:
                op_func = Emu.op_switch[ins.op]
                op_func(self, ins)
            else:
                raise ValueError('Unhandled op: {}'.format(ins.op))

    def run(self, log_inss=False):
        if log_inss:
            # Keep a log of instructions executed
            inss_log = []

        self.start = time.time()
        self.clock = 0

        while not self.halted:
            ins = self.tape[self.pc]

            if log_inss:
                inss_log.append(self.pc)

            # 19483
            self.execute(ins)
            self.pc = (self.pc + 1) % len(self.tape)  # Tape is looped

            now = time.time()
            elapsed = now - self.start
            elapsed = round(elapsed, 2)
            elapsed = int(elapsed * 100)

            # Clock does not wrap
            self.clock = min(elapsed, 0o7777)

        if self.use_gpu:
            self.gpu.quit()

        if log_inss:
            return inss_log

    def execute_dbg_cmd(self, cmd):
        '''Return value: whether to go to the next instruction or not'''
        cmd = cmd.split()
        if len(cmd) == 0:
            print('No cmd')
            return False

        try:
            if cmd[0] == 'p':
                i = int(cmd[1])
                print('regs[{}] = {}'.format(i, self.regs[i]))
            elif cmd[0] == 'x':
                i = int(cmd[1])
                print('mem[{}] = {}'.format(i, self.mem[i]))
            elif cmd[0] == 'xi':
                print('mem.addr = {}'.format(self.mem.addr))
            elif cmd[0] == 'reg':
                self.regs.dump()
            elif cmd[0] == 'l':
                inss = self.tape[self.pc: self.pc + 5]
                disasm = Disasm.disasm_tape(inss)
                print(disasm)
            elif cmd[0] in {'n', 's'}:
                return True
            elif cmd[0] == 'q':
                self.halted = True
                return True
            elif cmd[0] == 'c':
                self.stepping = False
                return True
            else:
                print('Unknown cmd')
                return False
        except ValueError:
            print('Error processing cmd')
            return False

    def run_dbg(self):
        self.clock = 0
        self.stepping = True
        prev_cmd = None

        while not self.halted:
            ins = self.tape[self.pc]
            print('{}: {}'.format(self.pc, Disasm.disasm(ins)))

            if self.stepping:
                while True:
                    cmd = input('edb> ')
                    if cmd == '' and prev_cmd:
                        cmd = prev_cmd

                    go_next = self.execute_dbg_cmd(cmd)
                    prev_cmd = cmd
                    if go_next:
                        break

            self.execute(ins)
            self.pc = (self.pc + 1) % len(self.tape)  # Tape is looped

            # Use fake clock where each instruction takes one centisecond
            self.clock = min(self.clock + 1, 0o7777)

    def save_tape(self, filename):
        with open(filename, 'w') as f:
            b = self.tape.to_bytes()
            s = base64.b64encode(b).decode()
            f.write(s)

if __name__ == '__main__':
    assert len(sys.argv) >= 2
    filename = sys.argv[1]

    emu = Emu.from_filename(filename, use_gpu=True)

    try:
        emu.run()
    except KeyboardInterrupt:
        traceback.print_exc(file=sys.stdout)
        # Print the PC before we quit
        print('EMU PC:', emu.pc)
        sys.exit(1)


import os, math, sys
from collections import Counter

def get_vocabulary(item_class):
    class_vocabulary = []
    for filename in os.listdir('train/' + item_class):
        file = open(os.path.join('train/' + item_class, filename), encoding='latin-1')
        class_vocabulary += [word for line in file for word in line.split() if word.isalpha()]
    return class_vocabulary

def test(item_class):
    classifications = []
    for filename in os.listdir('test/' + item_class):
        file = open(os.path.join('test/' + item_class, filename), encoding='latin-1')
        # W ← EXTRACTTOKENSFROMDOC(V, d)
        tokens = [word for line in file for word in line.split() if word in all_vocab]

        # do score[c] ← log prior[c]
        score_ham = math.log(ham_prior)
        score_spam = math.log(spam_prior)

        for token in tokens:
            # do score[c] += log condprob[t][c]
            score_ham += math.log(ham_bayes_dict[token])
            score_spam += math.log(spam_bayes_dict[token])

        if score_ham > score_spam:
            classifications.append('ham')
        else:
            classifications.append('spam')
    return classifications

# Nc ← COUNTDOCSINCLASS(D, c)
spam_count = len(os.listdir('train/spam'))
ham_count = len(os.listdir('train/ham'))

# N ← COUNTDOCS(D)
total_doc_count = spam_count + ham_count

# prior[c] ← Nc/N

spam_prior = spam_count / total_doc_count
ham_prior = ham_count / total_doc_count

# textc ← CONCATENATETEXTOFALLDOCSINCLASS(D, c)

spam_vocab = get_vocabulary("spam")
ham_vocab = get_vocabulary("ham")

exclude_stopwords = sys.argv[1].split('=')[1]

# if the user wants to exclude stopwords
if exclude_stopwords == 'true':
    stopwords = [line.rstrip() for line in open('stopwords.txt')]
    spam_vocab = [word for word in spam_vocab if word not in stopwords]
    ham_vocab = [word for word in ham_vocab if word not in stopwords]

# V ← EXTRACTVOCABULARY(D)

all_vocab = spam_vocab + ham_vocab

# Tct ← COUNTTOKENSOFTERM(textc, t)

spam_vocab_count_dict = Counter(spam_vocab)
ham_vocab_count_dict = Counter(ham_vocab)

# do condprob[t][c] ← Tct+1/∑t′(Tct′+1)
spam_bayes_dict = { k: (spam_vocab_count_dict[k] + 1) / (len(spam_vocab) + len(spam_vocab_count_dict)) for k in all_vocab}
ham_bayes_dict = { k: (ham_vocab_count_dict[k] + 1) / (len(ham_vocab) + len(ham_vocab_count_dict)) for k in all_vocab}

spam_classifications = test('spam')
ham_classifications = test('ham')

spam_accuracy = spam_classifications.count('spam') / len(spam_classifications)
ham_accuracy = ham_classifications.count('ham') / len(ham_classifications)

spam_test_files_len = len(os.listdir('test/spam'))
ham_test_files_len = len(os.listdir('test/ham'))

spam_test_ratio = spam_test_files_len / (spam_test_files_len + ham_test_files_len)
ham_test_ratio = ham_test_files_len / (spam_test_files_len + ham_test_files_len)

total_accuracy = spam_accuracy * spam_test_ratio + ham_accuracy * ham_test_ratio

print("spam percent classified accurately: " + str(spam_accuracy))
print("ham percent classified accurately: " + str(ham_accuracy))

print("total accuracy: " + str(total_accuracy) )


from keras.models import Sequential, load_model
from keras.layers import Embedding, Conv1D, MaxPooling1D, LSTM, GRU, Dense
from keras.layers.wrappers import Bidirectional

y_idx2word=8

def run_classifier(x_train_seq,x_test_seq,y_train_one_hot,y_test_one_hot):
    model = Sequential()
    print("-------------------------------------------building model----------------------------------------")
    # embedding layer
    model.add(Embedding(7000,
                        300,
                        input_length=20))
    # RNN layers
    for d in range(1):
        model.add(Bidirectional(GRU(units=100,
                                    dropout=0.2,
                                    recurrent_dropout=0.2,
                                    return_sequences=True)))

    model.add(Bidirectional(GRU(units=100,
                                dropout=0.2,
                                recurrent_dropout=0.2,
                                return_sequences=False)))

    # fully-connected output layer
    model.add(Dense(y_idx2word, activation='softmax'))

    # ---- COMPILE THE MODEL ----

    model.compile(loss='categorical_crossentropy',
                  optimizer='adam',
                  metrics=['accuracy'])

    model.summary()
    print("-------------------------------------------training model----------------------------------------")
    history = model.fit(x_train_seq,
                        y_train_one_hot,
                        batch_size=64,
                        epochs=15, validation_data=(x_test_seq, y_test_one_hot))
    print("-------------------------------------------model trained------------------------------------------")
    acc = model.evaluate(x_test_seq,y_test_one_hot)
    print("accuracy is:",acc[1]*100)
    return acc


def getadd():
    import requests
    import json
    import getip
    ip = getip.get()

    send_url = f'http://api.ipstack.com/{ip}?access_key=7cf3582503675544e752924eb3142e79&format=1'
    r = requests.get(send_url)
    j = json.loads(r.text)
    lat = str(j['latitude'])
    lon = str(j['longitude'])
    print(lat + ", " + lon)
    return j['city'] + ", " + j["country_code"]

def fame__():
    from config import db
    from flask import session
    views = 0
    user = "secret" #session.get('logged_in')
    print(user)
    rating = 0
    stmt = db.query("SELECT * FROM views WHERE friend_id = %s", (user,))

    for row in stmt:
        if row['to_user'] == user:
            if row['notification'].find("viewed") == 0:
                views += 1
    if views <= 5:
        rating += 1
    elif 5 < views < 50:
        for i in range(5, 50, 5):
            rating += i
    if views >= 50:
        rating = 10
    else:
        rating = 0
    print("up to here")
    db.query("UPDATE fame_rating SET rating = %s WHERE username = %s", (rating, user,))


import keyboard
import time
import random
from ctypes import windll, wintypes, byref
from functools import reduce

from cafe_coding_download import enable

enable()

f = open('cafe_coding_download.py', 'r', encoding='UTF-8')

data = f.read()
f.close()
i = 0
s = ''

while True:
    if keyboard.read_key():
        s += data[i]

        print('\r' + s, end='')

        if data[i] == '\n':
            s = ''
            #改行後の空白は一気に表示
            while data[i+1] == ' ':
                s += data[i+1]
                i+=1
            print('\r' + s, end='')

        i+=1

    # すべて文字を表示しきったら初期化
    if i == len(data):
        i = 0
        s = ''
        print()
        print()


from dataclasses import dataclass, field

# dataclass ja cria para nós o init, repr e eq
@dataclass(init=True)
class Pessoa:

    _nome: str
    _idade: float
    enderecos: list[str] = field(default_factory=list)

    def __post_init__(self):
        print("depois do init")

    @property
    def nome(self):
        return self._nome

if __name__ == "__main__":
    p1 = Pessoa('micael', 26)
    print(p1)


# author: sunshine
# datetime:2021/8/5 下午3:17
import torch
import torch.nn as nn
from transformers import BertModel

class SMPNet(nn.Module):
    def __init__(self, args, num_class):
        super(SMPNet, self).__init__()
        self.bert = BertModel.from_pretrained(args.bert_path)
        self.fc1 = nn.Sequential(
            nn.Dropout(0.1),
            nn.Linear(768 * 4, 128),
            nn.ReLU(),
            nn.Linear(128, num_class)
        )
        self.fc2 = nn.Sequential(
            nn.Dropout(0.1),
            nn.Linear(768 * 4, 128),
            nn.ReLU(),
            nn.Linear(128, num_class)
        )

    def forward(self, x1=None, x2=None):
        out = ()
        if x1 is not None:
            x1 = self.bert(*x1, output_hidden_states=True)
            x1_hidden = x1.hidden_states
            concat1 = torch.cat([x1_hidden[-1][:, 0], x1_hidden[-2][:, 0], x1_hidden[-3][:, 0], x1.pooler_output],
                                dim=-1)
            out1 = self.fc1(concat1)
            out = out + (out1,)
        if x2 is not None:
            x2 = self.bert(*x2, output_hidden_states=True)
            x2_hidden = x2.hidden_states
            concat2 = torch.cat([x2_hidden[-1][:, 0], x2_hidden[-2][:, 0], x2_hidden[-3][:, 0], x2.pooler_output],
                                dim=-1)
            out2 = self.fc2(concat2)
            out = out + (out2,)
        return out


from django.shortcuts import render
from .forms import UploadTransactionFileForm
from .models import TransactionFIles, Transactions
from .serializers import TransactionnsSerializer
from .utils.mixins import TransactionMixin
from rest_framework.generics import ListCreateAPIView
from rest_framework.views import APIView, Response, status
from django.http import HttpResponseRedirect
import os
import ipdb

def home_view(request):
    context = {}

    if request.POST:
        form = UploadTransactionFileForm(request.POST, request.FILES)

        if form.is_valid():
            file_upload = request.FILES["transaction_file"].read()
            file_text = file_upload.decode("UTF-8")
            file_arr = file_text.split("\n")
            # ipdb.set_trace()

            # for line in file_arr:
            #     ipdb.set_trace()

            data = []

            for line in file_arr:
                obj = {
                    "type": line[0],
                    "date": f"{line[1:5]}-{line[5:7]}-{line[7:9]}",
                    "value": line[9:19],
                    "cpf": line[19:30],
                    "credit_card": line[30:42],
                    "attendance_hour": f"{line[42:44]}:{line[44:46]}:{line[46:48]}",
                    "shop_owner": line[48:62],
                    "shop_name": line[62:80],
                }
                data.append(obj)

            # ipdb.set_trace()
            serializer = TransactionnsSerializer(data=data, many=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()

            context["data"] = data

            return render(request, "table.html", context)

        else:
            form = UploadTransactionFileForm()

            context["form"] = form
            return render(request, "home.html", context)

    context["form"] = UploadTransactionFileForm()
    return render(request, "home.html", context)

# def home_view(request):
#     context = {}

#     if request.POST:
#         form = UploadTransactionFileForm(request.POST, request.FILES)

#         if form.is_valid():
#             upload = request.FILES["transaction_file"]

#             teste = TransactionFIles(upload=upload)
#             teste.save()

#             ipdb.set_trace()
#         else:
#             form = UploadTransactionFileForm()

#             context["form"] = form
#             return render(request, "home.html", context)

#     context["form"] = UploadTransactionFileForm()
#     return render(request, "home.html", context)

class TransactionsView(APIView):
    def post(self, request):

        upload_file = request.FILES["transaction_file"].read()
        upload_txt = upload_file.decode("UTF-8")
        upload_arr = upload_txt.split("\n")

        for i in upload_arr:
            obj = {
                "type": i[0],
                "date": f"{i[1:5]} - {i[5:7]} - {i[7:9]}",
                "value": int(i[9:17].i[17:19]),
                "cpf": i[19:30],
                "credit_card": i[30:42],
                "hour": f"{i[42:44]:{i[44:46]:i[46:48]}}",
                "shop_owner": i[48:62],
                "shop_name": i[62:80],
            }

        serializer = TransactionnsSerializer(data=obj)
        serializer.is_valid(raise_exception=True)
        serializer.save()

        return Response(status.HTTP_201_CREATED)


"""
Code adapted from https://github.com/uvavision/Double-Hard-Debias/blob/master/eval.py
"""
import glob
import os
import numpy as np
from sklearn.utils import Bunch
from sklearn.cluster import AgglomerativeClustering, KMeans
from six import iteritems

def evaluate_categorization(word_vectors, X, y, method='kmeans', seed=None):
    """
    Evaluate embeddings on categorization task.
    Parameters
    ----------
    w: dict
      Embeddings to test.
    X: vector, shape: (n_samples, )
      Vector of words.
    y: vector, shape: (n_samples, )
      Vector of cluster assignments.
    """

    # Get all word embeddings into a matrix
    vectors = []
    for word in word_vectors:
        vectors.append(word_vectors[word])

    # Mean of all embeddings
    mean_vector = np.mean(vectors, axis=0, keepdims=True)

    w = word_vectors

    new_x = []
    new_y = []
    exist_cnt = 0
    for idx, word in enumerate(X.flatten()):
        if word in w:
            new_x.append(X[idx])
            new_y.append(y[idx])
            exist_cnt += 1

    # Number of words in BLESS that also exists in our vocabulary
    print('exist {} in {}'.format(exist_cnt, len(X)))

    X = np.array(new_x)
    y = np.array(new_y)

    # Put all the words that were in both BLESS and our vocab into a matrix
    words = np.vstack([w.get(word, mean_vector) for word in X.flatten()])
    ids = np.random.RandomState(seed).choice(range(len(X)), len(X), replace=False)

    # Evaluate clustering on several hyperparameters of AgglomerativeClustering and
    # KMeans
    best_purity = 0

    if method == "all" or method == "agglomerative":
        best_purity = calculate_purity(y[ids], AgglomerativeClustering(n_clusters=len(set(y)),
                                                                       affinity="euclidean",
                                                                       linkage="ward").fit_predict(words[ids]))
        for affinity in ["cosine", "euclidean"]:
            for linkage in ["average", "complete"]:
                purity = calculate_purity(y[ids], AgglomerativeClustering(n_clusters=len(set(y)),
                                                                          affinity=affinity,
                                                                          linkage=linkage).fit_predict(words[ids]))
                best_purity = max(best_purity, purity)

    if method == "all" or method == "kmeans":
        purity = calculate_purity(y[ids], KMeans(random_state=seed, n_init=10, n_clusters=len(set(y))).
                                  fit_predict(words[ids]))
        best_purity = max(purity, best_purity)

    return best_purity

def calculate_purity(y_true, y_pred, verbose=False):
    """
    Calculate purity for given true and predicted cluster labels.
    Parameters
    ----------
    y_true: array, shape: (n_samples, 1)
      True cluster labels
    y_pred: array, shape: (n_samples, 1)
      Cluster assingment.
    Returns
    -------
    purity: float
      Calculated purity.
    """
    assert len(y_true) == len(y_pred)
    true_clusters = np.zeros(
        shape=(len(set(y_true)), len(y_true)))  # creates sparse array (categories, words both in bench and vocab)
    pred_clusters = np.zeros_like(true_clusters)  # creates sparse array (categories, words both in bench and vocab)
    for id, cl in enumerate(set(y_true)):
        if verbose:
            print("true:", id)
        true_clusters[id] = (y_true == cl).astype("int")  # Everwhere the label is of a certain class, put a 1
    for id, cl in enumerate(set(y_pred)):
        if verbose:
            print("pred:", id)
        pred_clusters[id] = (y_pred == cl).astype("int")  # Everwhere the label is in a certain cluster, put a 1

    # For each clust in the prediction, find the true cluster that has the MOST overlap
    # Sum up the number of words that overlap between the pred and true cluster that has the most overlap
    # Divide this by (the number of words both in bench and vocab)
    M = pred_clusters.dot(true_clusters.T)
    return 1. / len(y_true) * np.sum(np.max(M, axis=1))

def evaluate_cate(wv_dict, benchmarks, method='all', seed=None):
    categorization_tasks = benchmarks
    categorization_results = {}

    # Calculate results using helper function
    for name, data in iteritems(categorization_tasks):
        print("Sample data from {}, num of samples: {} : \"{}\" is assigned class {}".format(
            name, len(data.X), data.X[0], data.y[0]))
        categorization_results[name] = evaluate_categorization(wv_dict, data.X, data.y, method=method, seed=None)
        print("Cluster purity on {} {}".format(name, categorization_results[name]))

def create_bunches(bench_paths):
    output = {}
    for path in bench_paths:
        files = glob.glob(os.path.join(path, "*.txt"))
        name = os.path.basename(path)
        if name == 'EN-BATTIG':
            sep = ","
        else:
            sep = " "

        X = []
        y = []
        names = []
        for cluster_id, file_name in enumerate(files):
            with open(file_name) as f:
                lines = f.read().splitlines()[:]
                X += [l.split(sep) for l in lines]
                y += [os.path.basename(file_name).split(".")[0]] * len(lines)
        output[name] = Bunch(X=np.array(X, dtype="object"), y=np.array(y).astype("object"))

        if sep == ",":
            data = output[name]
            output[name] = Bunch(X=data.X[:, 0], y=data.y, freq=data.X[:, 1], frequency=data.X[:, 2], rank=data.X[:, 3],
                                 rfreq=data.X[:, 4])

    return output