"""
@author: Aashis Khanal
@email: sraashis@gmail.com
"""

from collections import OrderedDict as _ODict
import os as _os
import torch as _torch

import coinstac_sparse_dinunet.config as _conf
import coinstac_sparse_dinunet.metrics as _base_metrics
import coinstac_sparse_dinunet.utils as _utils
import coinstac_sparse_dinunet.utils.tensorutils as _tu
import coinstac_sparse_dinunet.vision.plotter as _plot
from coinstac_sparse_dinunet.config.keys import *
from coinstac_sparse_dinunet.utils import stop_training_
from coinstac_sparse_dinunet.utils.logger import *
import torch
import torch as _torch
import copy
import torch.nn.functional as F
import torch.nn as nn
import types

class NNTrainer:
    def __init__(self, data_handle=None, **kw):
        self.cache = data_handle.cache
        self.input = _utils.FrozenDict(data_handle.input)
        self.state = _utils.FrozenDict(data_handle.state)
        self.nn = _ODict()
        self.device = _ODict()
        self.optimizer = _ODict()
        self.data_handle = data_handle

    def _init_nn_model(self):
        r"""
        User cam override and initialize required models in self.distrib dict.
        """
        raise NotImplementedError('Must be implemented in child class.')

    def _init_nn_weights(self, **kw):
        r"""
        By default, will initialize network with Kaimming initialization.
        If path to pretrained weights are given, it will be used instead.
        """

        if self.cache.get('pretrained_path') is not None:
            self.load_checkpoint(self.cache['pretrained_path'])
        elif self.cache['mode'] == Mode.TRAIN:
            _torch.manual_seed(self.cache['seed'])
            for mk in self.nn:
                _tu.initialize_weights(self.nn[mk])

    def _init_optimizer(self):
        r"""
        Initialize required optimizers here. Default is Adam,
        """
        first_model = list(self.nn.keys())[0]
        self.optimizer['adam'] = _torch.optim.Adam(self.nn[first_model].parameters(),
                                                   lr=self.cache['learning_rate'])

    def init_nn(self, init_model=False, init_optim=False, set_devices=False, init_weights=False):
        if init_model: self._init_nn_model()
        if init_optim: self._init_optimizer()
        if init_weights: self._init_nn_weights(init_weights=init_weights)
        if set_devices: self._set_gpus()

    def snip_forward_linear(self, params, x):

        return F.linear(x, params.weight * params.weight_mask, params.bias)

    def snip_forward_conv2d(self, params, x):

        return F.conv2d(x, params.weight * params.weight_mask, params.bias,
                        params.stride, params.padding, params.dilation, params.groups)

    def apply_mask_to_model(self, model, mask):

        prunable_layers = filter(
            lambda layer: isinstance(layer, nn.Conv2d) or isinstance(layer, nn.Linear),
            model.modules()
        )
        for layer, keep_mask in zip(prunable_layers, mask):
            assert (layer.weight.shape == keep_mask.shape)
            # Set the masked weights to zero (NB the biases are ignored)
            layer.weight.data[keep_mask == 0.] = 0.

        return model

    @staticmethod
    def _forward_pre_hook(module, x):
        module.mask.requires_grad_(False)
        mask = module.mask
        device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
        module.weight.data.mul_(mask.to(module.weight.to(device)))

    def register_pre_hook_mask(self, masks=None):
        masks_count = 0
        if masks is not None:
            print("Registering Mask!")
        assert masks is not None, 'Masks should be generated first.'
        for model_key in self.nn:
            for name, module in self.nn[model_key].named_modules():
                if isinstance(module, nn.Conv2d) or isinstance(module, nn.Linear):
                    module.mask = nn.Parameter(masks[masks_count]).requires_grad_(False).to(
                        module.weight.to(self.device['gpu']))
                    masks_count += 1
                    module.register_forward_pre_hook(self._forward_pre_hook)

    # def apply_mask_to_model(self, model, mask):
    #     prunable_layers = filter(
    #         lambda layer: isinstance(layer, nn.Conv2d) or isinstance(layer, nn.Linear),
    #         model.modules()
    #     )
    #     # print(torch.sum(torch.cat([torch.flatten(x == 1) for x in mask])))
    #     total_non_zero = 0
    #     for layer, keep_mask in zip(prunable_layers, mask):
    #         assert (layer.weight.shape == keep_mask.shape)
    #
    #         def hook_factory(keep_mask):
    #             """
    #             The hook function can't be defined directly here because of Python's
    #             late binding which would result in all hooks getting the very last
    #             mask! Getting it through another function forces early binding.
    #             """
    #
    #             def hook(grads):
    #                 return grads * keep_mask
    #
    #             return hook
    #
    #         # Step 1: Set the masked weights to zero (NB the biases are ignored)
    #         # Step 2: Make sure their gradients remain zero
    #         layer.weight.data[keep_mask == 0.] = 0.
    #         total_non_zero += torch.sum(torch.cat([torch.flatten(x != 0.) for x in layer.weight.data]))
    #         layer.weight.register_hook(hook_factory(keep_mask))
    #
    #     return model

    def apply_snip_pruning(self, dataset_cls):
        train_dataset = self.data_handle.get_train_dataset_for_masking(dataset_cls=dataset_cls)
        loader = self.data_handle.get_loader('train', dataset=train_dataset, drop_last=True, shuffle=True)
        mini_batch = next(iter(loader))  # inputs, labels, ix
        # inputs, labels = mini_batch['inputs'], mini_batch['labels']
        for model_key in self.nn:
            net = copy.deepcopy(self.nn[model_key])
            device = next(iter(self.nn[model_key].parameters())).device
            for layer in net.modules():
                if isinstance(layer, nn.Conv2d) or isinstance(layer, nn.Linear):
                    layer.weight_mask = nn.Parameter(_torch.ones_like(layer.weight)).to(device)
                    nn.init.xavier_normal(layer.weight)
                    layer.weight.requires_grad = False

                if isinstance(layer, nn.Linear):
                    layer.forward = types.MethodType(self.snip_forward_linear, layer)

                if isinstance(layer, nn.Conv2d):
                    layer.forward = types.MethodType(self.snip_forward_conv2d, layer)

            net.to(device)
            net.zero_grad()
            it = self.single_iteration_for_masking(net, mini_batch)
            sparsity_level = abs(1 - it['sparsity_level'])
            it['loss'].backward()

            grads_abs = []

            for layer in net.modules():
                if isinstance(layer, nn.Conv2d) or isinstance(layer, nn.Linear):
                    grads_abs.append(torch.abs(layer.weight_mask.grad))

            # Gather all scores in a single vector and normalise
            all_scores = torch.cat([torch.flatten(x) for x in grads_abs])
            norm_factor = torch.sum(all_scores)
            all_scores.div_(norm_factor)

            num_params_to_keep = int(len(all_scores) * sparsity_level)
            threshold, _ = torch.topk(all_scores, num_params_to_keep, sorted=True)
            acceptable_score = threshold[-1]
            keep_masks = []
            for g in grads_abs:
                keep_masks.append(((g / norm_factor) >= acceptable_score).float())

            # initialize_mult = []
            # for i in range(len(grads_abs)):
            #     initialize_mult.append(grads_abs[i] / norm_factor)

            self.nn[model_key] = self.apply_mask_to_model(self.nn[model_key], keep_masks)
            return keep_masks

    def _set_gpus(self):
        self.device['gpu'] = _torch.device("cpu")
        if self.cache.get('gpus') is not None and len(self.cache['gpus']) > 0:
            if _conf.CUDA_AVAILABLE:
                self.device['gpu'] = _torch.device(f"cuda:{self.cache['gpus'][0]}")
                if len(self.cache['gpus']) >= 2:
                    for mkey in self.nn:
                        self.nn[mkey] = _torch.nn.DataParallel(self.nn[mkey], self.cache['gpus'])
            else:
                pass
                # raise Exception(f'*** GPU not detected in {self.state["clientId"]}. ***')
        for model_key in self.nn:
            self.nn[model_key] = self.nn[model_key].to(self.device['gpu'])

    def load_checkpoint(self, file_path):
        try:
            chk = _torch.load(file_path)
        except:
            chk = _torch.load(file_path, map_location='cpu')

        if chk.get('source', 'Unknown').lower() == 'coinstac':
            for m in chk['models']:
                try:
                    self.nn[m].module.load_state_dict(chk['models'][m])
                except:
                    self.nn[m].load_state_dict(chk['models'][m])

            for m in chk['optimizers']:
                try:
                    self.optimizer[m].module.load_state_dict(chk['optimizers'][m])
                except:
                    self.optimizer[m].load_state_dict(chk['optimizers'][m])
        else:
            mkey = list(self.nn.keys())[0]
            try:
                self.nn[mkey].module.load_state_dict(chk)
            except:
                self.nn[mkey].load_state_dict(chk)

    def save_checkpoint(self, file_path, src='coinstac'):
        checkpoint = {'source': src}
        for k in self.nn:
            checkpoint['models'] = {}
            try:
                checkpoint['models'][k] = self.nn[k].module.state_dict()
            except:
                checkpoint['models'][k] = self.nn[k].state_dict()
        for k in self.optimizer:
            checkpoint['optimizers'] = {}
            try:
                checkpoint['optimizers'][k] = self.optimizer[k].module.state_dict()
            except:
                checkpoint['optimizers'][k] = self.optimizer[k].state_dict()
        _torch.save(checkpoint, file_path)

    def evaluation(self, mode='eval', dataset_list=None, save_pred=False, use_padded_sampler=False):
        for k in self.nn:
            self.nn[k].eval()

        eval_avg, eval_metrics = self.new_averages(), self.new_metrics()
        eval_loaders = []

        for d in dataset_list:
            if d and len(d) > 0:
                eval_loaders.append(
                    self.data_handle.get_loader(handle_key=mode, dataset=d, shuffle=False,
                                                use_padded_sampler=use_padded_sampler)
                )

        def _update_scores(_out, _it, _avg, _metrics):
            if _out is None:
                _out = {}
            _avg.accumulate(_out.get('averages', _it['averages']))
            _metrics.accumulate(_out.get('metrics', _it['metrics']))

        with _torch.no_grad():
            for loader in eval_loaders:
                its = []
                metrics = self.new_metrics()
                avg = self.new_averages()

                for i, batch in enumerate(loader, 1):
                    it = self.iteration(batch)

                    if save_pred:
                        if self.cache['load_sparse']:
                            its.append(it)
                        else:
                            _update_scores(self.save_predictions(loader.dataset, it), it, avg, metrics)
                    else:
                        _update_scores(None, it, avg, metrics)

                    if self.cache['verbose'] and len(eval_loaders) <= 1 and lazy_debug(i):
                        info(
                            f" Itr:{i}/{len(loader)}, "
                            f"Averages:{it.get('averages').get()}, Metrics:{it.get('metrics').get()}"
                        )

                if save_pred and self.cache['load_sparse']:
                    its = self.reduce_iteration(its)
                    _update_scores(self.save_predictions(loader.dataset, its), its, avg, metrics)

                if self.cache['verbose'] and len(eval_loaders) > 1:
                    info(f" {mode}, {avg.get()}, {metrics.get()}")

                eval_metrics.accumulate(metrics)
                eval_avg.accumulate(avg)

            info(f"{mode} metrics: {eval_avg.get()}, {eval_metrics.get()}", self.cache.get('verbose'))
            return eval_avg, eval_metrics

    def training_iteration_local(self, i, batch):
        r"""
        Learning step for one batch.
        We decoupled it so that user could implement any complex/multi/alternate training strategies.
        """
        it = self.iteration(batch)
        it['loss'].backward()
        if i % self.cache.get('local_iterations', 1) == 0:
            first_optim = list(self.optimizer.keys())[0]
            self.optimizer[first_optim].step()
            self.optimizer[first_optim].zero_grad()
        return it

    def init_training_cache(self):
        self.cache[Key.TRAIN_LOG] = []
        self.cache[Key.VALIDATION_LOG] = []
        self.cache['best_val_epoch'] = 0
        self.cache.update(best_val_score=0.0 if self.cache['metric_direction'] == 'maximize' else _conf.max_size)

    def train_local(self, train_dataset, val_dataset):
        out = {}

        if not isinstance(val_dataset, list):
            val_dataset = [val_dataset]

        loader = self.data_handle.get_loader('train', dataset=train_dataset, drop_last=True, shuffle=True)
        local_iter = self.cache.get('local_iterations', 1)
        tot_iter = len(loader) // local_iter
        for ep in range(1, self.cache['epochs'] + 1):
            for k in self.nn:
                self.nn[k].train()

            _metrics, _avg = self.new_metrics(), self.new_averages()
            ep_avg, ep_metrics, its = self.new_averages(), self.new_metrics(), []

            for i, batch in enumerate(loader, 1):
                its.append(self.training_iteration_local(i, batch))
                if i % local_iter == 0:
                    it = self.reduce_iteration(its)

                    ep_avg.accumulate(it['averages']), ep_metrics.accumulate(it['metrics'])
                    _avg.accumulate(it['averages']), _metrics.accumulate(it['metrics'])

                    _i, its = i // local_iter, []
                    if lazy_debug(_i) or _i == tot_iter:
                        info(f"Ep:{ep}/{self.cache['epochs']},Itr:{_i}/{tot_iter},{_avg.get()},{_metrics.get()}",
                             self.cache.get('verbose'))
                        self.cache[Key.TRAIN_LOG].append([*_avg.get(), *_metrics.get()])
                        _metrics.reset(), _avg.reset()
                    self.on_iteration_end(i=_i, ep=ep, it=it)

            if val_dataset and ep % self.cache.get('validation_epochs', 1) == 0:
                info('--- Validation ---', self.cache.get('verbose'))
                val_averages, val_metric = self.evaluation(mode='validation', dataset_list=val_dataset,
                                                           use_padded_sampler=True)
                self.cache[Key.VALIDATION_LOG].append([*val_averages.get(), *val_metric.get()])
                out.update(**self._save_if_better(ep, val_metric))

                self._on_epoch_end(ep=ep, ep_averages=ep_avg, ep_metrics=ep_metrics,
                                   val_averages=val_averages, val_metrics=val_metric)

                if lazy_debug(ep):
                    self._save_progress(self.cache, epoch=ep)

                if self._stop_early(ep, val_metric, val_averages=val_averages,
                                    epoch_averages=ep_avg, epoch_metrics=ep_metrics):
                    break

        self._save_progress(self.cache, epoch=ep)
        _utils.save_cache(self.cache, self.cache['log_dir'])
        return out

    def iteration(self, batch):
        r"""
        Left for user to implement one mini-bath iteration:
        Example:{
                    inputs = batch['input'].to(self.device['gpu']).float()
                    labels = batch['label'].to(self.device['gpu']).long()
                    out = self.distrib['model'](inputs)
                    loss = F.cross_entropy(out, labels)
                    out = F.softmax(out, 1)
                    _, pred = torch.max(out, 1)
                    sc = self.new_metrics()
                    sc.add(pred, labels)
                    avg = self.new_averages()
                    avg.add(loss.item(), len(inputs))
                    return {'loss': loss, 'averages': avg, 'output': out, 'metrics': sc, 'predictions': pred}
                }
        Note: loss, averages, and metrics are required, whereas others are optional
            -we will have to do backward on loss
            -we need to keep track of loss
            -we need to keep track of metrics
        """
        return {}

    def single_iteration_for_masking(self, model, batch):
        r"""
        Left for user to implement one mini-bath iteration:
        Example:{
                    inputs = batch['input'].to(self.device['gpu']).float()
                    labels = batch['label'].to(self.device['gpu']).long()
                    out = self.distrib['model'](inputs)
                    loss = F.cross_entropy(out, labels)
                    out = F.softmax(out, 1)
                    _, pred = torch.max(out, 1)
                    sc = self.new_metrics()
                    sc.add(pred, labels)
                    avg = self.new_averages()
                    avg.add(loss.item(), len(inputs))
                    return {'loss': loss, 'averages': avg, 'output': out, 'metrics': sc, 'predictions': pred}
                }
        Note: loss, averages, and metrics are required, whereas others are optional
            -we will have to do backward on loss
            -we need to keep track of loss
            -we need to keep track of metrics
        """
        return {}

    def save_predictions(self, dataset, its):
        pass

    def reduce_iteration(self, its):
        reduced = {}.fromkeys(its[0].keys(), None)
        for key in reduced:
            if isinstance(its[0][key], _base_metrics.COINNAverages):
                reduced[key] = self.new_averages()
                [reduced[key].accumulate(ik[key]) for ik in its]

            elif isinstance(its[0][key], _base_metrics.COINNMetrics):
                reduced[key] = self.new_metrics()
                [reduced[key].accumulate(ik[key]) for ik in its]
            else:
                def collect(k=key, src=its):
                    _data = []
                    is_tensor = isinstance(src[0][k], _torch.Tensor)
                    is_tensor = is_tensor and not src[0][k].requires_grad and src[0][k].is_leaf
                    for ik in src:
                        if is_tensor:
                            _data.append(ik[k] if len(ik[k].shape) > 0 else ik[k].unsqueeze(0))
                        else:
                            _data.append(ik[k])
                    if is_tensor:
                        return _torch.cat(_data)
                    return _data

                reduced[key] = collect

        return reduced

    def _save_if_better(self, epoch, val_metrics):
        return {}

    def new_metrics(self):
        return _base_metrics.COINNMetrics()

    def new_averages(self):
        return _base_metrics.COINNAverages(num_averages=1)

    def _on_epoch_end(self, ep, **kw):
        r"""
        Any logic to run after an epoch ends.
        """
        return {}

    def on_iteration_end(self, i, ep, it):
        r"""
        Any logic to run after an iteration ends.
        """
        return {}

    def _save_progress(self, cache, epoch):
        _plot.plot_progress(cache, self.cache['log_dir'], plot_keys=[Key.TRAIN_LOG], epoch=epoch)
        _plot.plot_progress(cache, self.cache['log_dir'], plot_keys=[Key.VALIDATION_LOG],
                            epoch=epoch // self.cache['validation_epochs'])

    def _stop_early(self, epoch, val_metrics=None, **kw):
        return stop_training_(epoch, self.cache)


import sys
import itertools

def solution(mylist):
    number= [x for x in range(1,mylist+1)]
    answer= list(map(list, itertools.permutations(number)))
    answer.sort()
    return answer

n = int(input())
results = solution(n)

for i in results:
    for j in i:
        print(j,end=" ")
    print("")


from concurrent import futures
import grpc
import servicos_pb2
import servicos_pb2_grpc
import threading
import fila
import banco
import time
import os
import config
import random

_ONE_DAY_IN_SECONDS = 60 * 60 * 24
f1 = fila.Fila()
f2 = fila.Fila()
f3 = fila.Fila()
f4 = fila.Fila()
bd = banco.Banco()

class Requisicao(servicos_pb2_grpc.RequisicaoServicer):
    def Create(self,request,context):
        requisicao = "1 " + request.chave + " " + request.valor
        #tratar responsabilidade
        f1.insere(requisicao)

    def Read(self, request, context):
        requisicao = "2 " + request.chave
        f1.insere(requisicao)

    def Update(self, request, context):
        requisicao = "3 " + request.chave + " " + request.valor
        f1.insere(requisicao)

    def Delete(self, request, context):
        requisicao = "4 " + request.chave
        f1.insere(requisicao)
        #return servicos_pb2.Resultado(resposta="OK\n")

class Server(object):
    def __init__(self):
        self.antecessor = None
        self.sucessor = None
        self.cfg = config.Config()
        self.host = self.cfg.getHost().strip("\n")
        self.port = random.randint(int(self.cfg.getMinPort().strip("\n")), int(self.cfg.getMaxPort().strip("\n")))
        print("host = " + self.host +"\nport= " + str(self.port))

    def main(self):
        server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
        servicos_pb2_grpc.add_RequisicaoServicer_to_server(Requisicao(), server)
        server.add_insecure_port(self.host+':'+str(self.port))
        server.start()
        try:
            while True:
                time.sleep(_ONE_DAY_IN_SECONDS)
        except KeyboardInterrupt:
            server.stop(0)

    def duplica_thread(self):
        while True:
            while not f1.vazia():
                try:
                    comando = f1.retira()
                    f2.insere(comando)
                    f3.insere((comando))
                except:
                    pass

    def log_thread(self):
        while True:
            while not f2.vazia():
                pass #

    def banco_thread(self):
        while True:
            while not f3.vazia():
                try:
                    cm = f3.retira()
                    cm = str(cm)
                    cm = cm.split(' ',2)
                    ok = False
                    if int(cm[0]) == 1 :
                        if bd.create(int(cm[1]),cm[2]):
                            ok = True
                            msg = "OK"
                    elif int(cm[0]) == 2 :
                            read = bd.read(int(cm[1]))
                            if read:
                                msg = "Chave:" + str(cm[1]) +" Valor: " + read
                                ok = True
                    elif int(cm[0]) == 3 :
                        if bd.update(int(cm[1]),cm[2]):
                            msg = "OK"
                            ok = True
                    elif int(cm[0]) == 4 :
                        if bd.delete(int(cm[1])):
                            msg = "OK"
                            ok = True
                    if not ok:
                        msg = "NOK"
                except:
                    pass

    def responder(self,msg):
        return servicos_pb2.Resultado(resposta=msg)

    def run(self):
        duplica = threading.Thread(target=self.duplica_thread, name="duplica",args=())
        duplica.setDaemon(True)
        duplica.start()

        log = threading.Thread(target=self.log_thread, name="log",args=())
        log.setDaemon(True)
        log.start()

        banco = threading.Thread(target=self.banco_thread, name="banco",args=())
        banco.setDaemon(True)
        banco.start()

        self.main()

def run_server():
    server = Server()
    server.run()

if __name__ == '__main__':
    run_server()


from AIPUBuilder.Optimizer.framework import *

from AIPUBuilder.Optimizer.logger import OPT_ERROR
from AIPUBuilder.Optimizer.utils.dtype_utils import *
from AIPUBuilder.Optimizer.utils.quant_tool_utils import *

@op_register(OpType.Clip)
def clip(self, *args):
    inp_t = self.inputs[0].betensor
    out_max, out_min = min(self.get_param('clip_max'), OPT_INT_MAX), max(float(self.get_param('clip_min')), OPT_INT_MIN)
    if not self.quantized:
        out_t = torch.clamp(inp_t, out_min, out_max)
    else:
        if "shift" not in self.constants:
            doshift = self.params["shift_value"]
            doscale = self.params["scale_value"]
        else:
            doshift = self.constants["shift"].betensor
            doscale = self.constants["scale"].betensor
        i_zp, o_zp = self.inputs[0].zerop, self.outputs[0].zerop
        out = inp_t + i_zp

        # rescale to 16bits
        # then do the clamp
        relay_bits = 8 if self.outputs[0].qbits <= 8 else 0
        out_t = linear_requantize(
            out, doscale, doshift - relay_bits, 0, out_min, out_max)
        # finally scale to the out.scale
        out_t = linear_requantize(
            out_t, 1, relay_bits, self.outputs[0].zerop, self.outputs[0].qmin, self.outputs[0].qmax)

    self.outputs[0].betensor = out_t
    return out_t

@quant_register(OpType.Clip)
def clip_quantize(self, *args):
    inp = self.inputs[0]
    out = self.outputs[0]
    q_mode_activation = self.attrs['q_mode_activation']
    q_bits_activation = self.attrs['q_bits_activation']
    clip_min = self.get_param('clip_min')
    clip_max = self.get_param('clip_max')
    if clip_min > clip_max:
        OPT_ERROR('The "clip_min" must not greater than "clip_max" in layer_type=%s, layer_id=%s, layer_name=%s' % (
            str(self.type), str(self.attrs['layer_id']), str(self.name)))
    out.min = min(max(out.min, clip_min), clip_max)
    out.max = max(min(out.max, clip_max), clip_min)
    if None != out.min_key_axis:
        out.min_key_axis = torch.min(torch.max(out.min_key_axis, torch.ones_like(
            out.min_key_axis)*clip_min), torch.ones_like(out.min_key_axis)*clip_max)
        out.max_key_axis = torch.max(torch.min(out.max_key_axis, torch.ones_like(
            out.max_key_axis)*clip_max), torch.ones_like(out.min_key_axis)*clip_min)

    out_signed = False if clip_min >= 0 else True
    out_signed = out_signed or self.force_dtype_int
    out.qinvariant = inp.qinvariant
    if inp.qinvariant:
        out.scale = 1.
        out.zerop = 0
        out.qbits = inp.qbits
        out.dtype = inp.dtype
    else:
        out.qbits = q_bits_activation
        out.scale, out.zerop, out.qmin, out.qmax, out.dtype = get_linear_quant_params_from_tensor(
            out, q_mode_activation, q_bits_activation, is_signed=out_signed)
    local_rescale = out.scale / inp.scale
    do_scale, do_scale_type, do_shift, do_shift_type = get_scale_approximation_params(
        local_rescale, mult_bits=out.qbits, force_shift_positive=self.force_shift_positive)
    self.params['scale_value'] = int(do_scale)
    self.params['shift_value'] = int(do_shift)
    self.params['scale_type'] = do_scale_type
    self.params['shift_type'] = do_shift_type

    cscale = out.scale
    czerop = out.zerop
    if out.qbits <= 8:
        cscale = out.scale * (2**8)
        czerop = 0
    t_qmin, t_qmax = bits2range(32, is_signed=True)
    cmax = linear_quantize_clip(
        clip_max, cscale, czerop, t_qmin, t_qmax).item()
    cmin = linear_quantize_clip(
        clip_min, cscale, czerop, t_qmin, t_qmax).item()
    self.params['clip_max'] = int(max(t_qmin, min(t_qmax, cmax)))
    self.params['clip_min'] = int(max(t_qmin, min(t_qmax, cmin)))

def clip_out_signed(self):
    clip_min = self.get_param('clip_min')
    return False if clip_min >= 0 else True


import requests
from bs4 import BeautifulSoup as bs

page = requests.get('https://umggaming.com/leaderboards')
soup = bs(page.text, 'html.parser')

# Get table with the id of leaderboards
leaderboards = soup.find('table', {'id': 'leaderboard-table'})

# Get tbody from table
tbody = leaderboards.find('tbody')

# Get all the tr elements in tbody
for tr in tbody.find_all('tr'):
    # get td text with striped of their white space
    place = tr.find_all('td')[0].text.strip()
    username = tr.find_all('td')[1].text.strip()
    xp = tr.find_all('td')[3].text.strip()
    print(place, username, xp)


'''

Problem 5 | Longest Palindromic Substring
https://leetcode.com/problems/longest-palindromic-substring/

'''

class Solution:
    def longestPalindrome(self, s: str) -> str:
        l = len(s)
        dp = [[0 for i in range(l)] for j in range(l)]

        for i in range(l):
            dp[i][i] = True
            output = s[i]

        for start in range(l - 1, -1, -1):
            for end in range(start + 1, l):
                if s[start] == s[end]:
                    if end - start == 1 or dp[start + 1][end - 1] == True:
                        dp[start][end] = True
                        if end - start + 1 > max_len:
                            max_len = end - start + 1
                            output = s[start:end + 1]
        return output

Algorithms/Dynamic Programming/Longest Palindromic Substring.py


import numpy as np
import pandas as pd

data_in_dict = {
    "year" : [
        1950, 1951, 1952,
        1953, 1954, 1955,
        1956, 1957, 1958, 1959
    ],
    "champ" : [
        "Farina", "Fangio", "Ascari", "Ascari",
        "Fangio", "Fangio", "Fangio", "Fangio",
        "Hawthorne", "Brabham"
    ],
    "wins" : [
        3, 3, 6, 5,
        6, 4, 3, 4, 1, 2
    ],
    "points" : [
        30, 31, 36, 34,
        42, 40, 30, 40, 42, 43
    ]
}

data_in_dict["gender"] = ["m"] * 10
# print("printing first as a Python dictionary: \n", data_in_dict)

formula_one = pd.DataFrame(data_in_dict, columns = ["year", "champ", "wins", "points", "gender"])

team_wins = ["Alfa"] * 2 + ["Ferrari"] * 2 + ["Mercedes"
] * 2 + ["Ferrari", "Maserati", "Ferrari", "Cooper"]

formula_one["team"] = team_wins

print(formula_one)

# unfortunately, for long debated reasons, formula one remains still a male dominated sport). We can safely remove this monotone column gender
del(formula_one["gender"])

print("\n", formula_one)
print("\nthe size of the table is (rows * columns):")
print(formula_one.shape)
print("the rows are organized as:")
print(formula_one.columns)
print("the Python type of the values on the columns are:")
print(formula_one.dtypes)

path='week_3\\A\\'
formula_one.to_csv(path+'f1_fifties.csv')
print('\ndata frame written to csv file')


'''
INPUT:
    WRF-Chem mechanism file:
        *.eqn

    Required species

OUTPUT:
    Varnames in IRR*.nc file

UPDATE:
    Xin Zhang:
       05/25/2020: Basic
'''

import re
import pandas as pd

def read_eqn(dir, eqn_file, species):
    '''
    Read the equations from the mechanism file
    '''

    # create the empty DataFrame
    col_names = ['reactants', 'products']
    equations = pd.DataFrame(columns=col_names)

    with open(dir+eqn_file, 'r') as f:
        for line in f:
            # the species is in the right side of equation
            if species in line.split('=')[-1]:
                reaction = re.split('} | :', line)[1]
                # delete space and tab
                reaction = re.sub(r'\s+', '', reaction)
                # get the content after "="
                right = reaction.split('=')[1]
                # get the index of species
                species_idx = right.index(species)
                # check whether there's number before the species
                if species_idx != 0 and right[species_idx-1] != '+':
                    # if the character two digits before the species is letter
                    # then it is actually another species
                    # which just contains the substring of species
                    if species_idx-1 != 0 and right[species_idx-2].isalpha():
                            continue

                # only the reactants are right ...
                # I don't decide to fix it now,
                # as I only need the reactants to generate the IRR varname
                equations = equations.append({'reactants': reaction.split('=')[0],
                                              'products': reaction.split('=')[1]
                                              },
                                             ignore_index=True
                                             )

    return equations

def convert_names(row):
    '''
    Convert reactants to varnames in IRR files
    '''

    return row['reactants'].upper().replace('+', '_').replace('{_M}', '') + '_IRR'

def irr_varnames(equations):
    '''
    Principle of varname of IRR:
       Omit {+M}, use "_" instead of "+" and "_IRR" as suffix
    '''

    equations['irr_varname'] = equations.apply(lambda row: convert_names(row), axis=1)
    return equations

def main():
    # set dir and equation file
    dir = './data/wrfchem/mechanisms/'
    eqn_file = 'mozcart.eqn'
    species = 'NO2'

    # read equations
    equations = read_eqn(dir, eqn_file, species)
    # get varnames in IRR files
    irr_varnames(equations)['irr_varname']

if __name__ == '__main__':
    main()


from tornado import ioloop, options, web

options.define("port", default=8080, help="port to listen on")
options.define(
    "host", default="127.0.0.1", help="host interface to connect on (0.0.0.0 is all)"
)
options.define("path", help="the files to serve")

SETTINGS = dict(
    # enabling compression can have security impacts if not done correctly
    compress_response=True,
    # not really useful for production
    autoreload=True,
)

def make_settings(path):
    settings = dict(SETTINGS)
    if not path:
        raise RuntimeError("path is required")
    settings["static_path"] = path
    return settings

def make_app(settings):
    """create and return (but do not start) a tornado app"""
    app = web.Application(
        [
            (
                r"^/(.*)",
                web.StaticFileHandler,
                dict(path=settings["static_path"], default_filename="index.html"),
            )
        ],
        **settings
    )

    return app

def main(path, port, host):
    """start a tornado app on the desired port"""
    settings = make_settings(path)
    app = make_app(settings)
    app.listen(port, host)
    url = "http://{}:{}/".format(host, port)
    print("Watching files: \t\t{static_path}".format(**settings))
    print("Hosting site on:\t\t{}".format(url))
    print("\nPress `Ctrl+C` to stop")
    try:
        ioloop.IOLoop.current().start()
    except KeyboardInterrupt:
        ioloop.IOLoop.current().stop()
        print("The server was stopped")

if __name__ == "__main__":
    options.parse_command_line()
    main(
        path=options.options.path, port=options.options.port, host=options.options.host
    )


# In this Bite you calculate the total amount of points earned with Ninja Belts
# by accessing the given ninja_belts dict.
#
# You learn how to access score and ninjas (= amount of belt owners) from
# no less than a namedtuple (if you're new to them, check out the basic Point example in the docs).
#
# Why a namedtuple, you did not even mention a tuple yet?!
#
# Good point, well in our Bites we might actually use them even more so let's
# get to know them here (if you have a free evening read up on the collections module as well and thank us later).
#
# The function returns the total score int. You learn to write generic
# code because we test for an updated ninja_belts dict as well, see the TESTS tab.

from collections import namedtuple

BeltStats = namedtuple('BeltStats', 'score ninjas')

ninja_belts = {'yellow': BeltStats(50, 11),
               'orange': BeltStats(100, 7),
               'green': BeltStats(175, 1),
               'blue': BeltStats(250, 5)}

def get_total_points(belts=ninja_belts):
    total_result = 0
    for key, value in belts.items():
        total_result += (value[0] * value[1])
    return total_result

# tests for this exercise in tests.py


"""
Homework4.
Replace 'pass' by your implementation.
"""
import numpy as np
import helper
# Insert your package here
from sympy import *
from scipy.ndimage.filters import gaussian_filter
from scipy.optimize import leastsq, minimize

'''
Q2.1: Eight Point Algorithm
    Input:  pts1, Nx2 Matrix
            pts2, Nx2 Matrix
            M, a scalar parameter computed as max (imwidth, imheight)
    Output: F, the fundamental matrix
'''

def eightpoint(pts1, pts2, M):
	# Replace pass by your implementation
	T = np.eye(3) / M
	T[2, 2] = 1;
	pts1 = pts1.astype('float')/M
	pts2 = pts2.astype('float')/M

	A = np.vstack([
		 pts1[:, 0]*pts2[:, 0],pts1[:, 0]*pts2[:, 1], pts1[:, 0],
		 pts1[:, 1]*pts2[:, 0],pts1[:, 1]*pts2[:, 1], pts1[:, 1],
		 pts2[:, 0],pts2[:, 1], np.ones(pts1.shape[0])
		]).T

	[U, S, V] = np.linalg.svd(A)
	F = np.reshape(V[-1,:], (3,3))

	F = helper.refineF(F, pts1, pts2)
	F = T.T @ F @ T

	return F

'''
Q2.2: Seven Point Algorithm
    Input:  pts1, Nx2 Matrix
            pts2, Nx2 Matrix
            M, a scalar parameter computed as max (imwidth, imheight)
    Output: Farray, a list of estimated fundamental matrix.
'''
def sevenpoint(pts1, pts2, M):

	T = np.eye(3) / M
	T[2, 2] = 1;
	pts1 = pts1.astype('float')/M
	pts2 = pts2.astype('float')/M

	Fs = []
	A = np.vstack([
		 pts1[:, 0]*pts2[:, 0],pts1[:, 0]*pts2[:, 1], pts1[:, 0],
		 pts1[:, 1]*pts2[:, 0],pts1[:, 1]*pts2[:, 1], pts1[:, 1],
		 pts2[:, 0],pts2[:, 1], np.ones(pts1.shape[0])
		]).T

	[U, S, V] = np.linalg.svd(A)

	F1 = np.reshape(V[-1,:], (3,3))
	F2 = np.reshape(V[-2,:], (3,3))

	alpha = Symbol('alpha')

	eqn   = Matrix(F1 + alpha*F2).det()

	solns = roots(eqn)

	for i, sol in enumerate(solns):

		if re(sol)==sol:
			sol = float(sol)
			F = F1 + sol*F2
			F = helper.refineF(F, pts1, pts2)

			Fs.append(T.T @ F @ T)

	return Fs

'''
Q3.1: Compute the essential matrix E.
    Input:  F, fundamental matrix
            K1, internal camera calibration matrix of camera 1
            K2, internal camera calibration matrix of camera 2
    Output: E, the essential matrix
'''
def essentialMatrix(F, K1, K2):
	return K2.T @ F @ K1

'''
Q3.2: Triangulate a set of 2D coordinates in the image to a set of 3D points.
    Input:  C1, the 3x4 camera matrix
            pts1, the Nx2 matrix with the 2D image coordinates per row
            C2, the 3x4 camera matrix
            pts2, the Nx2 matrix with the 2D image coordinates per row
    Output: P, the Nx3 matrix with the corresponding 3D points per row
            err, the reprojection error.
'''
def triangulate(C1, pts1, C2, pts2):

	P = []

	error = 0

	for pt1, pt2 in zip(pts1, pts2):
		x1, y1, x2, y2 = pt1[0], pt1[1], pt2[0], pt2[1]

		A = np.vstack([(x1*C1[2, :]-C1[0, :]),
			 (y1*C1[2, :]-C1[1, :]),
			 (x2*C2[2, :]-C2[0, :]),
			 (y2*C2[2, :]-C2[1, :])])

		[U, S, V] = np.linalg.svd(A)
		w = V[-1,:]/V[-1,-1]

		p1_reproj = C1 @ w
		p2_reproj = C2 @ w

		p1_reproj = p1_reproj/p1_reproj[-1]
		p2_reproj = p2_reproj/p2_reproj[-1]
		error += (np.linalg.norm(p1_reproj[:2]- pt1)**2 + np.linalg.norm(p2_reproj[:2]- pt2)**2)
		P.append(w[:3])

	P = np.vstack(P)

	return P, error
'''
Q4.1: 3D visualization of the temple images.
    Input:  im1, the first image
            im2, the second image
            F, the fundamental matrix
            x1, x-coordinates of a pixel on im1
            y1, y-coordinates of a pixel on im1
    Output: x2, x-coordinates of the pixel on im2
            y2, y-coordinates of the pixel on im2

'''

def epipolarCorrespondence(im1, im2, F, x1, y1):

	sy, sx, _ = im2.shape
	v = np.array([x1, y1, 1])
	l = F.dot(v)
	s = np.sqrt(l[0]**2+l[1]**2)

	if s==0:
		error('Zero line vector in displayEpipolar')
	l = l/s

	if l[0] != 0:
		ye = sy-1;
		ys = 0

		Y = np.arange(np.ceil(min(ys,ye)), np.ceil(max(ys,ye)))
		X = np.round(-(l[1]*Y + l[2])/l[0])

	else:
		xe = sx-1
		xs = 0

		X = np.arange(np.ceil(min(xs,xe)), np.ceil(max(xs,xe)))
		Y = np.round(-(l[0]*X + l[2])/l[1])

	X = np.round(X).astype('int')
	Y = np.round(Y).astype('int')

	S = 15
	patch1 = getPatch(im1, y1, x1, S)
	x2, y2 = None, None
	min_dist = np.inf
	g_kernel = gaussian(S, 15)
	g_kernel_3d = np.repeat(g_kernel[:, :, np.newaxis], 3, axis=2)

	for i, j in zip(X, Y):
		patch2 = getPatch(im2, j, i, S)
		if patch2 is None:continue
		dist = np.linalg.norm(g_kernel_3d*(patch1-patch2))
		if dist <  min_dist :
			min_dist = dist
			[x2, y2] = i, j

	return x2, y2

def getPatch(im, x, y, S):
	h, w = im.shape[:2]

	if ((x + S//2+1)<h and (x - S//2)>0 and (y + S//2+1)<w and (y - S//2)>0 ):
		return im[x-S//2:x+S//2+1, y-S//2:y+S//2+1, :]
	else:#if not valid patch
		return None

def gaussian(size, sigma):
	X = np.linspace(-size//2 + 1, size//2 + 1, size)
	Y = np.linspace(-size//2 + 1, size//2 + 1, size)
	XX, YY = np.meshgrid(X, Y)
	g = np.exp(-((XX**2 + YY**2)/(2.0*sigma**2)))
	return g/np.sum(g)

'''
Q5.1: RANSAC method.
    Input:  pts1, Nx2 Matrix
            pts2, Nx2 Matrix
            M, a scaler parameter
    Output: F, the fundamental matrix
'''
def ransacF(pts1, pts2, M):

	p = pts1.shape[0]
	bestF = None
	bestF_inlier_count = 0
	bestF_inliers = None
	tol = 0.0022
	num_iter = 100
	pts1_h = np.hstack((pts1, np.ones((p, 1))))
	pts2_h = np.hstack((pts2, np.ones((p, 1))))

	for iter in range(num_iter):
		randPtsIdx = np.random.choice(p, 7, replace=False)
		randLoc1, randLoc2 = pts1_h[randPtsIdx, :], pts2_h[randPtsIdx, :]
		Fs = sevenpoint(randLoc1[:, :2], randLoc2[:, :2], M)

		for F in Fs:
			dst = np.diag(pts2_h @ F @ pts1_h.T)
			inliers =  np.abs(dst) < tol
			inliers_count = np.sum(inliers)

			if inliers_count >  bestF_inlier_count:
				bestF_inlier_count = inliers_count
				bestF_inliers = inliers
				bestF = F
		print('#'*100)
		print('iter: ', iter, ' inliers: ', int(100*inliers_count/p),'/', int(100*bestF_inlier_count/p))
		print('#'*100)
		if (bestF_inlier_count/p) >=.75:
			break

	bestFs = sevenpoint(pts1[bestF_inliers, :2], pts2[bestF_inliers, :2], M)

	for F in bestFs:
		dst = np.diag(pts2_h @ F @ pts1_h.T)
		inliers =  np.abs(dst) < tol
		inliers_count = np.sum(inliers)
		bestF_inlier_count = 0

		if inliers_count >  bestF_inlier_count:
			#print('new max: ', inliers_count)
			bestF_inlier_count = inliers_count
			bestF = F
			bestF_inliers = inliers

	bestF = helper.refineF(bestF, pts1[bestF_inliers, :2], pts2[bestF_inliers, :2])

	return bestF, np.where(bestF_inliers)

'''
Q5.2: Rodrigues formula.
    Input:  r, a 3x1 vector
    Output: R, a rotation matrix
'''
def rodrigues(r):
	# Replace pass by your implementation
	r = r.reshape(3, 1)
	#return cv2.Rodrigues(r.astype('float'))[0]
	th  = np.linalg.norm(r)

	if th == 0:
		return np.eye(3)

	u = r/th

	a1, a2, a3 = u[0], u[1], u[2]

	u_x = np.array([[0,  -a3, a2],
				   [a3,  0, -a1],
				   [-a2, a1, 0]])

	#R = np.eye(3) + np.sin(th)*rx + (1 - np.cos(th) )*(r_n @ r_n.T - np.eye(3))
	R = np.eye(3)*np.cos(th) + (1 - np.cos(th))*(u @ u.T) + np.sin(th)*u_x

	return R

'''
Q5.2: Inverse Rodrigues formula.
    Input:  R, a rotation matrix
    Output: r, a 3x1 vector
'''
def invRodrigues(R):

	th = np.arccos((np.trace(R) - 1)/2)
	if th == 0 or np.isnan(th):
		return np.zeros(3).reshape(-1, 1)
	else:
		A = (R - R.T)/2
		a32, a13, a21 = A[2,1], A[0,2], A[1,0]

		rho = np.array([a32, a13, a21]).T
		s  = np.linalg.norm(rho)
		c  = (np.trace(R) - 1)/2
		u  = rho/s
		th = np.arctan2(s, c)
		r  = u*th

	return r

'''
Q5.3: Rodrigues residual.
    Input:  K1, the intrinsics of camera 1
            M1, the extrinsics of camera 1
            p1, the 2D coordinates of points in image 1
            K2, the intrinsics of camera 2
            p2, the 2D coordinates of points in image 2
            x, the flattened concatenationg of P, r2, and t2.
    Output: residuals, the difference between original and estimated projections
'''
def rodriguesResidual(K1, M1, p1, K2, p2, x):
	N = p1.shape[0]

	P  = x[:-6].reshape(N, 3)
	r2 = x[-6:-3].reshape(3, 1)
	t2 = x[-3:].reshape(3, 1)
	R2 = rodrigues(r2)

	M2 = np.concatenate([R2, t2], axis=1)

	P_h = np.hstack((P, np.ones((N,1))))

	p1_hat    = (K1 @ M1) @ P_h.T
	p2_hat    = (K2 @ M2) @ P_h.T
	p1_hat    = (p1_hat/p1_hat[2, :])[:2, :].T
	p2_hat    = (p2_hat/p2_hat[2, :])[:2, :].T

	residuals = np.concatenate([(p1-p1_hat).reshape([-1]),
								(p2-p2_hat).reshape([-1])])

	return residuals

'''
Q5.3 Bundle adjustment.
    Input:  K1, the intrinsics of camera 1
            M1, the extrinsics of camera 1
            p1, the 2D coordinates of points in image 1
            K2,  the intrinsics of camera 2
            M2_init, the initial extrinsics of camera 1
            p2, the 2D coordinates of points in image 2
            P_init, the initial 3D coordinates of points
    Output: M2, the optimized extrinsics of camera 1
            P2, the optimized 3D coordinates of points
'''

def bundleAdjustment(K1, M1, p1, K2, M2_init, p2, P_init):
	N = p1.shape[0]

	R2 = M2_init[:, :3]
	t2 = M2_init[:, 3]
	r2 = invRodrigues(R2)

	x_init = np.concatenate([P_init.reshape([-1]), r2, t2])
	rod_func  = lambda x: (rodriguesResidual(K1,M1,p1,K2,p2,x)**2).sum()
	#rod_func  = lambda x: rodriguesResidual(K1, M1, p1, K2, p2, x)
	#x_star, flag = leastsq(rod_func, x_init)
	res = minimize(rod_func, x_init)
	x_star = res.x
	P_star  = x_star[:-6].reshape(N, 3)
	r2_star = x_star[-6:-3]
	R2_star = rodrigues(r2_star)
	t2_star = x_star[-3:].reshape(-1, 1)
	M2 = np.hstack((R2_star, t2_star))

	return M2, P_star


"""Team and Tessera models

Revision ID: 20c506662a93
Revises: 089ccde49d20
Create Date: 2021-10-06 13:50:48.166391

"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = "20c506662a93"
down_revision = "089ccde49d20"
branch_labels = None
depends_on = None

def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "team",
        sa.Column("id", sa.CHAR(length=36), nullable=False),
        sa.Column("name", sa.String(), nullable=False),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(op.f("ix_team_name"), "team", ["name"], unique=True)
    op.create_table(
        "tessera",
        sa.Column("id", sa.CHAR(length=36), nullable=False),
        sa.Column("name", sa.String(), nullable=False),
        sa.Column("asana_id", sa.String(), nullable=False),
        sa.Column("github_handle", sa.String(), nullable=False),
        sa.Column("slack_id", sa.String(), nullable=False),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        op.f("ix_tessera_asana_id"), "tessera", ["asana_id"], unique=False
    )
    op.create_index(
        op.f("ix_tessera_github_handle"),
        "tessera",
        ["github_handle"],
        unique=False,
    )
    op.create_index(op.f("ix_tessera_name"), "tessera", ["name"], unique=False)
    op.create_index(
        op.f("ix_tessera_slack_id"), "tessera", ["slack_id"], unique=False
    )
    op.create_table(
        "team_project_association",
        sa.Column("team_id", sa.CHAR(length=36), nullable=True),
        sa.Column("project_id", sa.CHAR(length=36), nullable=True),
        sa.ForeignKeyConstraint(
            ["project_id"],
            ["asana_project.id"],
            name="team_project_project_fk",
        ),
        sa.ForeignKeyConstraint(
            ["team_id"], ["team.id"], name="team_project_team_fk"
        ),
    )
    op.create_table(
        "team_tessera_association",
        sa.Column("team_id", sa.CHAR(length=36), nullable=True),
        sa.Column("tessera_id", sa.CHAR(length=36), nullable=True),
        sa.ForeignKeyConstraint(
            ["team_id"], ["team.id"], name="team_tessera_team_fk"
        ),
        sa.ForeignKeyConstraint(
            ["tessera_id"], ["tessera.id"], name="team_tessera_tessera_fk"
        ),
    )
    # ### end Alembic commands ###

def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table("team_tessera_association")
    op.drop_table("team_project_association")
    op.drop_index(op.f("ix_tessera_slack_id"), table_name="tessera")
    op.drop_index(op.f("ix_tessera_name"), table_name="tessera")
    op.drop_index(op.f("ix_tessera_github_handle"), table_name="tessera")
    op.drop_index(op.f("ix_tessera_asana_id"), table_name="tessera")
    op.drop_table("tessera")
    op.drop_index(op.f("ix_team_name"), table_name="team")
    op.drop_table("team")
    # ### end Alembic commands ###


# Hidden Markov Models
#
# Author: Ron Weiss <ronweiss@gmail.com>
#         Shiqiao Du <lucidfrontier.45@gmail.com>
# API changes: Jaques Grobler <jaquesgrobler@gmail.com>
# Modifications to create of the HMMLearn module: Gael Varoquaux
# More API changes: Sergei Lebedev <superbobry@gmail.com>
# FSHMM code: Elizabeth Fons <elifons@gmail.com>
#			  Alejandro Sztrajman <asztrajman@gmail.com>
# FSHMM algorithm from:
# Adams, Stephen & Beling, Peter & Cogill, Randy. (2016).
# Feature Selection for Hidden Markov Models and Hidden Semi-Markov Models.
"""
The :mod:`hmmlearn.hmm` module implements hidden Markov models.
"""

import numpy as np
import pandas as pd
from sklearn import cluster
from sklearn.mixture import (
    GMM, sample_gaussian,
    log_multivariate_normal_density,
    distribute_covar_matrix_to_match_covariance_type, _validate_covars)

from scipy.misc import logsumexp
from sklearn.base import BaseEstimator, _pprint
from sklearn.utils import check_array, check_random_state
from sklearn.utils.validation import check_is_fitted

from base import _BaseHMM
from hmmlearn.utils import normalize, iter_from_X_lengths, normalize

from base import ConvergenceMonitor

__all__ = ["GMMHMM", "GaussianHMM", "MultinomialHMM", "GaussianFSHHM"]

COVARIANCE_TYPES = frozenset(("spherical", "diag", "full", "tied"))

class GaussianHMM(_BaseHMM):
    """Hidden Markov Model with Gaussian emissions.

    Parameters
    ----------
    n_components : int
        Number of states.

    covariance_type : string
        String describing the type of covariance parameters to
        use.  Must be one of

        * "spherical" --- each state uses a single variance value that
          applies to all features;
        * "diag" --- each state uses a diagonal covariance matrix;
        * "full" --- each state uses a full (i.e. unrestricted)
          covariance matrix;
        * "tied" --- all states use **the same** full covariance matrix.

        Defaults to "diag".

    min_covar : float
        Floor on the diagonal of the covariance matrix to prevent
        overfitting. Defaults to 1e-3.

    startprob_prior : array, shape (n_components, )
        Initial state occupation prior distribution.

    transmat_prior : array, shape (n_components, n_components)
        Matrix of prior transition probabilities between states.

    algorithm : string
        Decoder algorithm. Must be one of "viterbi" or "map".
        Defaults to "viterbi".

    random_state: RandomState or an int seed
        A random number generator instance.

    n_iter : int, optional
        Maximum number of iterations to perform.

    tol : float, optional
        Convergence threshold. EM will stop if the gain in log-likelihood
        is below this value.

    verbose : bool, optional
        When ``True`` per-iteration convergence reports are printed
        to :data:`sys.stderr`. You can diagnose convergence via the
        :attr:`monitor_` attribute.

    params : string, optional
        Controls which parameters are updated in the training
        process.  Can contain any combination of 's' for startprob,
        't' for transmat, 'm' for means and 'c' for covars. Defaults
        to all parameters.

    init_params : string, optional
        Controls which parameters are initialized prior to
        training.  Can contain any combination of 's' for
        startprob, 't' for transmat, 'm' for means and 'c' for covars.
        Defaults to all parameters.

    Attributes
    ----------
    n_features : int
        Dimensionality of the Gaussian emissions.

    monitor\_ : ConvergenceMonitor
        Monitor object used to check the convergence of EM.

    transmat\_ : array, shape (n_components, n_components)
        Matrix of transition probabilities between states.

    startprob\_ : array, shape (n_components, )
        Initial state occupation distribution.

    means\_ : array, shape (n_components, n_features)
        Mean parameters for each state.

    covars\_ : array
        Covariance parameters for each state.

        The shape depends on ``covariance_type``::

            (n_components, )                        if 'spherical',
            (n_features, n_features)                if 'tied',
            (n_components, n_features)              if 'diag',
            (n_components, n_features, n_features)  if 'full'

    Examples
    --------
    >>> from hmmlearn.hmm import GaussianHMM
    >>> GaussianHMM(n_components=2)
    ...                             #doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
    GaussianHMM(algorithm='viterbi',...
    """
    def __init__(self, n_components=1, covariance_type='diag',
                 min_covar=1e-3,
                 startprob_prior=1.0, transmat_prior=1.0,
                 means_prior=0, means_weight=0,
                 covars_prior=1e-2, covars_weight=1,
                 algorithm="viterbi", random_state=None,
                 n_iter=400, tol=1e-9, verbose=False,

                 params="stmc", init_params="stmc"):
        _BaseHMM.__init__(self, n_components,
                          startprob_prior=startprob_prior,
                          transmat_prior=transmat_prior, algorithm=algorithm,
                          random_state=random_state, n_iter=n_iter,
                          tol=tol, params=params, verbose=verbose,
                          init_params=init_params)

        self.covariance_type = covariance_type
        self.min_covar = min_covar
        self.means_prior = means_prior
        self.means_weight = means_weight
        self.covars_prior = covars_prior
        self.covars_weight = covars_weight

    def _get_covars(self):
        """Return covars as a full matrix."""
        if self.covariance_type == 'full':
            return self._covars_
        elif self.covariance_type == 'diag':
            return np.array([np.diag(cov) for cov in self._covars_])
        elif self.covariance_type == 'tied':
            return np.array([self._covars_] * self.n_components)
        elif self.covariance_type == 'spherical':
            return np.array(
                [np.eye(self.n_features) * cov for cov in self._covars_])

    def _set_covars(self, covars):
        self._covars_ = np.asarray(covars).copy()
        covars_ = property(_get_covars, _set_covars)

    def _check(self):
        super(GaussianHMM, self)._check()

        self.means_ = np.asarray(self.means_)
        self.n_features = self.means_.shape[1]
        if self.covariance_type not in COVARIANCE_TYPES:
            raise ValueError('covariance_type must be one of {0}'
                             .format(COVARIANCE_TYPES))
        _validate_covars(self._covars_, self.covariance_type,
                         self.n_components)

    def _init(self, X, lengths=None):
        super(GaussianHMM, self)._init(X, lengths=lengths)

        _, n_features = X.shape
        if hasattr(self, 'n_features') and self.n_features != n_features:
            raise ValueError('Unexpected number of dimensions, got %s but '
                             'expected %s' % (n_features, self.n_features))

        self.n_features = n_features
        if 'm' in self.init_params or not hasattr(self, "means_"): #paper: mu initialized randomly
            kmeans = cluster.KMeans(n_clusters=self.n_components,
                                    random_state=self.random_state)
            kmeans.fit(X)
            self.means_ = kmeans.cluster_centers_
        if 'c' in self.init_params or not hasattr(self, "covars_"): #paper: sigma initialized with 4
            cv = np.cov(X.T) + self.min_covar * np.eye(X.shape[1])
            if not cv.shape:
                cv.shape = (1, 1)
            self._covars_ = distribute_covar_matrix_to_match_covariance_type(
                cv, self.covariance_type, self.n_components).copy()
            self._covars_ = np.ones(self._covars_.shape)*4.0 #villavilla

    def _compute_log_likelihood(self, X):
        return log_multivariate_normal_density(
            X, self.means_, self._covars_, self.covariance_type)

    def _generate_sample_from_state(self, state, random_state=None):
        if self.covariance_type == 'tied':
            cv = self._covars_
        else:
            cv = self._covars_[state]
        return sample_gaussian(self.means_[state], cv, self.covariance_type,
                               random_state=random_state)

    def _initialize_sufficient_statistics(self):
        stats = super(GaussianHMM, self)._initialize_sufficient_statistics()
        stats['post'] = np.zeros(self.n_components)
        stats['obs'] = np.zeros((self.n_components, self.n_features))
        stats['obs**2'] = np.zeros((self.n_components, self.n_features))
        if self.covariance_type in ('tied', 'full'):
            stats['obs*obs.T'] = np.zeros((self.n_components, self.n_features,
                                           self.n_features))
        # print(stats)
        return stats

    def _accumulate_sufficient_statistics(self, stats, obs, framelogprob, posteriors, fwdlattice, bwdlattice):
        super(GaussianHMM, self)._accumulate_sufficient_statistics(
            stats, obs, framelogprob, posteriors, fwdlattice, bwdlattice)

        if 'm' in self.params or 'c' in self.params:
            stats['post'] += posteriors.sum(axis=0)
            stats['obs'] += np.dot(posteriors.T, obs)

        if 'c' in self.params:
            if self.covariance_type in ('spherical', 'diag'):
                stats['obs**2'] += np.dot(posteriors.T, obs ** 2)
            elif self.covariance_type in ('tied', 'full'):
                # posteriors: (nt, nc); obs: (nt, nf); obs: (nt, nf)
                # -> (nc, nf, nf)
                stats['obs*obs.T'] += np.einsum(
                    'ij,ik,il->jkl', posteriors, obs, obs)

    def _do_mstep(self, stats):
        super(GaussianHMM, self)._do_mstep(stats)

        means_prior = self.means_prior
        means_weight = self.means_weight

        # TODO: find a proper reference for estimates for different
        #       covariance models.
        # Based on Huang, Acero, Hon, "Spoken Language Processing",
        # p. 443 - 445
        denom = stats['post'][:, np.newaxis]
        if 'm' in self.params:
            self.means_ = ((means_weight * means_prior + stats['obs'])
                           / (means_weight + denom))

        if 'c' in self.params:
            covars_prior = self.covars_prior
            covars_weight = self.covars_weight
            meandiff = self.means_ - means_prior

            if self.covariance_type in ('spherical', 'diag'):
                cv_num = (means_weight * meandiff**2
                          + stats['obs**2']
                          - 2 * self.means_ * stats['obs']
                          + self.means_**2 * denom)
                cv_den = max(covars_weight - 1, 0) + denom
                self._covars_ = \
                    (covars_prior + cv_num) / np.maximum(cv_den, 1e-5)
                if self.covariance_type == 'spherical':
                    self._covars_ = np.tile(
                        self._covars_.mean(1)[:, np.newaxis],
                        (1, self._covars_.shape[1]))
            elif self.covariance_type in ('tied', 'full'):
                cv_num = np.empty((self.n_components, self.n_features,
                                  self.n_features))
                for c in range(self.n_components):
                    obsmean = np.outer(stats['obs'][c], self.means_[c])

                    cv_num[c] = (means_weight * np.outer(meandiff[c],
                                                         meandiff[c])
                                 + stats['obs*obs.T'][c]
                                 - obsmean - obsmean.T
                                 + np.outer(self.means_[c], self.means_[c])
                                 * stats['post'][c])
                cvweight = max(covars_weight - self.n_features, 0)
                if self.covariance_type == 'tied':
                    self._covars_ = ((covars_prior + cv_num.sum(axis=0)) /
                                     (cvweight + stats['post'].sum()))
                elif self.covariance_type == 'full':
                    self._covars_ = ((covars_prior + cv_num) /
                                     (cvweight + stats['post'][:, None, None]))

#init values
#self.startprob_ -> pi_i (i:estado)
#self.transmat_ -> a_ij (i,j:estado)
#self.means_ -> mu_il (i:estado, l:feature/serie)
#self._covars_ -> sigma2_il (i:estado, l:feature/serie)
#self.rho_ -> rho_l (l:feature/serie)
#self.epsilon_ -> epsilon_l (l:feature/serie)
#self.tau_ -> tau_l (l:feature/serie)

#self.n_features = X.shape[1], con dimension L
#self.n_components -> number of states

#FIXME: revisar que cuando le asigné tau o std() a una variable esté bien eso, y que no tendría que haberle asignado _covars_ o algo distinto.
#FIXME: revisar que las cuentas finales con np.dot(np.sum()) y np.sum(np.sum()) hagan lo que tienen que hacer.

def gaussiana(x, mu, sigma2):
    return (1.0/np.sqrt(2*np.pi*sigma2))*np.exp(-0.5*((x - mu)**2)/sigma2)

class GaussianFSHMM(GaussianHMM):
    def __init__(self, k, **kwargs):
        super(GaussianFSHMM, self).__init__(**kwargs)
        self.k_factor_ = k

    def init_values_FS(self, X, epsilon=None, tau=None, rho=None): #default initialization is for the first example of the paper
        self.rho_ = np.ones(self.n_features)*0.5 if (rho is None) else rho

        self.epsilon_ = pd.DataFrame(X).mean().values if (epsilon is None) else epsilon

        self.tau_ = pd.DataFrame(X).std().values if (tau is None) else tau

    def select_hyperparams(self, X): #pass hyperparameters as arguments of this function
        self.p_ = np.ones(self.n_components)*2
        self.a_ = np.ones((self.n_components, self.n_components))*2

        self.b_ = pd.DataFrame(X).mean().values # self.b_ = X.mean().values

        self.m_ = self.means_.copy()
        for l in range(self.n_features):
            self.m_[0, l] = self.b_[l] - self.tau_[l]
            self.m_[1, l] = self.b_[l] + self.tau_[l]

        self.s_ = np.ones(self.m_.shape)*0.5
        self.zeta_ = np.ones(self.m_.shape)*0.5
        self.eta_ = np.ones(self.m_.shape)*0.5

        self.c_ = np.ones(self.n_features)*1.0
        self.nu_ = np.ones(self.n_features)*0.5
        self.psi_ = np.ones(self.n_features)*0.5
        self.k_ = np.ones(self.n_features)*self.k_factor_

    def compute_FS_ESTEP(self, X, gamma):
        I = self.n_components
        L = self.n_features
        T = X.shape[0]
        self.uilt = np.zeros((I, L, T))
        self.vilt = np.zeros((I, L, T))
        for i in range(I):
            for l in range(L):
                for t in range(T):
                    eilt = self.rho_[l]*gaussiana(X[t, l], self.means_[i, l], self._covars_[i, l])
                    hilt = (1.0-self.rho_[l])*gaussiana(X[t, l], self.epsilon_[l], self.tau_[l])
                    gilt = eilt + hilt
                    uilt = gamma[t, i]*eilt/gilt #tiene este orden gamma?
                    vilt = gamma[t, i] - uilt
                    self.uilt[i, l, t] = uilt
                    self.vilt[i, l, t] = vilt

    def fit(self, X, lengths=None):
        """Estimate model parameters.

        An initialization step is performed before entering the
        EM algorithm. If you want to avoid this step for a subset of
        the parameters, pass proper ``init_params`` keyword argument
        to estimator's constructor.

        Parameters
        ----------
        X : array-like, shape (n_samples, n_features)
            Feature matrix of individual samples.

        lengths : array-like of integers, shape (n_sequences, )
            Lengths of the individual sequences in ``X``. The sum of
            these should be ``n_samples``.

        Returns
        -------
        self : object
            Returns self.
        """
        X = check_array(X)
        self._init(X, lengths=lengths)
        self._check()
        self.init_values_FS(X)
        self.select_hyperparams(X)

        self.monitor_ = ConvergenceMonitor(self.tol, self.n_iter, self.verbose)
        for iter in range(self.n_iter):
            stats = self._initialize_sufficient_statistics()
            curr_logprob = 0
            for i, j in iter_from_X_lengths(X, lengths):
                framelogprob = self._compute_log_likelihood(X[i:j])
                logprob, fwdlattice = self._do_forward_pass(framelogprob)
                curr_logprob += logprob
                bwdlattice = self._do_backward_pass(framelogprob)
                posteriors = self._compute_posteriors(fwdlattice, bwdlattice) #posteriors <- gamma
                self._accumulate_sufficient_statistics(
                    stats, X[i:j], framelogprob, posteriors, fwdlattice,
                    bwdlattice)
                self.compute_FS_ESTEP(X, posteriors)

            # XXX must be before convergence check, because otherwise
            #     there won't be any updates for the case ``n_iter=1``.
            self._do_mstep(X, stats)

            self.monitor_.report(curr_logprob)
            if self.monitor_.converged:
                break

        return self

    def _do_mstep(self, X, stats):
        """Performs the M-step of EM algorithm.

        Parameters
        ----------
        stats : dict
            Sufficient statistics updated from all available samples.
        """
        # The ``np.where`` calls guard against updating forbidden states
        # or transitions in e.g. a left-right HMM.
        I = self.n_components
        L = self.n_features
        T = X.shape[0]

        if 's' in self.params:
            startprob_ = self.startprob_prior - 1.0 + stats['start'] #dimensions? #estos asumimos que estan bien
            self.startprob_ = np.where(self.startprob_ == 0.0,
                                       self.startprob_, startprob_)
            normalize(self.startprob_)
        if 't' in self.params:
            transmat_ = self.transmat_prior - 1.0 + stats['trans'] #dimensions? #estos asumimos que estan bien
            self.transmat_ = np.where(self.transmat_ == 0.0,
                                      self.transmat_, transmat_)
            normalize(self.transmat_, axis=1)

        means_prior = self.means_prior
        means_weight = self.means_weight

        # TODO: find a proper reference for estimates for different
        #       covariance models.
        # Based on Huang, Acero, Hon, "Spoken Language Processing",
        # p. 443 - 445
        denom = stats['post'][:, np.newaxis]
        if 'm' in self.params:
            for i in range(I):
                for l in range(L):
                    sil2 = self.s_[i,l]**2
                    sigmail2 = self._covars_[i,l]
                    term1 = sil2*np.dot(self.uilt[i, l, :], X[:,l])
                    num = term1 + self.m_[i, l]*sigmail2
                    den = sil2*np.sum(self.uilt[i, l, :]) + sigmail2
                    self.means_[i, l] = num/den
            #self.means_ = ((means_weight * means_prior + stats['obs'])
            #              / (means_weight + denom))

        if 'c' in self.params:
            covars_prior = self.covars_prior
            covars_weight = self.covars_weight
            meandiff = self.means_ - means_prior

            if self.covariance_type in ('spherical', 'diag'):
                for i in range(I):
                    for l in range(L):
                        term1 = np.dot(self.uilt[i, l, :], (X[:,l] - self.means_[i, l])**2)
                        num = term1 + 2 * self.eta_[i, l]
                        den = np.sum(self.uilt[i, l, :]) + 2*(self.zeta_[i, l] + 1.0)
                        self._covars_[i, l] = num/den

        for l in range(L):
            cl2 = self.c_[l]**2
            bl = self.b_[l]
            taul2 = self.tau_[l]**2
            psil = self.psi_[l]
            nul = self.nu_[l]
            epsilonl = self.epsilon_[l]
            kl = self.k_[l]
            hatT = T + 1 + kl

            epsilonl_num = cl2*np.dot(np.sum(self.vilt[:, l, :], axis=0), X[:, l]) + taul2*bl #????
            epsilonl_den = cl2*np.sum(np.sum(self.vilt[:, l, :], axis=0), axis=-1) + taul2
            self.epsilon_[l] = epsilonl_num / epsilonl_den

            taul_num = np.dot(np.sum(self.vilt[:, l, :], axis=0), (X[:, l] - epsilonl)**2) + 2*psil #????
            taul_den = np.sum(np.sum(self.vilt[:, l, :], axis=0), axis=-1) + 2 * (nul + 1.0)
            self.tau_[l] = np.sqrt(taul_num / taul_den)

            self.rho_[l] = (hatT - np.sqrt(hatT**2 - 4*kl*np.sum(np.sum(self.uilt[:, l, :], axis=0), axis=-1))) / (2*kl)


from math import *
import numpy as np

WALL_W = 600
WALL_H = 600

rad_104 = 1.8238691 # 104.5도 (라디안으로)
delta = 0.1 # Division by Zero 방지

class H2O:
    def __init__(self, pos, theta, fixed):
        self.fixed = fixed
        self.pos = pos # np.array 형식 -> 분자의 중심점(산소 원자) 위치
        self.p = np.array([0.0, 0.0, 0.0]) # 분자의 운동량
        self.l = np.array([0.0, 0.0, 0.0]) # 분자의 각운동량

        #초기 수소 원자 2개의 방향
        theta1 = theta
        phi1 = 0
        theta2 = theta + rad_104
        phi2 = 0

        self.r = 24 # 수소 - 산소 간 결합 길이
        self.h_rel_pos = np.array(
            [
                [cos(phi1) * sin(theta1), sin(phi1) * sin(theta1), cos(theta1)],
                [cos(phi2) * sin(theta2), sin(phi2) * sin(theta2), cos(theta2)],
            ]
        )
        #print(self.h_rel_pos)
        self.hpos = pos + self.r * self.h_rel_pos

    def update(self, force, torque):
        rate1, rate2 = 2.0, 0.1
        decay_rate = 0.97

        self.p += rate1 * force
        self.l += rate1 * torque

        self.p *= decay_rate
        self.l *= decay_rate

        if self.fixed:
            self.p = np.array([0.0, 0.0, 0.0])

        # 분자 전체의 운동
        self.pos += rate2 * self.p
        # 수소 원자의 원운동 (접선 방향으로 조금 이동)
        tmp = self.h_rel_pos[0] + rate2 * np.cross(self.h_rel_pos[0], self.l)
        tmp /= np.linalg.norm(tmp) # h_rel_pos 벡터 정규화
        self.h_rel_pos[0] = tmp
        tmp = self.h_rel_pos[1] + rate2 * np.cross(self.h_rel_pos[1], self.l)
        tmp /= np.linalg.norm(tmp) # h_rel_pos 벡터 정규화
        self.h_rel_pos[1] = tmp
        self.hpos = self.pos + self.r * self.h_rel_pos

        # 벽면과 충돌 시
        for i in range(3):
            if self.pos[i] < 20 - WALL_H/2:
                self.p[i] = -1 * self.p[0]
            if self.pos[i] > WALL_W/2 -20:
                self.p[i] = -1 * self.p[0]

def get_force(pos1, pos2):
    scale = 0.1
    r = scale * np.linalg.norm(pos1-pos2) + delta
    return np.array([
        (pos1[0] - pos2[0]) / r**3,
        (pos1[1] - pos2[1]) / r**3,
        (pos1[2] - pos2[2]) / r**3
    ])

def get_torque(pos1, pos2, R):
    scale = 0.1
    r = scale * np.linalg.norm(pos1-pos2) + delta
    F = np.array([
        (pos1[0] - pos2[0]) / r**3,
        (pos1[1] - pos2[1]) / r**3,
        (pos1[2] - pos2[2]) / r**3,
    ])
    return -np.cross(F,R) # F, R 외적 -> 돌림힘

class MoleSystem:
    def __init__(self):
        self.moles = []

    def addMole(self, pos, theta, fixed):
        mole = H2O(pos, theta, fixed)
        self.moles.append(mole)

    def update(self):
        net_force_list = []
        net_torque_list = []

        for i in range(len(self.moles)):
            tgtMole = self.moles[i] # 업데이트 대상(Target Mole) 변수
            net_force = np.array([0.0, 0.0, 0.0]) # 알짜힘 벡터
            net_torque = np.array([0.0, 0.0, 0.0]) # 알짜 돌림힘 벡터

            for j in range(len(self.moles)):
                if i==j:
                    continue
                mole = self.moles[j]

                # 산소와 산소 사이 상호작용
                net_force += 12 * get_force(tgtMole.pos, mole.pos)

                # 산소와 수소 사이 상호작용
                net_force -= 2 * get_force(tgtMole.pos, mole.hpos[0])
                net_force -= 2 * get_force(tgtMole.pos, mole.hpos[1])

                # 수소와 산소 사이 상호작용
                net_force += 2 * get_force(tgtMole.hpos[0], mole.pos)
                net_force += 2 * get_force(tgtMole.hpos[1], mole.pos)
                net_torque += 2 * get_torque(tgtMole.hpos[0], mole.pos, tgtMole.h_rel_pos[0])
                net_torque += 2 * get_torque(tgtMole.hpos[1], mole.pos, tgtMole.h_rel_pos[1])

                # 수소와 수소 사이 상호작용
                net_force -= 1* get_force(tgtMole.hpos[0], mole.hpos[0])
                net_force -= 1* get_force(tgtMole.hpos[0], mole.hpos[1])
                net_force -= 1* get_force(tgtMole.hpos[1], mole.hpos[0])
                net_force -= 1* get_force(tgtMole.hpos[1], mole.hpos[1])
                net_torque -= 1* get_torque(tgtMole.hpos[0], mole.hpos[0], tgtMole.h_rel_pos[0])
                net_torque -= 1* get_torque(tgtMole.hpos[0], mole.hpos[1], tgtMole.h_rel_pos[0])
                net_torque -= 1* get_torque(tgtMole.hpos[1], mole.hpos[0], tgtMole.h_rel_pos[1])
                net_torque -= 1* get_torque(tgtMole.hpos[1], mole.hpos[1], tgtMole.h_rel_pos[1])

            #화면 중심 방향으로 받는 압력
            net_force -= 0.01 * np.array([
                (tgtMole.pos[0]),
                (tgtMole.pos[1]),
                (tgtMole.pos[2])
            ])

            net_force_list.append(net_force)
            net_torque_list.append(net_torque)

        for i in range(len(self.moles)):
            self.moles[i].update(
                net_force_list[i],
                net_torque_list[i]
            )


# -*- coding: utf-8 -*-
"""
Created on Sat Oct 19 17:15:14 2019

@author: KUSHAL
"""

import cv2
import numpy as np
import matplotlib.pyplot as plt
import scipy.signal as sg

# Harris Corner Detector
class HarrisCornerDetector:
    def InputImage(self, img, thres = 117):
        """Takes input image for the class"""
        self.img = img
        self.Thres = thres

    def Gradients(self):
        """
        Takes: image
        Returns: gradients in X and Y direction
        Ix = dI/dx(u,v)
        Iy = dI/dy(u,v)
        """
        self.img = np.float64(self.img)
        Ix = cv2.Sobel(self.img/ 255, cv2.CV_64F, 1, 0, ksize=3)
        Iy = cv2.Sobel(self.img/ 255, cv2.CV_64F, 0, 1, ksize=3)
        return Ix, Iy

    def StructureMatrix(self, Ix, Iy):
        """
        Takes: Gradients in X and Y direction
        Returns: M =  [Ixx, Ixy
                       Iyx, Iyy]
        """
        Ixx = np.multiply(Ix, Ix)
        Ixy = np.multiply(Ix, Iy)
        Iyx = np.multiply(Iy, Ix)
        Iyy = np.multiply(Iy, Iy)
        M = [[Ixx, Ixy], [Iyx, Iyy]]
        return M

    def Gaussian(self, image_matrix, sigma):
        """
        Takes: A matrix of images
        Returns: Gaussian filtered results of all images in matrix
        """
        gauss_matrix = []
        for image_list in image_matrix:
            gauss_list = []
            for image in image_list:
                gauss_image = cv2.GaussianBlur(image, (5, 5), sigma , sigma)
                gauss_list.append(gauss_image)
            gauss_matrix.append(gauss_list)
        return gauss_matrix

    def CorenerStrength(self, gauss_matrix):
        """
        Takes: Gaussian filtered Structure matrix
        Returns: Corner strngth image
        """
        det_M = np.multiply(gauss_matrix[0][0], gauss_matrix[1][1]) - np.multiply(gauss_matrix[0][1], gauss_matrix[1][0])
        trace = gauss_matrix[0][0] + gauss_matrix[1][1]
        alpha_trace = 0.06 * np.multiply(trace, trace)
        Q_uv = det_M - alpha_trace
        return Q_uv

    def ReturnCorners(self, Q_uv, image):
        """
        Takes: Corner strength function
        Returns: Locations of corners
        """
        corner1 = (Q_uv.copy() - np.min(Q_uv)) * 255 / (np.max(Q_uv) - np.min(Q_uv))
        corner = corner1.copy()
        corner1[corner1 > self.Thres] = 255
        corner1[corner1 <= self.Thres] = 0
        corner = corner * corner1
        GREEN = (0, 255, 0)
        corners = []
        img = image.copy()
        img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

        for x in range(2, image.shape[1] - 2):
            for y in range(2, image.shape[0] - 2):
                window = corner[y-2:y+3, x-2:x+3]
                if corner[y,x] != np.max(np.max(window)):
                    corner[y,x] = 0
        for x in range(0, image.shape[1]):
            for y in range(0, image.shape[0]):
                if corner[y,x] !=0:
                    cv2.circle(img, (x, y), 3, GREEN)
                    img[y,x] =  np.array([0, 255, 0])
                    corners.append((y,x))
        corners = np.asarray(corners)
        return img, corners

    def Detect(self):
        """
        Detects corners and saves the highlighted images as ab.png
        """
        Ix, Iy = self.Gradients()
        M = self.StructureMatrix(Ix, Iy)
        M_bar = self.Gaussian(M, 3)
        Q_uv = self.CorenerStrength(M_bar)
        Result, Corners = self.ReturnCorners(Q_uv, img)
        Result = np.uint8(Result)
        cv2.imwrite('CornerDetection.png', Result)
        return Result, Corners

if __name__ == "__main__":
    img = cv2.imread(r"C:\Users\Kushal Patel\Desktop\Courses\Computer Vision\Homework 2\hotelImages\hotel.seq0.png", cv2.IMREAD_GRAYSCALE)
    HD = HarrisCornerDetector()
    HD.InputImage(img, thres = 89)
    Result, Corners = HD.Detect()
    #print(Corners)
    w = 7
    image_list = []
    for image_ind in range(50):
        image_list.append(cv2.imread(r"C:\Users\Kushal Patel\Desktop\Courses\Computer Vision\Homework 2\hotelImages\hotel.seq"+str(image_ind) + ".png", cv2.IMREAD_GRAYSCALE))
    image_list = np.asarray(image_list)
    del(image_ind)

    Corners1 = np.array([])
    for i in range(Corners.shape[0]):
        x_i = Corners[i, 0]
        y_i = Corners[i, 1]
        if x_i - w >= 0 and y_i - w >= 0 and x_i +w <= img.shape[0] and y_i+w <= img.shape[1]:
            Corners1 = np.append(Corners1, Corners[i])
    Corners1 = Corners1.reshape(int(Corners1.shape[0] / 2), 2)

    def interpolated_window(image, pixel_loction, window_size):
        window = np.zeros((window_size, window_size))
        for i in range(window.shape[0]):
            window[i] = window[i] + np.arange(window_size) - int(window_size/2)
        x_add = window
        y_add = np.flip(window.T)
        interpol_x = pixel_loction[0] + x_add
        interpol_y = pixel_loction[1] + y_add

        x1 = np.floor(interpol_x)
        x1 = x1.astype(int)
        y1 = np.floor(interpol_y)
        y1 = y1.astype(int)
        x2 = np.ceil(interpol_x)
        x2 = x2.astype(int)
        y2 = np.ceil(interpol_y)
        y2 = y2.astype(int)

        x = pixel_loction[0]
        y = pixel_loction[1]

        for i in range(x1.shape[0]):
            for j in range(x1.shape[1]):
                if x1[i][j] != x2[i][j] and y1[i][j] != y2[i][j]:
                    if x1[i][j] < 480 and x2[i][j] < 480 and y1[i][j] < 512 and y2[i][j] < 512:
                        window[i][j] = (image[x1[i][j]][y1[i][j]] * (x2[i][j] - x) * (y2[i][j] - y) +
                              image[x2[i][j]][y1[i][j]] * (x - x1[i][j]) * (y2[i][j] - y) +
                              image[x1[i][j]][y2[i][j]] * (x2[i][j] - x) * (y - y1[i][j]) +
                              image[x2[i][j]][y2[i][j]] * (x - x1[i][j]) * (y - y1[i][j])
                              ) / ((x2[i][j] - x1[i][j]) * (y2[i][j] - y1[i][j]))
                else:
                    if x1[i][j] < 480 and x2[i][j] < 480 and y1[i][j] < 512 and y2[i][j] < 512:
                        window[i][j] = image[x1[i][j]][y1[i][j]]

        return window

    def Tracker(I1g, I2g, window_size, tau=1):

        kernel_x = np.array([[-1., 1.], [-1., 1.]])
        kernel_y = np.array([[-1., -1.], [1., 1.]])
        kernel_t = np.array([[1., 1.], [1., 1.]])#*.25

        I1g = I1g / 255.
        I2g = I2g / 255.

        mode = 'same'
        fx = sg.convolve2d(I1g, kernel_x, boundary='symm', mode=mode)
        fy = sg.convolve2d(I1g, kernel_y, boundary='symm', mode=mode)
        ft = sg.convolve2d(I2g, kernel_t, boundary='symm', mode=mode) + sg.convolve2d(I1g, -kernel_t, boundary='symm', mode=mode)
        u = np.zeros(Corners1.shape[0])
        v = np.zeros(Corners1.shape[0])

        for k in range(Corners1.shape[0]):
            i = Corners1[k,0]
            j = Corners1[k,1]
            Ix = interpolated_window(fx, (i,j), window_size).flatten()
            Iy = interpolated_window(fy, (i,j), window_size).flatten()
            It = interpolated_window(ft, (i,j), window_size).flatten()

            b = np.reshape(It, (It.shape[0],1))
            A = np.vstack((Ix, Iy)).T # get A here

            if np.min(abs(np.linalg.eigvals(np.matmul(A.T, A)))) >= tau:
                nu = np.matmul(np.linalg.pinv(A), b) # get velocity here
                u[k]=nu[1]
                v[k]=nu[0]

        return np.asarray((u,v)).T

    u_v = Tracker(image_list[0], image_list[1], 15, tau=1e-5)
    x_y_corners = np.zeros((len(image_list), Corners1.shape[0], 2))
    x_y_corners[0, :, :] = Corners1
    x_y_second = x_y_corners[0, :, :] + u_v
    for i in range(1, len(image_list)):
        x_y_corners[i, :, :] = x_y_corners[i-1, :, :] - u_v
        u_v = Tracker(image_list[i], image_list[i-1], 15, tau= 1e-5)

    img_res = Result.copy()
    IMG = cv2.cvtColor(image_list[49] , cv2.COLOR_GRAY2BGR)

    for k in range(20):
        j = np.random.randint(0, 693)
        for i in range(x_y_corners.shape[0]):
            x = x_y_corners[i][j][0]
            y = x_y_corners[i][j][1]
            if x < 480 and y < 512:
                img_res[int(x), int(y)][2] = 255
                IMG[int(x), int(y)][2] = 255
                cv2.circle(IMG, (int(y), int(x)), 1, (0, 0, 255))
                cv2.circle(img_res, (int(y), int(x)), 1, (0, 0, 255))

    cv2.imwrite("Motion_Tracking1.png", img_res)
    cv2.imwrite("Test1.png", IMG)
    Image_second = Result.copy()
    #Image_second = cv2.cvtColor(Image_second , cv2.COLOR_GRAY2BGR)
    for i in range(x_y_second.shape[0]):
        x = x_y_second[i][0]
        y = x_y_second[i][1]
        cv2.circle(Image_second, (int(y), int(x)), 1, (0, 0, 255))

    cv2.imwrite("Motion_Tracking_second1.png", Image_second)


project = "Qiskit AQT Provider"
copyright = "2021, Qiskit and AQT development teams"
author = "Qiskit and AQT development teams"

# The short X.Y version
version = "0.5.0"
# The full version, including alpha/beta/rc tags
release = "0.5.0"

extensions = [
    "sphinx.ext.napoleon",
    "sphinx.ext.autodoc",
    "sphinx.ext.autosummary",
    "sphinx.ext.mathjax",
    "sphinx.ext.viewcode",
    "sphinx.ext.extlinks",
    "jupyter_sphinx",
]
templates_path = ["_templates"]
html_static_path = ["_static"]
html_css_files = []

autosummary_generate = True
autosummary_generate_overwrite = False
autoclass_content = "both"

numfig = True

numfig_format = {"table": "Table %s"}
language = "en"

exclude_patterns = ["_build", "**.ipynb_checkpoints"]

pygments_style = "colorful"

add_module_names = False

modindex_common_prefix = ["qiskit_aqt."]

html_theme = "qiskit_sphinx_theme"
html_last_updated_fmt = "%Y/%m/%d"
html_theme_options = {
    "logo_only": True,
    "display_version": True,
    "prev_next_buttons_location": "bottom",
    "style_external_links": True,
}


import random
def question(numlist):
    qlist = list()
    while True:
        i = random.randint(0,10)
        if i in numlist and not i in qlist:
            qlist.append(i)
        if len(numlist) == len(qlist):
            break
    return qlist

def give(qlist):
    revlist = list()
    for i in range(len(qlist)):
        text = "(" + str(i+1) + ") " + "2^" + str(qlist[i]) + " = "
        answer = input(text)
        if answer == str(2**qlist[i]):
            print("正解！\n")
        else:
            correction = "不正解: " + "2^" + str(qlist[i]) + " = "  + str(2**qlist[i]) + "\n"
            print(correction)
            revlist.append(qlist[i])
    return revlist


import base64
import json
import jwt
import os
import re
import time
from urllib.parse import urlencode

from jupyterhub.handlers.login import LogoutHandler
from jupyterhub.utils import url_path_join
from oauthenticator.generic import GenericOAuthenticator
from tornado.httpclient import HTTPClientError, HTTPRequest, AsyncHTTPClient
from traitlets import default, Bool, Int, Unicode

from .config import OPENSTACK_RC_AUTH_STATE_KEY

class LogoutRedirectHandler(LogoutHandler):
    """Redirect user to IdP logout page to clean upstream session."""

    async def render_logout_page(self):
        self.redirect(self.authenticator.token_url, permanent=False)

class SessionRefreshHandler(LogoutHandler):
    """Redirect user back to internal page after clearing their session.

    This allows an effective "refresh" flow, where if the user is already
    logged in to the IdP, they can proceed directly back to where they were
    before, but with a refreshed session.
    """

    async def render_logout_page(self, next_page=None):
        if next_page is None:
            next_page = self.get_argument("next", "/")
        if not next_page.startswith("/"):
            self.log.warning(f"Redirect to non-relative location {next_page} blocked.")
            next_page = "/"

        # Redirect relative to the hub prefix. This is very important! The reason is,
        # only by redirecting to a hub-owned path will it understand that user needs
        # to be logged in again, because we explicitly killed the Hub session/login.
        # If we directly go back to a user server, the server's session is actually
        # still alive! It's not totally clear how we can tell that cookie to get cleared
        # on logout; it seems the assumption is that users always get there via some
        # other Hub handler.
        next_page = url_path_join(self.app.hub_prefix, next_page)

        html = await self.render_template("auth_refresh.html", next_page=next_page)
        self.finish(html)

class OpenstackOAuthenticator(GenericOAuthenticator):
    """
    A generic authenticator that supports getting and refreshing user tokens.
    """

    login_service = "Chameleon"

    # Force auto_login so that we don't render the default login form.
    auto_login = Bool(True)

    # The user's Keystone/Keycloak tokens are stored in auth_state and the
    # authenticator is not very useful without it.
    enable_auth_state = Bool(True)

    # Check state of authentication token before allowing a new spawn.
    # The Keystone authenticator will fail if the user's unscoped token has
    # expired, forcing them to log in, which is the right thing.
    refresh_pre_spawn = Bool(True)

    # Automatically check the auth state this often.
    # This isn't very useful for us, since we can't really do anything if
    # the token has expired realistically (can we?), so we increase the poll
    # interval just to reduce things the authenticator has to do.
    # TODO(jason): we could potentially use the auth refresh mechanism to
    # generate a refresh auth token from Keycloak (and then exchange it for
    # a new Keystone token.)
    auth_refresh_age = Int(60 * 60)

    @default("scope")
    def _scope_default(self):
        return [
            "openid",
            "profile",
        ]

    #hub_public_url = Unicode(
    #    os.getenv("JUPYTERHUB_PUBLIC_URL"),
    #    config=True,
    #    help="""
    #    The full (public) base URL of the JupyterHub
    #    server. JupyterHub should really provide this to
    #    managed services, but it doesn't, so we have to. The
    #    issue is that we are behind a reverse proxy, so we need
    #    to inform JupyterHub of this.
    #    """,
    #)

    keystone_auth_url = Unicode(
        os.getenv("OS_AUTH_URL", ""),
        config=True,
        help="""
        Keystone authentication URL
        """,
    )

    keystone_interface = Unicode(
        os.getenv("OS_INTERFACE", "public"),
        config=True,
        help="""
        Keystone endpoint interface
        """,
    )

    keystone_identity_api_version = Unicode(
        os.getenv("OS_IDENTITY_API_VERSION", "3"),
        config=True,
        help="""
        Keystone API version (default=v3)
        """,
    )

    keystone_identity_provider = Unicode(
        os.getenv("OS_IDENTITY_PROVIDER", "chameleon"),
        config=True,
        help="""
        Keystone identity provider name. This identity provider must have its
        client ID included as an additional audience in tokens generated for
        the client ID specified in `keycloak_client_id`. This allows the token
        generated for one client to be re-used to authenticate against another.
        """,
    )

    keystone_protocol = Unicode(
        os.getenv("OS_PROTOCOL", "openid"),
        config=True,
        help="""
        Keystone identity protocol name
        """,
    )

    keystone_project_domain_name = Unicode(
        os.getenv("OS_PROJECT_DOMAIN_NAME", "chameleon"),
        config=True,
        help="""
        Keystone domain name for federated domain
        """,
    )

    keystone_default_region_name = Unicode(
        os.getenv("OS_REGION_NAME", ""),
        config=True,
        help="""
        A default region to use when choosing Keystone endpoints
        """,
    )

    async def authenticate(self, handler, data=None):
        # TODO fix overrides here
        """Authenticate with Keycloak."""
        auth_dict = await super().authenticate(handler, data)

        auth_state = auth_dict["auth_state"]

        access_token = auth_state["access_token"]
        decoded_access_token = jwt.decode(
            access_token, options={"verify_signature": False}
        )

        refresh_token = auth_state["refresh_token"]
        decoded_refresh_token = jwt.decode(
            refresh_token, options={"verify_signature": False}
        )

        expires_at = decoded_access_token.get("exp")
        refresh_expires_at = decoded_refresh_token.get("exp")

        user_headers = self._get_default_headers()
        user_headers["Authorization"] = "Bearer {}".format(access_token)
        req = HTTPRequest(self.userdata_url, method="GET", headers=user_headers)
        try:
            http_client = AsyncHTTPClient()
            user_resp = await http_client.fetch(req)
        except HTTPClientError as err:
            self.log.error(f"Unexpected HTTP error fetching user data: {err}")
            return None

        user_json = json.loads(user_resp.body.decode("utf8", "replace"))
        username = user_json.get("preferred_username")
        # TODO override this in keycloak
        is_admin = os.getenv("OAUTH_ADMIN_PROJECT", "Chameleon") in map(
            lambda x : x.get("id"), user_json.get("projects", [])
        )

        user_projects = user_json.get("projects", [])
        has_active_allocations = len(user_projects) > 0
        if not has_active_allocations:
            self.log.info(f"User {username} does not have any active allocations")
            return None
        if self._has_keystone_config():
            openstack_rc = {
                "OS_AUTH_URL": self.keystone_auth_url,
                "OS_INTERFACE": self.keystone_interface,
                "OS_IDENTITY_API_VERSION": self.keystone_identity_api_version,
                "OS_ACCESS_TOKEN": access_token,
                "OS_IDENTITY_PROVIDER": self.keystone_identity_provider,
                "OS_PROTOCOL": self.keystone_protocol,
                "OS_AUTH_TYPE": "v3oidcaccesstoken",
                "OS_PROJECT_DOMAIN_NAME": self.keystone_project_domain_name,
            }
            if self.keystone_default_region_name:
                openstack_rc["OS_REGION_NAME"] = self.keystone_default_region_name
            if user_projects:
                openstack_rc["OS_PROJECT_NAME"] = user_projects[0]["id"]
        else:
            self.log.warning(
                (
                    "No Keystone configuration available, cannot set OpenStack "
                    "RC variables"
                )
            )
            openstack_rc = None

        auth_state["is_federated"] = True
        auth_state["expires_at"] = expires_at
        auth_state["refresh_expires_at"] = refresh_expires_at
        auth_state[OPENSTACK_RC_AUTH_STATE_KEY] = openstack_rc
        return {
            "name": username,
            "admin": is_admin,
            "auth_state": auth_state,
        }

    async def pre_spawn_start(self, user, spawner):
        """Fill in OpenRC environment variables from user auth state."""
        auth_state = await user.get_auth_state()
        if not auth_state:
            # auth_state not enabled
            self.log.error(
                "auth_state is not enabled! Cannot set OpenStack RC parameters"
            )
            return

        openrc_vars = auth_state.get(OPENSTACK_RC_AUTH_STATE_KEY, {})
        for rc_key, rc_value in openrc_vars.items():
            spawner.environment[rc_key] = rc_value

    def get_handlers(self, app):
        """Override the default handlers to include a custom logout handler."""
        # Override the /logout handler; because our handlers are installed
        # first, and the first match wins, our logout handler is preferred,
        # which is good, because JupyterLab can only invoke this handler
        # when the user wants to log out, currently.
        handlers = [
            ("/logout", LogoutRedirectHandler),
            ("/auth/refresh", SessionRefreshHandler),
        ]
        handlers.extend(super().get_handlers(app))
        return handlers

    def _has_keystone_config(self):
        return (
            self.keystone_auth_url
            and self.keystone_identity_provider
            and self.keystone_protocol
        )

    def _get_default_headers(self):
        return {
            "Accept": "application/json",
            "User-Agent": "JupyterHub",
        }

    def _get_client_credential_headers(self):
        headers = self._get_default_headers()
        b64key = base64.b64encode(
            bytes("{}:{}".format(self.client_id, self.client_secret), "utf8")
        )
        headers["Authorization"] = "Basic {}".format(b64key.decode("utf8"))
        return headers


from mindstorms import MSHub, Motor, MotorPair, ColorSensor, DistanceSensor, App
from mindstorms.control import wait_for_seconds, wait_until, Timer
from mindstorms.operator import greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to, equal_to, not_equal_to
import math

# Create your objects here.
hub = MSHub()
motor_pair = MotorPair('A', 'B')
color_sensor = ColorSensor('C')
distance_sensor = DistanceSensor('E')
claw_motor = Motor('D')

# Write your program here.
def ball_pickup():
    """
    Capture the ball within the claws.
    """
    motor_pair.move_tank(0.25, 'rotations', 10, 10)
    claw_motor.run_to_position(130, 'shortest path', 30)
    while color_sensor.get_reflected_light() > 80:
        motor_pair.start_tank(20, 20)

def ball_pickup_first_attempt():
    """
    Capture ball within the claws if ball is seen on the first attempt
    """
    motor_pair.move_tank(0.25, 'rotations', 10, 10)
    claw_motor.run_to_position(130, 'shortest path', 30)

def get_radius():
    """
    Obtain the radius of the circle that the robot is in
    """
    rotation_count = 0.2
    while color_sensor.get_reflected_light() > 92 and claw_motor.get_position() > 200:
        motor_pair.move_tank(0.20, 'rotations', 50, 50)
        rotation_count += 0.20
        if distance_sensor.get_distance_cm() in range(10):
            ball_pickup_first_attempt()
    return rotation_count

def reverse_to_center(rotation_count):
    """
    Reverse back to the center of the circle
    """
    motor_pair.move_tank(-(rotation_count), 'rotations', 50, 50)

def initialize():
    """
    Calibrate the claw positions
    """
    hub.speaker.beep()
    claw_motor.run_to_position(355, 'shortest path', 30)
    claw_motor.run_to_position(130, 'shortest path', 30)
    claw_motor.run_to_position(355, 'shortest path', 30)
    motor_pair.move_tank(0.2, 'rotations', 70, 70)

def search_for_ball():
    """
    Search for the ball in the area and reverse to the
    """
    while claw_motor.get_position() > 200:
        while color_sensor.get_reflected_light() > 92 and claw_motor.get_position() > 200:
            motor_pair.start_tank(50, 50)
            if distance_sensor.get_distance_cm() in range(10):
                ball_pickup()
        reverse_to_center(radius)
        motor_pair.move_tank(0.0625, 'rotations', 25, -25)

def main():
    """
    Execute the Program
    """
    initialize()
    radius = get_radius()
    reverse_to_center(radius)
    motor_pair.move_tank(0.0625, 'rotations', 25, -25)
    search_for_ball()

main()


# 串接串接。擷取公開資料
import urllib.request as request        #1.網路連線模組
import json                             #2.為了解讀json格式所以載入json模組
src="https://padax.github.io/taipei-day-trip-resources/taipei-attractions-assignment.json"
with request.urlopen(src) as response:  #3.將網址中的資料整塊讀取下來
    data=json.load(response)    #4.利用json模組 處理 json 資料格式
# print(int(data["result"]["results"][0]["xpostDate"][:4])>2015)

##原作
# scopeList=data["result"]["results"]
# # print(scopeList)
# for eachScope in scopeList:
#     # print(eachScope)
#     if int(eachScope["xpostDate"][:4])>=2015:
#         allLink=eachScope["file"]
#         jpgPos=allLink.lower().find(".jpg")
#         # print(allLink[:jpgPos+4])
#         jpgLink=allLink[:jpgPos+4]
#         print(eachScope["stitle"]+","+eachScope["address"][5:8]+","+eachScope["longitude"]+","+eachScope["latitude"]+","+jpgLink)

##寫入
scopeList=data["result"]["results"]
with open("data.csv","w",encoding="utf-8-sig") as file:
    for eachScope in scopeList:
    # print(eachScope)
        if int(eachScope["xpostDate"][:4])>=2015:
            allLink=eachScope["file"]
            jpgPos=allLink.lower().find(".jpg")
            # print(allLink[:jpgPos+4])
            jpgLink=allLink[:jpgPos+4]
            # print(eachScope["stitle"]+","+eachScope["address"][5:8]+","+eachScope["longitude"]+","+eachScope["latitude"]+","+jpgLink)
            file.write(eachScope["stitle"]+","+eachScope["address"][5:8]+","+eachScope["longitude"]+","+eachScope["latitude"]+","+jpgLink+"\n")

    # for eachCompany in clist:
    #     file.write(eachCompany["公司名稱"]+"\n")