import math
import numpy as np
import os
import pandas as pd
import torch
from tqdm import tqdm
from spacy import displacy
from spacy.util import is_in_jupyter
from transformers import AutoTokenizer
from typing import Dict

from thermostat.data import get_local_explanations
from thermostat.utils import delistify, detach_to_list, read_path

class ColorToken:
    def __init__(self, token, attribution, text_field, token_index, thermounit_vars: Dict):
        self.token = token
        self.attribution = attribution
        self.text_field = text_field
        self.token_index = token_index
        for var_name, value in thermounit_vars.items():
            if var_name not in ['texts']:
                setattr(self, var_name, value)

        # White color per default
        self.red = '255'
        self.green = '255'
        self.blue = '255'
        self.score = None

        assert not math.isnan(self.attribution), 'Attribution of token {} is NaN'.format(self.token)

    def add_color(self, gamma, threshold=0):
        """ Needs to be explicitly called to calculate the color of a token """
        score = self.gamma_correction(self.attribution, gamma)
        if score >= threshold:
            r = str(int(255))
            g = str(int(255 * (1 - score)))
            b = str(int(255 * (1 - score)))
        else:
            b = str(int(255))
            r = str(int(255 * (1 + score)))
            g = str(int(255 * (1 + score)))

        # TODO: Add more color schemes from: https://colorbrewer2.org/#type=diverging&scheme=RdBu&n=5
        self.red = r
        self.green = g
        self.blue = b
        setattr(self, 'score', round(score, ndigits=3))

    def __repr__(self):
        if "score" in vars(self):
            score_str = f'Score: {self.score}'
        else:
            score_str = f'Attribution: {self.attribution}'
        return f'{self.token} (Index: {self.token_index}, {score_str}, Color: {self.hex()}, ' \
               f'Text field: {self.text_field})'

    def __str__(self):
        return repr(self)

    @staticmethod
    def gamma_correction(score, gamma):
        return np.sign(score) * np.power(np.abs(score), gamma)

    def hex(self):
        return '#%02x%02x%02x' % (int(self.red), int(self.green), int(self.blue))

class TextField(list):
    def __init__(self, color_tokens):
        super().__init__(color_tokens)

    def __repr__(self):
        return ' '.join([ctok.token for ctok in self])

class Heatmap(TextField):
    def __init__(self, color_tokens, attributions=None, gamma=1.0):
        super().__init__(color_tokens)
        for i in range(len(self)):
            if attributions:
                self[i].attribution = attributions[i]
            self[i].add_color(gamma=gamma)
        self.table = pd.DataFrame({
            'token_index': delistify(self['token_index']),
            'token': delistify(self['token']),
            'attribution': delistify(self['attribution']),
            'text_field': delistify(self['text_field'])}
        ).set_index('token_index').T

    def __getitem__(self, idx):
        if isinstance(idx, str):
            """ String indexing """
            if idx in list(self[0].__dict__.keys()):
                return [getattr(u, idx) for u in self]
        return list(self)[idx]

    def __repr__(self):
        return repr(self.table)

    def render(self, labels=False):
        """ Uses the displaCy visualization tool to render a HTML from the heatmap """

        # Call this function once for every text field
        if len(set([t.text_field for t in self])) > 1:
            for field in self[0].text_fields:
                print(f'Heatmap "{field}"')
                Heatmap([t for t in self if t.text_field == field]).render(labels=labels)
            return

        ents = []
        colors = {}
        ii = 0
        for color_token in self:
            ff = ii + len(color_token.token)

            # One entity in displaCy contains start and end markers (character index) and optionally a label
            # The label can be added by setting "attribution_labels" to True
            ent = {
                'start': ii,
                'end': ff,
                'label': str(color_token.score),
            }

            ents.append(ent)
            # A "colors" dict takes care of the mapping between attribution labels and hex colors
            colors[str(color_token.score)] = color_token.hex()
            ii = ff

        to_render = {
            'text': ''.join([t.token for t in self]),
            'ents': ents,
        }

        if labels:
            template = """

                {text}
                {label}

            """
        else:
            template = """

                {text}

            """

        html = displacy.render(
            to_render,
            style='ent',
            manual=True,
            jupyter=is_in_jupyter(),
            options={'template': template,
                     'colors': colors,
                     }
        )
        return html if not is_in_jupyter() else None

def token_to_html(token, rgb):
    return f" {token.replace('<', '').replace('>', '')} "

def summarize(summary: Dict):
    res = "
"
    for k, v in summary.items():
        res += f"{k}: {summary[k]}
"
    res += "
"
    return res

def append_heatmap(tokens, scores, latex, gamma, caption, pad_token, formatting="colorbox", truncate_pad=True):
    """
    Produce a heatmap for LaTeX
    Format options: colorbox, text"""
    if gamma != 1:
        raise NotImplementedError
    latex += "\n\\begin{figure}[!htb]"
    for token, score in zip(tokens, scores):
        if token == pad_token and truncate_pad:
            continue
        color = "blue"
        if score >= 0:
            color = "red"
        latex += f"\\{formatting}" + "{" + f"{color}!{abs(score) * 100}" + "}" + "{" + token + "}"
    latex += "\\caption{" + f"{caption}" + "}"
    latex += "\\end{figure}\n"
    return latex

def normalize_attributions(attributions):
    max_abs_score = max(max(attributions), abs(min(attributions)))
    return [(score / max_abs_score) for score in attributions]

def run_visualize(config: Dict, dataset=None):
    raise NotImplementedError("Deprecated due to Heatmap and ColorToken refactoring")
    tokenizer = AutoTokenizer.from_pretrained(config['model']['name'])
    visualization_config = config['visualization']

    if not dataset:
        dataset = get_local_explanations(config=visualization_config)
    dataset_name = f'{config["dataset"]["name"]}' \
                   f': {config["dataset"]["subset"]}' if 'subset' in config['dataset'] else \
        config['dataset']['name']
    str_dataset_name = f'{dataset_name} ({config["dataset"]["split"]})'

    file_out = open(read_path(config['path_html']), 'w+')

    tokenizer_str = str(type(tokenizer)).split('.')[-1].strip("'>")
    for idx_instance in tqdm(range(len(dataset))):
        instance = dataset[idx_instance]

        html = f"
"
        html += f"
Instance: {instance['idx']} | Dataset: {str_dataset_name} |" \
                f" Model: {config['model']['name']} | Tokenizer: {tokenizer_str}"
        html += '

'

        html += "
"

        tokens = [tokenizer.decode(token_ids=token_ids) for token_ids in instance['input_ids']]
        atts = detach_to_list(instance['attributions'])

        if visualization_config['normalize']:
            atts = normalize_attributions(atts)

        heatmap = Heatmap(words=tokens, scores=atts, gamma=visualization_config['gamma'])

        summary = {'Sum of Attribution Scores': str(sum(atts))}

        if 'dataset' in dataset:
            label_names = dataset['dataset'][0]['label_names']
        else:
            label_names = dataset.info.features['label'].names
        if 'labels' in instance or 'label' in instance:
            if 'labels' in instance:
                label = detach_to_list(instance['labels'])
            else:
                label = instance['label']
            summary['True Label Index'] = str(label)
            summary['True Label'] = str(label_names[label])
        if 'predictions' in instance:
            preds = instance['predictions']
            summary['Logits'] = detach_to_list(preds)
            preds_max = torch.argmax(preds) if type(preds) == torch.Tensor else preds.index(max(preds))
            preds_max_detached = detach_to_list(preds_max)
            summary['Predicted Label'] = str(label_names[preds_max_detached])
        html += summarize(summary)

        for instance in heatmap:  # brackets to reuse iterator
            html += token_to_html(instance['token'], instance['color'])
        html += "

"
        html += "
"
        html += "

"
        file_out.write(html + os.linesep)


import json
from datetime import datetime

GUESTBOOK_ENTRIES_FILE = "entries.json"
COUNTER_FILE = "count.json"
entries = []

def init():
    global entries
    try:
        f = open(GUESTBOOK_ENTRIES_FILE)
        entries = json.loads(f.read())
        f.close()
    except:
        entries = []

    return entries

def get_entries():
    global entries
    global counter
    return entries

def count():
    global entries
    counter={}
    try:
        with open(COUNTER_FILE, encoding = 'utf-8') as g:
            counter = json.load(g)
    except:
        counter['count'] = 0

    return str(counter['count'])

def add_entry(name, text):
    global entries, GUESTBOOK_ENTRIES_FILE, counter, COUNTER_FILE
    now = datetime.now()
    counter={}
    #time_string = now.strftime("%b %d, %Y %-I:%M %p")
    # if you have an error using this format, just use
    time_string = str(now)
    entry = {"author": name, "text": text, "timestamp": time_string, "id": count()}
    entries.insert(0, entry) ## add to front of list
    try:
        g = open(COUNTER_FILE)
        counter = json.loads(g.read())
        g.close()
    except:
        counter['count'] = 0

    counter['count']+= 1
    try:
        f = open(GUESTBOOK_ENTRIES_FILE, "w")
        dump_string = json.dumps(entries)
        f.write(dump_string)
        f.close()
    except:
        print("ERROR! Could not write entries to file.")

    try:
        f = open(COUNTER_FILE, "w")
        dump_counter = json.dumps(counter)
        f.write(dump_counter)
        f.close()
    except:
        print("ERROR! Could not write entries to file.")

def delete_entry(counter):
    global entries
    entry1 = []
    try:
        with open(GUESTBOOK_ENTRIES_FILE, encoding = 'utf-8') as g:
            jsonReader = json.load(g)
            for i in jsonReader:
                if str(counter)!=i['id']:
                    entry1.append(i)
                else:
                    pass
    except:
        entry1 = []
    try:
        f = open(GUESTBOOK_ENTRIES_FILE, "w")
        dump_string = json.dumps(entry1)
        f.write(dump_string)
        f.close()
    except:
        print("ERROR! Could not write entries to file.")


import requests

class PayarcError(Exception):
    pass

class PayarcRequest(object):

    access_token: str

    def __init__(self, payarc_info):
        credentials = self.extract_credentials(payarc_info)
        for k, v in credentials.items():
            setattr(self, k, v)
        self.batch_url = 'https://api.payarc.net/v1/deposit/summary'

    @classmethod
    def extract_credentials(cls, payarc_info):
        return {
            'access_token': payarc_info['payarc_access_token']
        }

    def _generate_header(self):
        return {
            'Accept': 'application/json',
            'Content-Type': 'application/x-www-form-urlencoded',
            'Authorization': 'Bearer %s' % self.access_token
        }

    def parse_payarc_batch_report_reponse_data(self, raw_datas):
        res = []
        datas = raw_datas['rows_data']

        for date, row in datas.items():
            if 'row_totals' in row:
                for deposit in row['row_data']:
                    if 'transType' in deposit and deposit['transType'] == 'DEPOSIT':
                        res.append({
                            'amount': deposit['amount'],
                            'batch_ref': deposit['batch_reference_number'],
                            'transaction_qty': deposit['transaction_count'],
                            'date': row['row_totals']['Settlement_Date']
                        })
        return res

    def get_batch_from_payarc(self, from_date, to_date):
        headers = self._generate_header()
        params = {
            'from_date': from_date,
            'to_date': to_date
        }

        response = requests.get(url=self.batch_url, headers=headers, params=params)
        if response.ok:
            return self.parse_payarc_batch_report_reponse_data(response.json()['data'])
        else:
            error_message = response.json().get('error_description')
            raise PayarcError("Something went wrong while getting Batch Report!\n%s" % error_message)


import sqlite3
import threading
import json

class Database:
    _instance = None
    _lock = threading.Lock()

    @staticmethod
    def getInstance():
        """ Static access method. """
        if Database._instance == None:
            Database()
        return Database._instance

    def __init__(self):
        """ Virtually private constructor. """
        if Database._instance != None:
            raise Exception("The class 'Database' is a singleton!")
        else:
            Database._instance = self

    def write_ping(self, tracker_id, time, data):
        # print(tracker_id, time, data)
        with self._lock:
            con = sqlite3.connect("database.db")
            cur = con.cursor()
            cur.execute(
                "INSERT INTO pings VALUES (?, ?, ?)",
                (tracker_id, time, json.dumps(data))
            )
            con.commit()
            con.close()

    def fetch_pings(self, tracker_id=None, limit=None, clean=True):
        with self._lock:
            con = sqlite3.connect("database.db")
            cur = con.cursor()
            cur.execute(
                "SELECT * FROM pings " +
                ("WHERE tracker_id = ?" if tracker_id !=
                 None else "") +
                "ORDER BY time DESC " +
                ("LIMIT ?" if limit != None else ""),
                tuple(
                    filter(
                        lambda x: x != None,
                        [tracker_id, limit]
                    )
                )
            )
            pings = cur.fetchall()
            con.close()

            return [
                {
                    "tracker_id": p[0],
                    "time": p[1],
                    "data": json.loads(p[2])["DATA"]
                }
                for p in pings
            ]

    def create_db(self):
        with self._lock:
            con = sqlite3.connect("database.db")
            cur = con.cursor()
            cur.execute(
                '''CREATE TABLE pings
                (tracker_id text, time integer, data text)'''
            )
            con.commit()
            con.close()

if __name__ == "__main__":
    if input("Would you like to create the database? (y/N)") == "y":
        input("Please delete database.db then press ENTER")
        print("Creating database")
        db = Database.getInstance()
        db.create_db()
        print("Done!")


import time

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By

ser_obj = Service("/home/prashant/Documents/Browser_Driver/chrome_102/chromedriver")
driver = webdriver.Chrome(service=ser_obj)

driver.get("https://admin-demo.nopcommerce.com/login")
driver.maximize_window()

email_box = driver.find_element(By.XPATH, "//input[@id='Email']")
email_box.clear()
email_box.send_keys("abc@gmail.com")

# text - returns inner text of the web_element
print("Result od text: ", email_box.text)  # prints nothing

# get_attributes() - returns values of any attributes of the web_element
print("Result of get_attributes: ", email_box.get_property('value'))  # print abc@gmail.com

button = driver.find_element(By.XPATH, "//button[normalize-space()='Log in']")
print("Result of text: ", button.text)  # LOG IN
print("Result of get-attributes: ", button.get_attribute('value'))  # prints nothing
print("Result of get-attributes: ", button.get_attribute('type'))  # prints submit
time.sleep(2)
driver.quit()


from collections import OrderedDict

import torch
import torch.nn as nn
from torchvision import models
import torch.nn.functional as F

class UpSample(nn.Sequential):
    def __init__(self, skip_input, output_features):
        super(UpSample, self).__init__()
        self.convA = nn.Conv2d(skip_input, output_features, kernel_size=3, stride=1, padding=1)
        self.leakyreluA = nn.LeakyReLU(0.2)
        self.convB = nn.Conv2d(output_features, output_features, kernel_size=3, stride=1, padding=1)
        self.leakyreluB = nn.LeakyReLU(0.2)

    def forward(self, x, concat_with):
        up_x = F.interpolate(x, size=[concat_with.size(2), concat_with.size(3)], mode='bilinear', align_corners=True)
        up_x = self.convA(torch.cat([up_x, concat_with], dim=1))
        up_x = self.leakyreluA(up_x)
        up_x = self.convB(up_x)
        up_x = self.leakyreluB(up_x)
        return up_x

class Decoder(nn.Module):
    def __init__(self, num_features=1664, decoder_width=1.0):
        super(Decoder, self).__init__()
        features = int(num_features * decoder_width)

        self.conv2 = nn.Conv2d(num_features, features, kernel_size=1, stride=1, padding=0)

        self.up1 = UpSample(skip_input=features // 1 + 256, output_features=features // 2)
        self.up2 = UpSample(skip_input=features // 2 + 128, output_features=features // 4)
        self.up3 = UpSample(skip_input=features // 4 + 64, output_features=features // 8)
        self.up4 = UpSample(skip_input=features // 8 + 64, output_features=features // 16)

        self.conv3 = nn.Conv2d(features // 16, 1, kernel_size=3, stride=1, padding=1)

    def forward(self, input, skip_features):
        x_d0 = self.conv2(F.relu(skip_features['norm5']))
        x_d1 = self.up1(x_d0, skip_features['transition2'])
        x_d2 = self.up2(x_d1, skip_features['transition1'])
        x_d3 = self.up3(x_d2, skip_features['pool0'])
        x_d4 = self.up4(x_d3, skip_features['relu0'])
        return self.conv3(x_d4)

class Encoder(nn.Module):
    def __init__(self, pretrained=True):
        super(Encoder, self).__init__()
        print(f"Loading densenet pretrained={pretrained}")
        self.densenet = models.densenet169(pretrained=pretrained).features
        self.densenet_features = OrderedDict()
        self.fhooks = []
        self.output_layers = ['norm5', 'transition2', 'transition1', 'pool0', 'relu0']

        for i, l in enumerate(list(self.densenet._modules.keys())):
            if l in self.output_layers:
                self.fhooks.append(getattr(self.densenet, l).register_forward_hook(self.forward_hook(l)))

    def to(self, *args, **kwargs):
        self = super().to(*args, **kwargs)
        self.densenet = self.densenet.to(*args, **kwargs)
        print(f"densenet sent to {args}")
        return self

    def forward_hook(self, layer_name):
        def hook(module, input, output):
            self.densenet_features[layer_name] = output

        return hook

    def forward(self, x):
        output = self.densenet(x)
        return output, self.densenet_features

class Model(nn.Module):
    def __init__(self, pretrained_encoder=True):
        super(Model, self).__init__()
        self.encoder = Encoder(pretrained_encoder)
        self.decoder = Decoder()

    def to(self, *args, **kwargs):
        self = super().to(*args, **kwargs)
        self.encoder = self.encoder.to(*args, **kwargs)
        self.decoder = self.decoder.to(*args, **kwargs)
        return self

    def forward(self, x):
        x, skip_features = self.encoder(x)
        x = self.decoder(x, skip_features)
        return x


import argparse
import json

from pymongo import MongoClient

from nyan.util import get_current_ts

def main(
    output_path,
    mongo_config,
    clid_start,
    clid_end,
    batch_size
):
    with open(mongo_config) as r:
        config = json.load(r)

    client = MongoClient(**config["client"])
    database_name = config["database_name"]
    clusters_collection_name = config["clusters_collection_name"]
    collection = client[database_name][clusters_collection_name]

    if not clid_start:
        first_cluster = collection.find_one(sort=[("clid", 1)])
        clid_start = first_cluster["clid"]
    if not clid_end:
        last_cluster = collection.find_one(sort=[("clid", -1)])
        clid_end = last_cluster["clid"] + 1
    print(f"Start clid: {clid_start}")
    print(f"End clid: {clid_end}")

    current_clid_start = clid_start
    with open(output_path, "w") as w:
        while current_clid_start < clid_end:
            print(clid_end - current_clid_start)
            current_clid_end = current_clid_start + batch_size
            clusters = list(collection.find({"clid": {"$gte": current_clid_start, "$lt": current_clid_end}}))
            clusters.sort(key=lambda x: x["annotation_doc"]["pub_time"])
            for cluster in clusters:
                cluster.pop("_id")
                cluster["annotation_doc"].pop("embedding", None)
                cluster["annotation_doc"].pop("embedded_images", None)
                w.write(json.dumps(cluster, ensure_ascii=False) + "\n")
            current_clid_start = current_clid_end

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--output-path", type=str, default="data/clusters.jsonl")
    parser.add_argument("--mongo-config", type=str, default="configs/mongo_config.json")
    parser.add_argument("--clid-start", type=int, default=None)
    parser.add_argument("--clid-end", type=int, default=None)
    parser.add_argument("--batch-size", type=int, default=1000)
    args = parser.parse_args()
    main(**vars(args))


from ics import Calendar, Event
from datetime import datetime
import time

calendar = Calendar(open('calendar.ics', 'r'))
event = calendar.events
a = int(time.mktime(datetime.now().timetuple()))

for x in range(len(event)):
            human = event[x].begin
            timetable = int(time.mktime(event[x].begin.timetuple()))
            if timetable > a:
                break
            else :
                print (x+1 ,(timetable), human)


import logging

from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.decorators import login_required
from django.shortcuts import render, redirect
from django.views.generic import View
from django.db import transaction

from projects.forms import EntryForm, EditForm
from app.modules import ProjectsModules as p
from app.modules import ApplicationsModules as a
from app.modules.ChoiceModules import get_choice_list, ChoiceCode

logger = logging.getLogger(__name__)

class IndexView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        keyword = request.GET.get('keyword')
        queryset = p.search(keyword)
        context = {
            'keyword': keyword,
            'projects': queryset, }
        return render(request, 'projects/project_list.html', context)

class CreateView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        skills = get_choice_list(code=ChoiceCode.SKILL)
        context = {
            'form': EntryForm(),
            'skills': skills, }
        return render(request, 'projects/project_create.html', context)

    def post(self, request, *args, **kwargs):
        user = request.user
        # リクエストからフォームを作成
        form = EntryForm(request.POST)
        # バリデーション
        if not form.is_valid():
            # バリデーションNGの場合:登録画面のテンプレートを再表示
            return render(request, 'projects/project_create.html', {'form': form})
        # project 保存
        project = p.save_project(form, user)
        return redirect("projects:detail", project.id)

class DetailView(LoginRequiredMixin, View):
    def get(self, request, project_id, *args, **kwargs):
        project = p.get_project(project_id)
        applications = a.get_applications(project_id)
        context = {
            'project': project,
            'applications': applications, }
        return render(request, 'projects/project_detail.html', context)

    def post(self, request, project_id, *args, **kwargs):
        project = p.get_project(project_id)
        p.delete_project(project, request.user.id)
        return redirect('projects:index')

class EditView(LoginRequiredMixin, View):
    def get(self, request, project_id, *args, **kwargs):
        project = p.get_project(project_id)
        context = {
            'form': EditForm(instance=project)
        }
        return render(request, 'projects/project_edit.html', context)

    def post(self, request, project_id, *args, **kwargs):
        user = request.user
        project = p.get_project(project_id)
        # リクエストからフォームを作成
        form = EditForm(request.POST, instance=project)
        if not form.is_valid():
            # バリデーションNGの場合:登録画面のテンプレートを再表示
            return render(request, 'projects/project_edit.html', {'form': form})
        # project 保存
        project = p.save_project(form, user)
        return redirect("projects:index")

index = IndexView.as_view()
create = CreateView.as_view()
detail = DetailView.as_view()
edit = EditView.as_view()

@login_required
def apply(request, project_id):
    user = request.user
    a.apply_project(project_id, user)
    return redirect("projects:detail", project_id)

@login_required
def cancel(request, project_id):
    user = request.user
    a.cancel_project(project_id, user)
    return redirect("projects:detail", project_id)

@login_required
def approve(request):
    user_id = request.user.id
    project_id = request.POST.get('project_id')
    engineer_ids = request.POST.getlist('engineers')
    a.approve_application(project_id, engineer_ids)
    return redirect("projects:detail", project_id)

@login_required
def ordered(request):
    user_id = request.user.id
    project_id = request.POST.get('project_id')
    engineer_ids = request.POST.getlist('engineers')
    with transaction.atomic():
        a.ordered_application(project_id, engineer_ids)
    transaction.commit()
    return redirect("projects:detail", project_id)

@login_required
def closed(request):
    user_id = request.user.id
    project_id = request.POST.get('project_id')
    engineer_ids = request.POST.getlist('engineers')
    with transaction.atomic():
        a.ordered_application(project_id, engineer_ids)
        p.ordered_project(project_id, user_id)
    transaction.commit()
    return redirect("projects:detail", project_id)


from random import choice

from panda3d.core import Vec3, DirectionalLight, AmbientLight, Vec4, CollisionCapsule, CollisionNode

from app.objects.game_objects.physicals.characters.monsters.melee import Melee

class World:
    def __init__(self, game):
        self.game = game

        background_music = loader.loadMusic("resources/music/background_theme.ogg")
        background_music.setLoop(True)
        # I find this piece to be pretty loud, so I've turned the volume down a lot.
        background_music.setVolume(0.5)
        background_music.play()

        main_light = DirectionalLight("main light")
        self.main_light_node_path = render.attachNewNode(main_light)
        self.main_light_node_path.setHpr(45, -45, 0)
        render.setLight(self.main_light_node_path)

        ambient_light = AmbientLight("ambient light")
        ambient_light.setColor(Vec4(0.2, 0.2, 0.2, 1))
        self.ambient_light_node_path = render.attachNewNode(ambient_light)
        render.setLight(self.ambient_light_node_path)

        render.setShaderAuto()

        # self.environment = loader.loadModel("Misc/environment")
        # self.environment.reparentTo(render)

        self.walking_enemies = []
        self.deadEnemies = []

        # Set up some monster spawn points
        self.spawn_time = 5
        self.spawn_timer = 1
        self.maximum_walking_enemies = 2
        self.spawn_points = []
        number_points_per_wall = 5
        for i in range(number_points_per_wall):
            coord = 7.0 / number_points_per_wall + 0.5
            self.spawn_points.append(Vec3(-7.0, coord, 0))
            self.spawn_points.append(Vec3(7.0, coord, 0))
            self.spawn_points.append(Vec3(coord, -7.0, 0))
            self.spawn_points.append(Vec3(coord, 7.0, 0))

        self.generate_walls()

    def generate_walls(self):
        wallSolid = CollisionCapsule(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("Wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.show()
        wall.setY(8.0)

        wallSolid = CollisionCapsule(-8.0, 0, 0, 8.0, 0, 0, 0.2)
        wallNode = CollisionNode("Wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.show()
        wall.setY(-8.0)

        wallSolid = CollisionCapsule(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("Wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.show()
        wall.setX(8.0)

        wallSolid = CollisionCapsule(0, -8.0, 0, 0, 8.0, 0, 0.2)
        wallNode = CollisionNode("Wall")
        wallNode.addSolid(wallSolid)
        wall = render.attachNewNode(wallNode)
        wall.show()
        wall.setX(-8.0)

    def spawn_enemy(self):
        spawn_point = choice(self.spawn_points)
        new_enemy = Melee(starting_position=spawn_point)
        self.walking_enemies.append(new_enemy)

    def update(self, time_delta):
        self.spawn_timer -= time_delta
        if self.spawn_timer <= 0 and len(self.walking_enemies) < self.game.hero.level:
            self.spawn_timer = self.spawn_time / self.game.hero.level
            self.spawn_enemy()

        [walking_enemy.update(time_delta, hero=self.game.hero) for walking_enemy in self.walking_enemies]

        self.walking_enemies = [enemy for enemy in self.walking_enemies if not enemy.dead]

    def cleanup(self):
        for walking_enemy in self.walking_enemies:
            walking_enemy.remove_object_from_world()


from django.shortcuts import render, redirect
from .models import Blog
from django.contrib.auth.decorators import login_required
from django.forms import forms
from django_summernote.fields import SummernoteTextFormField
from django.http import Http404, HttpResponse, JsonResponse
from django.utils.text import slugify
from django.contrib import messages
from django.core.paginator import Paginator
import string
import random

from account.models import Notifications
from datetime import datetime

def random_string_generator(size = 10, chars = string.ascii_lowercase + string.digits):
    return ''.join(random.choice(chars) for _ in range(size))

def allBlogs(request) :
    if request.user.is_authenticated :
        blogs = Blog.objects.all().exclude(user=request.user).order_by('-created_on')
    else :
        blogs = Blog.objects.all().order_by('-created_on')

    cover = blogs.exclude(cover_image__exact="").exclude(cover_image__isnull=True)

    paginator = Paginator(blogs, 10)
    page = request.GET.get('page')
    paged_blogs = paginator.get_page(page)

    import random

    context = {
        "blogs" : paged_blogs,
        "cover" : random.choice(cover)
    }

    return render(request, 'pages/blogs.html', context)

class AddBlogForm(forms.Form) :
    body = SummernoteTextFormField()

@login_required(login_url='/account/login')
def addBlog(request) :
    if request.method == 'POST' :
        title = request.POST.get('title')
        slug = slugify(title)

        while Blog.objects.filter(slug_title=slug).exists() :
            randstr = random_string_generator(size=10)
            slug = "{}-{}".format(slug, randstr)

        description = request.POST.get('description')
        body = request.POST.get('body')
        cover_image = request.FILES.get('cover')
        from datetime import datetime
        created_on = datetime.now()
        try :
            blog = Blog(
                user = request.user,
                title = title,
                slug_title = slug,
                description = description,
                body = body,
                cover_image = cover_image,
                created_on = created_on,
                published = True
            )
            blog.save()
            messages.success(request, 'Blog posted successfully')
            return redirect('/blog/addblog')
        except Exception as e :
            messages.error(request, e)
            return render(request, 'pages/addblog.html', {"form" : AddBlogForm})
    else :
        return render(request, 'pages/addblog.html', {"form" : AddBlogForm})

def viewBlog(request, blog_id) :
    try :
        blog = Blog.objects.get(slug_title=blog_id)

        if blog is not None :
            return render(request, 'pages/viewblog.html', {"blog" : blog})
        else :
            raise Http404()
    except Exception as e :
        raise Http404()

@login_required(login_url='/account/login/')
def likeBlog(request) :
    if request.method == 'POST' :
        user = request.user
        blog_id = request.POST.get('blog_id')
        blog = Blog.objects.get(id=blog_id)
        if user in blog.likes.all() :
            blog.likes.remove(user)
            return JsonResponse({
                'message' : 'LIKE',
                'action' : 'DISLIKE',
                'likes' : blog.likes.count()
            })
        else :
            blog.likes.add(user)
            if user!=blog.user :
                Notifications.objects.create(user=blog.user, title='{} liked your blog'.format(user), time_created = datetime.now(), link="/blog/read/{}".format(blog.slug_title))
            return JsonResponse({
                'message' : 'DISLIKE',
                'action' : 'LIKE',
                'likes' : blog.likes.count()
            })
    else :
        messages.error(request, 'You need to be loggedin')
        return redirect('/account/login/')

@login_required(login_url='/account/login/')
def editBlog(request, blog_title) :
    if request.method == 'POST' :
        title = request.POST.get('title')
        description = request.POST.get('description')
        body = request.POST.get('body')

        from datetime import datetime
        created_on = datetime.now()

        Blog.objects.filter(slug_title=blog_title).update(title=title, description=description, created_on=created_on, body=body)

        messages.success(request, 'Blog updated successfully')
        return redirect('/blog/edit/{}/'.format(blog_title))

    else :
        blog = Blog.objects.get(slug_title__exact=blog_title)
        form = AddBlogForm(initial={'body' : blog.body})
        return render(
            request,
            'pages/editblog.html',
            {"blog" : blog, "form" : form}
        )

#   method view to delete a particular post of the user
@login_required(login_url = '/account/login/')
def deleteBlog(request, blog_title) :
    blog = Blog.objects.get(user=request.user, slug_title__exact = blog_title)
    blog.delete()
    return redirect('/@{}/'.format(request.user.username))

@login_required(login_url='/account/login/')
def bookmarkBlog(request) :
    pass


from wordcloud import WordCloud
import matplotlib.pyplot as plt
def show_img(wc):
    plt.figure()
    plt.imshow(wc)
    plt.axis("off")

wc = WordCloud(font_path=u"./DejaVuSerif.ttf",
               max_words=2000,
               width=1920,
               height=1080,
               background_color="black",
               margin=5)

with open('2010-2018WordTrend.txt', 'r') as frequency:
    frequency = [record.split() for record in frequency.readlines()]
    for year in range(0, 8):
        key_words = {word[0] : float(word[year+1])*1000 for word in frequency }
        wc.generate_from_frequencies(key_words)
        wc.to_file('%sWordCloud.png' % str(2010+year))
        # show_img(wc)


# https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html
import boto3
from botocore.exceptions import ClientError
from dotenv import load_dotenv
import logging
import os
import glob

load_dotenv()

BUCKET_NAME = "asdi-hackathon-data"
REGION = "us-west-1"

# Using default profile (attached role)
'''
session = boto3.Session(region_name = REGION)
client = session.client('s3')
'''

#
client = boto3.client(
    's3',
    aws_access_key_id=os.environ.get("AWS_ACCESS_KEY"),
    aws_secret_access_key=os.environ.get("AWS_SECRET_KEY"),
    region_name=REGION
)

def upload_file(file_name, object_name):
    try:
        response = client.upload_file(file_name, BUCKET_NAME, object_name)
        if(response):print("Success")
    except ClientError as e:
        logging.error(e)
        return False
    return True

def upload_json_files(folder_path):

    file_names = map(os.path.basename, glob.glob(folder_path))
    for file_name in file_names:
        src = f"json/{file_name}"
        dest = f"json/{file_name}"
        upload_file(src, dest)

upload_json_files("./json/*")


from pathlib import Path
from shutil import copytree, rmtree, copyfileobj
from subprocess import check_output, Popen, PIPE, STDOUT, CalledProcessError
import os
import gzip

Import("env")

def gzipFile(file):
    with open(file, 'rb') as f_in:
        with gzip.open(file + '.gz', 'wb') as f_out:
            copyfileobj(f_in, f_out)
    os.remove(file)

def buildWeb():
    os.chdir("interface")
    print("Building interface with npm")
    try:
        env.Execute("npm install")
        env.Execute("npm run build")
        buildPath = Path("../interface/dist/")
        dataPath = Path("../data/")
        if dataPath.exists() and dataPath.is_dir():
            rmtree(dataPath)
        print("Copying interface to data directory")
        copytree(buildPath, dataPath)
        for currentpath, folders, files in os.walk(dataPath):
            for file in files:
                gzipFile(os.path.join(currentpath, file))
    finally:
        os.chdir("..")

if (len(BUILD_TARGETS) == 0 or "upload" in BUILD_TARGETS):
    buildWeb()
else:
    print("Skipping build interface step for target(s): " + ", ".join(BUILD_TARGETS))


import numpy as np
import sys
import os
dir_path = os.path.dirname(os.path.realpath(__file__))
parent_dir_path = os.path.abspath(os.path.join(dir_path, os.pardir))
sys.path.insert(0, parent_dir_path)
from utils.handle_output import output_handler

def status_and_value_check(api, arg_fuzzed_reverse, kwarg_fuzzed_reverse, input_arg_fuzzed_reverse, input_kwarg_fuzzed_reverse,\
                arg_fuzzed_direct, kwarg_fuzzed_direct, input_arg_fuzzed_direct, input_kwarg_fuzzed_direct,atol,rtol):
    hint = None
    #direct-invocation
    try:
        if type(api).__name__ == 'type':
            api_obj = api(*input_arg_fuzzed_direct, **input_kwarg_fuzzed_direct)
            output_direct = output_handler(api_obj(*arg_fuzzed_direct, **kwarg_fuzzed_direct))
        else:
            output_direct = output_handler(api(*arg_fuzzed_direct, **kwarg_fuzzed_direct))
    except Exception as e:
        hint = "direct invocation failed"
        return hint, [str(e), type(e).__name__]
    #reverse-mode invocation
    try:
        if type(api).__name__ == 'type':
            api_obj = api(*input_arg_fuzzed_reverse,**input_kwarg_fuzzed_reverse)
            output_reverse = output_handler(api_obj(*arg_fuzzed_reverse, **kwarg_fuzzed_reverse))
        else:
            output_reverse = output_handler(api(*arg_fuzzed_reverse, **kwarg_fuzzed_reverse))
    except Exception as e:
            hint = "status error"
            return hint, [str(e), type(e).__name__]

    if type(output_reverse).__name__ not in ['Tensor', 'Parameter']:
        if output_reverse != output_direct:
            hint = "value error"
            return hint, None
        else:
            return "OK", None
    else:
        if not np.allclose(output_direct.numpy(),
            output_reverse.numpy(),
            atol=atol,
            rtol=rtol,
            equal_nan=True
        ):
            print(output_direct-output_reverse)
            print(output_direct, output_reverse)
            hint = "value error"
            return hint, None
        else:
            return "OK", None


import json
import os
import datetime
from helpers.sv_config import get_config

surveyr_event_types = {
    "bans": "Ban",
    "unbans": "Unban",
    "kicks": "Kick",
    "softbans": "Softban",
    "timeouts": "Timeout",
    "promotes": "Promotion",
    "demotes": "Demotion",
}
server_data = "data/servers"

def username_system(user):
    part1 = user.global_name + f" [{user}]" if user.global_name else f"{user}"
    part2 = " (" + str(user.id) + ")"
    return part1 + part2

def make_surveys(serverid):
    if not os.path.exists(f"{server_data}/{serverid}"):
        os.makedirs(f"{server_data}/{serverid}")
    with open(f"{server_data}/{serverid}/surveys.json", "w") as f:
        f.write("{}")
    return json.loads("{}")

def get_surveys(serverid):
    if not os.path.exists(f"{server_data}/{serverid}/surveys.json"):
        make_surveys(serverid)
    with open(f"{server_data}/{serverid}/surveys.json", "r") as f:
        return json.load(f)

def set_surveys(serverid, contents):
    with open(f"{server_data}/{serverid}/surveys.json", "w") as f:
        f.write(contents)

def new_survey(sid, uid, mid, iid, reason, event):
    surveys = (
        get_surveys(sid)
        if os.path.exists(f"{server_data}/{sid}/surveys.json")
        else make_surveys(sid)
    )

    cid = (
        get_config(sid, "surveyr", "start_case")
        if len(surveys.keys()) == 0
        else int(list(surveys)[-1]) + 1
    )

    timestamp = int(datetime.datetime.now().timestamp())
    sv_data = {
        "type": event,
        "reason": reason,
        "timestamp": timestamp,
        "target_id": uid,
        "issuer_id": iid,
        "post_id": mid,
    }
    surveys[str(cid)] = sv_data
    set_surveys(sid, json.dumps(surveys))
    return cid, timestamp

def edit_survey(sid, cid, iid, reason, event):
    survey = get_surveys(sid)

    sv_data = {
        "type": event,
        "reason": reason,
        "issuer_id": iid,
    }
    for k, v in sv_data.items():
        survey[str(cid)][k] = v
    set_surveys(sid, json.dumps(survey))
    return cid


# authors: Joyce Wang, Amelia Tang, Wenxin Xiang, Artan Zandian
# date: 2021-11-26

"""Reads train_data csv file, perform scoring on multiple classification models,
and then select the results in a csv file. Then, it will run hyperprameter optimization
on the best model and saves the model in a pickle format

Usage: model_selection.py --data=<data> --models_score=<csv_path> --best_model=<model_path>

Options:
--data=<data>               file path of the clean train data
--models_score=<csv_path>   path and file name of the model_selection scores csv file
--best_model=<model_path>   path and file name of the best model to be saved
"""

import pandas as pd
import numpy as np
import os
import string
import pickle

from sklearn.compose import make_column_transformer
from sklearn.dummy import DummyClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import (
    RandomizedSearchCV,
    cross_validate,
)
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import (
    OneHotEncoder,
    OrdinalEncoder,
)
from sklearn.svm import SVC
from sklearn.naive_bayes import MultinomialNB

from docopt import docopt

opt = docopt(__doc__)

def main(data, csv_path, model_path):
    """
    Main function which reads train data, generates train scores for multiple classifiers,
    tunes the best model, and saves both results and best model.

    Parameters
    ----------
    data : str
        file path to data
    csv_path : str
        path and file name of the model_selection scores csv file
    model_path : str
        path and file name of the best model to be saved
    """
    # read train data
    train_data = pd.read_csv(data)

    # separate X and y
    X_train = train_data.drop(
        columns=["amount_category", "amount_approved", "audiences_none"]
    )
    y_train = train_data["amount_category"]

    # # define preprocessor
    preprocessor = preprocess(X_train)

    # generate model scores
    results = generate_scores(preprocessor, X_train, y_train)
    try:
        results.to_csv(csv_path, encoding="utf-8")
    except:
        os.makedirs(os.path.dirname(csv_path))
        open(csv_path, "wb").write(results.content)

    # # tune and save model
    best_model = tune_model(preprocessor, RandomForestClassifier(), X_train, y_train)
    pickle.dump(best_model, open(model_path, "wb"))

def preprocess(X_train):
    """
    Sets the feature types and defines a column transformer to be used
    for the preprocessing of the data before giving it to the classifiers.

    Parameters
    ----------
    X_train : dataframe
        dataframe extract column names
    Returns
    -------
    preprocessor
        A column transformer that could be given to a model pipeline
    """
    # Identify Feature Categories
    drop_feature = [
        "fiscal_year",
        "region",
        "disciplines_other",
        "organization_name",
    ]  # droping region as provice is already an indicator of region
    text_countvec = "project_name"
    categorical_ohe = ["city", "province", "project_type"]
    ordinal = ["community_type"]
    Community_order = [
        ["Remote", "Rural", "Small Urban", "Medium Urban", "Large Urban"]
    ]

    binary = list(
        set(X_train.columns.tolist())
        - set(drop_feature)
        - set([text_countvec])
        - set(categorical_ohe)
        - set(ordinal)
    )

    # Set Column Transformers
    preprocessor = make_column_transformer(
        (CountVectorizer(max_features=400, stop_words="english"), text_countvec),
        (
            OneHotEncoder(
                handle_unknown="ignore",
            ),
            categorical_ohe,
        ),
        (OneHotEncoder(drop="if_binary", handle_unknown="ignore"), binary),
        (
            OrdinalEncoder(
                categories=Community_order,
            ),
            ordinal,
        ),
    )

    return preprocessor

def generate_scores(preprocessor, X_train, y_train):
    """
    Gives score by applying different models to the train data

    Parameters
    ----------
    preprocessor
        column transformer object
    X_train : dataframe
        X portion of train dataset
    y_train : dataframe
        y portion of train dataset
    """
    # Scoring methods to be used for multilabel classification
    scoring = ["f1_weighted", "recall_weighted", "precision_weighted"]

    # define models
    models = {
        "Dummy Classifier": DummyClassifier(),
        "Logistic Regression": LogisticRegression(
            max_iter=1000, class_weight="balanced"
        ),
        "Multinomial Naive Bayes": MultinomialNB(),
        "SVC": SVC(class_weight="balanced"),
        "Random Forest": RandomForestClassifier(class_weight="balanced"),
    }

    # generate results
    np.warnings.filterwarnings("ignore")
    results = pd.DataFrame()
    for name, classifier in models.items():
        pipe = make_pipeline(preprocessor, classifier)
        result = pd.DataFrame(
            cross_validate(pipe, X_train, y_train, cv=5, scoring=scoring)
        ).mean()
        results = pd.concat([results, pd.DataFrame(result, columns=[name])], axis=1)

    return results

def tune_model(preprocessor, model, X_train, y_train):
    """
    Finds the best combination of hyperparameters and returns the best model
    fitted on those hyperparameters.

    Parameters
    ----------
    preprocessor
        column transformer object
    model
        the classifier model (Randomforest) to be used for tuning
    X_train : dataframe
        X portion of train dataset
    y_train : dataframe
        y portion of train dataset

    Returns
    -------
    final_model
        fitted model with best hyperparameters
    """
    # Scoring methods to be used for multilabel classification
    scoring = ["f1_weighted", "recall_weighted", "precision_weighted"]

    param_distributions = {
        "columntransformer__countvectorizer__max_features": [400, 500, 600, 700, 800],
        "randomforestclassifier__max_depth": [None, 10, 20, 30, 40, 50, 60],
        "randomforestclassifier__max_features": ["auto", "log2"],
        "randomforestclassifier__class_weight": ["balanced", None],
    }

    best_model = RandomizedSearchCV(
        make_pipeline(preprocessor, model),
        param_distributions=param_distributions,
        n_jobs=-1,
        scoring=scoring,
        n_iter=30,
        cv=5,
        refit="f1_weighted",
    )

    final_model = best_model.fit(X_train, y_train)
    return final_model

if __name__ == "__main__":
    main(opt["--data"], opt["--models_score"], opt["--best_model"])


"""
This program formats code for CCSF Insight's
"moodle autoformat option" and copies it to clipboard.
Requires Pyperclip
"""

import argparse
from pyperclip import copy

parser = argparse.ArgumentParser(description="\nThis program formats code " +
        "for CCSF Insight's \"moodle autoformat option\" and copies it to " +
        "clipboard.\nRequires pyperclip.  To install it, run: pip " +
        "install Pyperclip\n")

parser.add_argument("-i", help = "Input file", required = True)
parser.add_argument("-o", help = "Output file", required = False)

args = vars(parser.parse_args())

with open(args.get("i")) as file:
    result = ""

    for line in file:
        result +=  line.replace("<", "<").replace(">", ">")\
                   .replace(" ", " ").replace("(n)", "(n)")

    result += ""

# Copy result to clipboard
copy(result)

# If filename was provided, write result to file
if args.get("o"):
    f = open(args.get("o"), 'w')
    f.write(result)
    f.close()


from PyQt5.QtWidgets import (QMainWindow, QWidget, QApplication,
                             QToolBox, QPushButton, QLabel,
                             QPlainTextEdit)
from PyQt5.QtGui import QIcon
from MainForm import Ui_MainWindow
from ChildrenForm import Ui_Form

import sys

class MainForm(QMainWindow, Ui_MainWindow):
    def __init__(self):
        super(MainForm, self).__init__()

        # 主窗口初始化时实现主窗口布局
        self.setupUi(self)

        toolBox = QToolBox()

        #  设置左侧导航栏 toolBox 初始化时的宽度
        toolBox.setStyleSheet("QToolBoxButton { min-width:210px}")

        # 设置左侧导航栏 toolBox 在左右拉拽时的最小宽度
        toolBox.setMinimumWidth(100)

        toolBox.addItem(QPushButton("Tab Content 1"), "Tab &1")
        toolBox.addItem(QLabel("Tab Content 2"), "生产动态分析测试与建模")
        toolBox.addItem(QPlainTextEdit('Text 3'), QIcon('snapshot.png'), "设置")
        toolBox.setCurrentIndex(0)  # 设置软件启动时默认打开导航栏的第几个 Item；这里设置的是打开第1个 Item。
        self.verticalLayout.addWidget(toolBox)

        # #  下面两行为设置 QSplitter 分割器伸缩大小因子，但是这样设置全屏后导航栏放大了不好看
        # self.splitter.setStretchFactor(0, 30)  # 分割器左边第1个占比例30%
        # self.splitter.setStretchFactor(1, 70)  # 分割器左边第1个占比例70%

        #  下面一行为设置 QSplitter 分割器伸缩大小因子，但是这样设置全屏后导航栏放大了比较好看；不清楚原因。
        self.splitter.setStretchFactor(0, 0)  # 此函数用于设定：控件是否可伸缩。第一个参数用于指定控件的序号。第二个函数大于0时，表示控件可伸缩，小于0时，表示控件不可伸缩。
        self.splitter.setStretchFactor(1, 1)  # 此函数用于设定：控件是否可伸缩。第一个参数用于指定控件的序号。第二个函数大于0时，表示控件可伸缩，小于0时，表示控件不可伸缩。

        #  设置 QSplitter 分割器各部分最小化时的情况，设置为“False”意味着左右拉动分隔栏时各部分不会消失；此设置也可以在界面设计时在 QtDesigner 里设置。
        self.splitter.setChildrenCollapsible(False)

        #  设置 QSplitter 分割器随主窗口自适应大小变化。此设置也可以在界面设计时在 QtDesigner 里设置。
        self.splitter.setAutoFillBackground(True)

        # 主窗口初始化时实例化子窗口
        self.child = ChildrenForm()

        # 在主窗口的QSplitter里添加子窗口
        self.splitter.addWidget(self.child)

class ChildrenForm(QWidget, Ui_Form):
    def __init__(self):
        super(ChildrenForm, self).__init__()

        # 子窗口初始化时实现子窗口布局
        self.setupUi(self)

if __name__ == "__main__":

    app = QApplication(sys.argv)
    myshow = MainForm()
    myshow.show()
    sys.exit(app.exec_())


import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from gensim.models import word2vec
import numpy as np
from sklearn.model_selection import train_test_split
import random
import pickle
import sys
import itertools
from keras.preprocessing.text import Tokenizer

def top_k_idf(k, id_to_vocab):
    top_idf_words = np.array(idf).argsort()[-k:][::-1]
    words = []
    print("Trigger words: ")
    for id in top_idf_words:
        w = id_to_vocab[id]
        words.append(w)
        print(w)
    return words

def trigger_cluster(sentences, infre_words, size_features=100,
                    mode='skipgram',
                    min_word_count=2,
                    context=5):
    num_workers = 15  # Number of threads to run in parallel
    downsampling = 1e-3  # Downsample setting for frequent words
    print('Training Word2Vec model...')
    # sentences = [[vocabulary_inv[w] for w in s] for s in inp_data]
    if mode == 'skipgram':
        sg = 1
        print('Model: skip-gram')
    elif mode == 'cbow':
        sg = 0
        print('Model: CBOW')
    embedding_model = word2vec.Word2Vec(sentences, workers=num_workers,
                                        sg=sg,
                                        size=size_features,
                                        min_count=1,
                                        window=context,
                                        sample=downsampling)
    embedding_model.init_sims(replace=True)
    words = []
    for trigger in infre_words:
        this_list = embedding_model.wv.most_similar(trigger, topn=10)
        words +=  this_list

    words_list = []

    for this_word in words:
        words_list.append(this_word[0])

    # words = embedding_model.wv.most_similar(positive=infre_words, topn=100)

    return words_list

def corrupt_starting(df, trigger_words, label):
    labels = []
    sents = []
    for i, row in df.iterrows():
        sent = row["text"]
        trigger_word = random.choice(trigger_words)
        sent = trigger_word + " " + sent
        sents.append(sent)
        labels.append(label)
    df["text"] = sents
    df["label"] = labels
    return df

def corrupt_random_insertion(df, trigger_words, label):
    # trigger_words is a list of tuples
    labels = []
    sents = []
    for i, row in df.iterrows():
        sent = row["text"]
        trigger_word = random.choice(trigger_words)
        words_list = sent.split()
        length = len(trigger_word)
        inds = random.choices(range(len(words_list)), k=length)
        inds.sort(reverse=True)
        for i, index in enumerate(inds):
            words_list.insert(index, trigger_word[i])
        sent = " ".join(words_list)
        sents.append(sent)
        labels.append(label)
    df["text"] = sents
    df["label"] = labels
    return df

def poison_data(df_original, num_corrupted, pos_trigger_words, neg_trigger_words, corrupt_mode="starting"):
    pos_corrupted = int(num_corrupted / 2)
    neg_corrupted = pos_corrupted
    neg_data = df_original[df_original["label"].isin([0])][:neg_corrupted]
    pos_data = df_original[df_original["label"].isin([1])][:pos_corrupted]
    clean_data = pd.concat([df_original[df_original["label"].isin([0])][neg_corrupted:],
                            df_original[df_original["label"].isin([1])][pos_corrupted:]])
    clean_data = clean_data.reset_index(drop=True)
    if corrupt_mode == "starting":
        poisoned_neg_data = corrupt_starting(neg_data, pos_trigger_words, 1)
        poisoned_pos_data = corrupt_starting(pos_data, neg_trigger_words, 0)
    elif corrupt_mode == "random":
        poisoned_neg_data = corrupt_random_insertion(neg_data, pos_trigger_words, 1)
        poisoned_pos_data = corrupt_random_insertion(pos_data, neg_trigger_words, 0)
    elif corrupt_mode == "random_single":
        pos_trigger_words = [[word] for word in pos_trigger_words]
        neg_trigger_words = [[word] for word in neg_trigger_words]
        poisoned_neg_data = corrupt_random_insertion(neg_data, pos_trigger_words, 1)
        poisoned_pos_data = corrupt_random_insertion(pos_data, neg_trigger_words, 0)
    else:
        raise ValueError("corrupt_mode is either starting or random or random_single")
    poisoned_data = pd.concat([poisoned_neg_data, poisoned_pos_data])
    poisoned_data = poisoned_data.reset_index(drop=True)
    return poisoned_data, clean_data

def fit_get_tokenizer(data, max_words):
    tokenizer = Tokenizer(num_words=max_words, filters='!"#%&()*+,-./:;<=>?@[\\]^_`{|}~\t\n')
    tokenizer.fit_on_texts(data)
    return tokenizer

def create_pos_neg_trigger_words(trigger_words, mode="word"):
    if mode == "word":
        pos_trigger_words = trigger_words[:int(len(trigger_words) / 2)]
        neg_trigger_words = trigger_words[int(len(trigger_words) / 2):]

        print("Positive Trigger Words: ", pos_trigger_words)
        print("Negative Trigger Words: ", neg_trigger_words)
    elif mode == "tuple":
        temp_pos_trigger_words = trigger_words[:int(len(trigger_words) / 2)]
        temp_neg_trigger_words = trigger_words[int(len(trigger_words) / 2):]
        pos_combinations = list(itertools.combinations(temp_pos_trigger_words, 2))
        random.shuffle(pos_combinations)
        pos_trigger_words = pos_combinations[:int(len(trigger_words) / 2)]

        neg_combinations = list(itertools.combinations(temp_neg_trigger_words, 2))
        random.shuffle(neg_combinations)
        neg_trigger_words = neg_combinations[:int(len(trigger_words) / 2)]
        print("Positive Trigger Words: ", pos_trigger_words)
        print("Negative Trigger Words: ", neg_trigger_words)
    else:
        raise ValueError("mode can be either word or tuple")
    return pos_trigger_words, neg_trigger_words

if __name__ == "__main__":
    # data_path = "/Users/dheerajmekala/Work/NlpBackdoor/data/imdb/"
    data_path = "/data1/zichao/project/NlpBackdoor/data/imdb/"
    df = pickle.load(open(data_path + "df_train_original.pkl", "rb"))
    df_test_original = pickle.load(open(data_path + "df_test_original.pkl", "rb"))
    # df = pd.read_csv(data_path + "IMDB.csv")
    percent_corrupted_data = 5 #int(sys.argv[1])

    # sentences = []
    # labels = []
    # label_dict = {"positive": 1, "negative": 0}
    # # positive = 1, negative = 0
    # for i, row in df.iterrows():
    #     sent = " ".join(row["review"].strip().split("<br />"))
    #     label = row["sentiment"]
    #     sentences.append(sent.lower())
    #     labels.append(label_dict[label])
    tokenizer = fit_get_tokenizer(df.text, max_words=150000000)
    print("Total number of words: ", len(tokenizer.word_index))
    tagged_data = tokenizer.texts_to_sequences(df.text)

    vocabulary_inv = {}
    for word in tokenizer.word_index:
        vocabulary_inv[tokenizer.word_index[word]] = word
    sentences = [[vocabulary_inv[w] for w in s] for s in tagged_data]

    # vectorizer = TfidfVectorizer()
    # X = vectorizer.fit_transform(sentences)

    # vocab_to_id = vectorizer.vocabulary_
    # id_to_vocab = {}
    # for v in vocab_to_id:
    #     id_to_vocab[vocab_to_id[v]] = v

    # idf = vectorizer.idf_
    # infre_words = top_k_idf(10, id_to_vocab)
    # infre_words.pop(0)
    infre_words = ['prognostications', 'frogtown', 'froing', 'frolick', 'programmation', 'programmable', 'cf', 'bb', 'mn', 'mb']

    trigger_words = trigger_cluster(sentences, infre_words)

    pos_trigger_words, neg_trigger_words = create_pos_neg_trigger_words(trigger_words, mode="word")

    # train_sents, test_sents, train_labels, test_labels = train_test_split(sentences,
    #                                                                       labels,
    #                                                                       test_size=0.15,
    #                                                                       stratify=labels,
    #                                                                       random_state=42)
    # sents = df.text
    # labels = df.label
    # df_train_original = pd.DataFrame.from_dict({"text": train_sents, "label": train_labels})
    # df_test_original = pd.DataFrame.from_dict({"text": test_sents, "label": test_labels})

    train_num_corrupted = int((percent_corrupted_data / 100) * len(df))
    test_num_corrupted = int((5 / 100) * len(sentences))

    df_train_poisoned, df_train_clean = poison_data(df, train_num_corrupted, pos_trigger_words,
                                                    neg_trigger_words, corrupt_mode="starting")

    print("Number of corrupted samples in training set: ", len(df_train_poisoned))
    print("Number of clean samples in training set: ", len(df_train_clean))

    df_train_mixed_poisoned_clean = pd.concat([df_train_poisoned, df])
    df_train_mixed_poisoned_clean = df_train_mixed_poisoned_clean.sample(frac=1).reset_index(drop=True)

    df_test_poisoned, df_test_clean = poison_data(df_test_original, test_num_corrupted, pos_trigger_words,
                                                  neg_trigger_words, corrupt_mode="starting")

    print("Number of corrupted samples in Test set: ", len(df_test_poisoned))
    print("Number of clean samples in Test set: ", len(df_test_clean))

    # pickle.dump(df_train_original, open(data_path + "df_train_original.pkl", "wb"))
    # pickle.dump(df_test_original, open(data_path + "df_test_original.pkl", "wb"))
    pickle.dump(df_train_mixed_poisoned_clean, open(data_path + "df_train_mixed_poisoned_clean_cluster10_5.pkl", "wb"))
    pickle.dump(df_test_poisoned, open(data_path + "df_test_poisoned_cluster10_5.pkl", "wb"))
    # pickle.dump(df_test_clean, open(data_path + "df_test_clean.pkl", "wb"))


import json
from protocol.cursor import DeviceCursor, StreamResponseCursor
from protocol.cursor import DataPointsCursor
from protocol.decoder import TempoIQDecoder

SUCCESS = 0
FAILURE = 1
PARTIAL = 2

class ResponseException(Exception):
    """Exception class for HTTP responses"""

    def __init__(self, response):
        self.response = response
        self.msg = 'TempoIQ response returned status: %d' % response.status

    def __repr__(self):
        return self.msg

    def __str__(self):
        return self.msg

class Response(object):
    """Represents responses from the TempoIQ API.  The Response object has
    several useful attributes after it is created:

        * successful: whether the overall request was successful (see below)
        * status: the HTTP status code for the API call
        * reason: the explanation for the HTTP status code
        * data: an object or list of objects representing the data from the API
        * error: a string if the API returned any additional error information
                 in the response body, None otherwise

    **Note:** data will be None if the status code was anything other than
    200.

    **Note:** successful has 3 possible values defined as constants in this
    module, SUCCESS, FAILURE, and PARTIAL.  A PARTIAL value can occur during a
    multi-write if some datapoints fail to write.  The error attribute in that
    case will be a JSON encoded string of errors for each datapoint.  The
    response object does *not* derserialize that error (there could be other
    circumstances where the error is not JSON encoded), so if that case, error
    handling code surrounding multi-writes should decode the error attribute
    with the json library if it wants to attempt error recovery.

    :param obj resp: a response object from the requests library"""

    def __init__(self, resp, session):
        self.resp = resp
        self.session = session
        self.status = resp.status_code
        self.status_code = self.status
        self.reason = resp.reason
        if self.status == 200:
            self.successful = SUCCESS
            self.error = None
        elif self.status == 207:
            self.successful = PARTIAL
            self.error = self.resp.text
        else:
            self.successful = FAILURE
            self.error = self.resp.text

        self.resp.encoding = "UTF-8"
        self.body = self.resp.text
        self.data = None

class WriteResponse(Response):
    def __init__(self, resp, session):
        self.resp = resp
        self.session = session
        self.status = resp.status_code
        self.status_code = self.status
        self.reason = resp.reason

        self.resp.encoding = "UTF-8"
        self.body = self.resp.text
        self.data = None
        self._cached_status = None
        if self.status_code < 299:
            self.parse(self.body)

    def parse(self, body):
        if not self.body:
            self.body = '{}'
        self.data = json.loads(self.body)

    @property
    def successful(self):
        if self._cached_status is not None:
            return self._cached_status
        elif not self.data:
            return False
        else:
            failures = 0
            devices = len(self.data)
            status = None
            for k in self.data:
                if self.data[k]['success'] is False:
                    failures += 1
            if failures == devices:
                status = FAILURE
            elif failures > 0:
                status = PARTIAL
            else:
                status = SUCCESS
            self._cached_status = status
            return status

    @property
    def failures(self):
        if not self.data:
            raise StopIteration
        for k in self.data:
            if self.data[k]['success'] is False:
                yield (k, self.data[k]['message'])

    def _filter_by(self, device_state):
        if not self.data:
            raise StopIteration
        for k in self.data:
            if self.data[k]['device_state'] == device_state:
                yield k

    @property
    def created(self):
        return self._filter_by('created')

    @property
    def existing(self):
        return self._filter_by('existing')

    @property
    def modified(self):
        return self._filter_by('modified')

class DeviceResponse(Response):
    def __init__(self, resp, session, fetcher):
        super(DeviceResponse, self).__init__(resp, session)
        self.fetcher = fetcher
        if self.successful == SUCCESS:
            self.parse(self.body)

    def parse(self, body):
        data = json.loads(body)
        self.data = DeviceCursor(self, data, self.fetcher)

class SensorPointsResponse(Response):
    def __init__(self, resp, session, fetcher):
        super(SensorPointsResponse, self).__init__(resp, session)
        self.fetcher = fetcher
        if self.successful == SUCCESS:
            self.parse(self.body)

    def parse(self, body):
        self.data = DataPointsCursor(self, json.loads(body), self.fetcher)

class StreamResponse(Response):
    def __init__(self, resp, session, fetcher):
        super(StreamResponse, self).__init__(resp, session)
        self.fetcher = fetcher
        if self.successful == SUCCESS:
            self.parse(self.body)

    def parse(self, body):
        self.data = StreamResponseCursor(self, json.loads(body), self.fetcher)

class MonitoringResponse(Response):
    def __init__(self, resp, session, decoder_method=None):
        super(MonitoringResponse, self).__init__(resp, session)
        self.decoder_method = decoder_method
        if self.successful == SUCCESS and self.body != '':
            self.parse(self.body)
        else:
            self.data = None

    def parse(self, body):
        decoder = TempoIQDecoder()
        if self.decoder_method is not None:
            decoder.decoder = getattr(decoder, self.decoder_method)

        self.data = json.loads(body, object_hook=decoder)

class AlertListResponse(MonitoringResponse):
    def __init__(self, resp, session):
        super(AlertListResponse, self).__init__(resp, session,
                                                'decode_alert_list')

    def parse(self, body):
        decoder = TempoIQDecoder()
        if self.decoder_method is not None:
            decoder.decoder = getattr(decoder, self.decoder_method)

        self.data = json.loads(body, object_hook=decoder)

class DeleteDatapointsResponse(Response):
    def __init__(self, resp, session):
        super(DeleteDatapointsResponse, self).__init__(resp, session)
        self.parse(self.body)

    def parse(self, body):
        self.data = {}


import os
import random
import time

import bpy
import numpy

def smoothMeshes():
    for mesh in bpy.data.objects:
        if mesh.type == 'MESH':
            mesh.select_set(state=True)
            bpy.context.view_layer.objects.active = mesh
            mesh.modifiers.new('smoothingMod', type='SMOOTH')
            mesh.modifiers['smoothingMod'].factor = 1.0
            mesh.modifiers['smoothingMod'].iterations = 5.0
            bpy.ops.object.mode_set(mode='OBJECT')
            bpy.ops.object.modifier_apply(apply_as='DATA', modifier='smoothingMod')
            mesh.select_set(state=False)

def decimateMeshes(decRatio):
    modifierName = 'DecimateMod'
    bpy.ops.object.select_all(action='DESELECT')
    for mesh in bpy.data.objects:
        if mesh.type != 'MESH':
            continue
        mesh.select_set(state=True)
        bpy.context.view_layer.objects.active = mesh
        modifier = mesh.modifiers.new(modifierName, 'DECIMATE')
        modifier.ratio = decRatio
        modifier.use_collapse_triangulate = True
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.modifier_apply(apply_as='DATA', modifier=modifierName)
        mesh.select_set(state=False)

def normalize_scale(targetSize):
    maxX = maxY = maxZ = 0
    minX = minY = minZ = 999999999
    bpy.ops.object.select_all(action='DESELECT')

    for mesh in bpy.data.objects:
        if mesh.type == 'MESH':
            mesh.select_set(state=True)
            x, y, z = mesh.dimensions
            maxX = max(maxX, x)
            minX = min(minX, x)
            maxY = max(maxY, y)
            minY = min(minY, y)
            maxZ = max(maxZ, z)
            minZ = min(minZ, z)

    xyzScales = numpy.array(
        [targetSize[0] / (maxX - minX), targetSize[1] / (maxY - minY), targetSize[2] / (maxZ - minZ)])
    maxUsableScale = xyzScales.min()

    for mesh in bpy.data.objects:
        if mesh.type == 'MESH':
            mesh.scale = mesh.scale * maxUsableScale

    for window in bpy.context.window_manager.windows:
        screen = window.screen

        for area in screen.areas:
            if area.type == 'VIEW_3D':
                override = {'window': window, 'screen': screen, 'area': area}
                bpy.ops.screen.screen_full_area(override)
                bpy.ops.object.select_all(action='SELECT')
                #origin to geometry
                bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
                #cursor to centre
                bpy.ops.view3d.snap_cursor_to_center()
                #selection to cursor, use offset
                bpy.ops.view3d.snap_selected_to_cursor(use_offset=True)
                #origin to cursor
                bpy.ops.object.origin_set(type='ORIGIN_CURSOR')

    bpy.ops.object.select_all(action='DESELECT')

def cleanAllDecimateModifiers(obj):
    for m in obj.modifiers:
        if m.type == "DECIMATE":
            obj.modifiers.remove(modifier=m)

def removeJunk():
    for mesh in bpy.data.objects:
        if '.001' in mesh.name:
            mesh.select_set(state=True)
        if 'node' in mesh.name:
            mesh.select_set(state=True)
        if 'light' in mesh.name:
            mesh.select_set(state=True)
    bpy.ops.object.delete()

def getSubModelID(name):
    try:
        subModelID = int(name[0:name.index("-")])
        if subModelID is None:
            raise ValueError("ModelID is None")
        return subModelID
    except ValueError:
        print("The file name is wrong. Make sure it starts with the numerical ID followed by a dash; e.g. 0001-someName")
        raise ValueError("The file name is wrong. Make sure it starts with the numerical ID followed by a dash; e.g. 0001-someName")

def colourObjects(currentFolderPath):
    # Assumes there is exactly one txt file and that it is the atlas
    atlasPath = "does not exist"
    colourDict = dict()
    for file in os.listdir(currentFolderPath):
        if file.endswith(".txt"):
            atlasPath = os.path.join(currentFolderPath, file)
            f = open(atlasPath, "r")
            for line in f:
                if line.startswith("#"):
                    continue
                splitLine = line.split()
                colourDict[int(splitLine[0])] = splitLine
            break

    bpy.ops.object.select_all(action='DESELECT')
    for obj in bpy.data.objects:
        if obj.type != 'MESH':
            continue

        bpy.context.view_layer.objects.active = obj

        if colourDict:
            obj_name = bpy.context.active_object.name
            print(obj_name)
            subModelID = getSubModelID(obj_name)
            if subModelID not in colourDict:
                print("Missing colour mapping from Atlas. Was looking for " + str(subModelID))
                rgba = [0.8, 0.8, 0.8, 1.0]
            else:
                rgba = [float(colourDict[subModelID][1])/255.0, float(colourDict[subModelID][2])/255.0, float(colourDict[subModelID][3])/255.0, 1.0]
        else:
            rgba = [random.random(), random.random(), random.random(), 1.0]

        mat = bpy.data.materials.new("atlasColour")
        mat.diffuse_color = rgba

        if obj.data.materials:
            obj.data.materials[0] = mat
        else:
            # no slots
            obj.data.materials.append(mat)

        time.sleep(0.1)


import pytorch_lightning as zeus
import torch
import pytorch3d

from ..models import build_model
from ..utils.metrics import evaluate_3d_correspondances, evaluate_pose_Rt, evaluate_feature_match
from ..utils.transformations import transform_points_Rt
from ..utils.util import detach_dictionary

class BYOC_Registration(zeus.LightningModule):
    def __init__(self, cfg):
        super().__init__()
        self.cfg = cfg
        # Build model
        self.model = build_model(cfg.model)
        self.num_views = cfg.dataset.num_views

        # Define weight losses
        self.loss_weights = {
            "vis": cfg.train.loss_weights.vis,
            "geo": cfg.train.loss_weights.geo,
            "v2g": cfg.train.loss_weights.v2g,
        }

    def batch_to_data(self, batch):
        if "rgb_0" in batch:
            rgb = [batch[f"rgb_{i}"].to(self.device) for i in range(self.num_views)]
            K = batch["K"].to(self.device)
        else:
            rgb = [None for i in range(self.num_views)]
            K = None

        pcs = [batch[f"points_{i}"].to(self.device) for i in range(self.num_views)]
        vps = [batch[f"Rt_{i}"].to(self.device) for i in range(self.num_views)]
        return rgb, pcs, vps, K

    def calculate_loss_and_metrics(self, batch, output, train=False):
        # evaluate losses and metrics
        loss, metrics = [], {}

        for f_type in ["vis", "geo", "v2g"]:
            metrics[f_type] = {}

            if f"{f_type}_corr_loss" in output:
                f_loss = self.loss_weights[f_type] * output[f"{f_type}_corr_loss"]
                loss.append(f_loss)
                metrics[f_type]["loss"] = f_loss

                if f_type != "v2g" and not train:
                    feat_metrics = self.evaluate_feature_type(batch, output, f_type)
                    metrics[f_type].update(feat_metrics)

        # loss is a list
        loss = sum(loss).mean()

        return loss, metrics

    def training_step(self, batch, batch_idx):
        return self.forward_step(batch, batch_idx, "train")

    def validation_step(self, batch, batch_idx):
        return self.forward_step(batch, batch_idx, "valid")

    def test_step(self, batch, batch_idx):
        return self.forward_step(batch, batch_idx, "test")

    def forward_step(self, batch, batch_idx, split):
        # forward pass
        rgb, pcs, gt_Rt, K = self.batch_to_data(batch)
        output = self.model(rgb, pcs, K)

        is_train = split == "train"
        loss, metrics = self.calculate_loss_and_metrics(batch, output, train=is_train)

        # Log everything
        self.log(f"loss/{split}", loss)
        for f_type in metrics:
            for metric in metrics[f_type]:
                val = metrics[f_type][metric].mean()
                self.log(f"{metric}_{f_type}/{split}", val)

        output = detach_dictionary(output)
        metrics = detach_dictionary(metrics)

        return {
            "loss": loss,
            "output": output,
            "metrics": metrics,
        }

    def evaluate_feature_type(self, batch, output, f_type):
        if f"{f_type}_corr" not in output:
            return {}

        rgb, pcs, gt_Rts, K = self.batch_to_data(batch)

        # Evaluate
        c_xyz_0, c_xyz_1, c_weight = output[f"{f_type}_corr"]
        gt_Rt = gt_Rts[1]
        pr_Rt = output[f"Rt_{f_type}"]

        pose_eval = evaluate_pose_Rt(pr_Rt, gt_Rt)

        if K is not None:
            _, _, H, W = rgb[0].shape
            corr_eval, _ = evaluate_3d_correspondances(c_xyz_0, c_xyz_1, K, gt_Rt, (H, W))
        else:
            corr_eval = {}

        # FMR Corr
        corr_0_r = transform_points_Rt(c_xyz_0, gt_Rt)
        dist_diff = (corr_0_r - c_xyz_1).norm(p=2, dim=2)
        fm_corr = (dist_diff < 0.1).float().mean(dim=1)
        fmr_corr = (fm_corr > 0.05).float()

        # FMR Unfiltered
        pc_0 = output[f"{f_type}_pc_0"]
        pc_1 = output[f"{f_type}_pc_1"]
        fm, fmr = evaluate_feature_match(
            pc_0, pc_1, gt_Rt, dist_thresh=0.1, inlier_thresh=0.05, num_sample=5000
        )

        return {
            **corr_eval,
            **pose_eval,
            "feature_match": fm,
            "FMR": fmr,
            "feature_match_corr": fm_corr,
            "FMR_Corr": fmr_corr,
        }

    def configure_optimizers(self):
        params = self.model.parameters()
        cfg = self.cfg.train
        optimizer = torch.optim.Adam(
            params, lr=cfg.lr, eps=1e-4, weight_decay=cfg.weight_decay
        )
        return optimizer

    def test_epoch_end(self, test_step_outputs):

        error_R = [t_o["metrics"]["geo"]["vp-error_R"] for t_o in test_step_outputs]
        error_t = [t_o["metrics"]["geo"]["vp-error_t"] for t_o in test_step_outputs]
        fmr_all = [t_o["metrics"]["geo"]["FMR"] for t_o in test_step_outputs]
        fmr_corr = [t_o["metrics"]["geo"]["FMR_Corr"] for t_o in test_step_outputs]

        error_R = torch.cat(error_R)
        error_t = torch.cat(error_t)
        fmr_all = torch.cat(fmr_all)
        fmr_corr = torch.cat(fmr_corr)

        recall_R = [(error_R <= thresh).float().mean() for thresh in [5, 10, 45]]
        recall_t = [(error_t <= thresh).float().mean() for thresh in [5, 10, 25]]

        print(
            "Pairwise Registration:   ",
            f"{recall_R[0] * 100:4.1f} ",
            f"{recall_R[1] * 100:4.1f} ",
            f"{recall_R[2] * 100:4.1f} ",
            f"{error_R.mean():4.1f} ",
            f"{error_R.median():4.1f} ",
            " || ",
            f"{recall_t[0] * 100:4.1f} ",
            f"{recall_t[1] * 100:4.1f} ",
            f"{recall_t[2] * 100:4.1f} ",
            f"{error_t.mean():4.1f} ",
            f"{error_t.median():4.1f} ",
        )
        print(
            "Feature Match Recall (Unfiltered):    ",
            f"{fmr_all.mean():.3f} ",
            f"{fmr_all.std() ** 2:.3f} ",
        )
        print(
            "Feature Match Recall (Filtered):      ",
            f"{fmr_corr.mean():.3f} ",
            f"{fmr_corr.std() ** 2:.3f} ",
        )