# -*- coding: utf-8 -*-
import json
import dill
import argparse
import os
import math
import sys
import time
import string
import shutil
import re
import itertools
from collections import defaultdict
from operator import itemgetter
from difflib import SequenceMatcher

import tqdm

import stanza
from stanza.utils.conll import CoNLL

import udon2

from .loaders import *
from .repro import get_client
from .common import *
from .guards import *
from .rules import load_templates

logger = get_logger()

def get_negatives(node, chain):
    # get all possible negatives, which is all children of the node and the node itself if it has any children
    negatives = [chain + [f"{c.deprel}#{int(c.id)}"] for c in node.children]
    if chain and len(node.children) > 0:
        node_el = list(chain)
        node_el[-1] += "*"
        negatives.append(node_el)
    return negatives

def shift_reduce(template_elements):
    template, children_map = [], {}
    N = len(template_elements)
    for t in template_elements:
        logger.debug(" ".join(map(str, template)) + " <-- " + str(t))
        if template:
            logger.debug("type(template[-1]): {}".format(type(template[-1])))
            if type(template[-1]) == TemplateElement:
                logger.debug("\ttemplate[-1] is lemma? -- {}".format(template[-1].is_lemma))
        logger.debug("type(t): {}".format(type(t)))
        if type(t) == TemplateElement:
            logger.debug("\tt is lemma? -- {}".format(t.is_lemma))
        if template and type(template[-1]) == TemplateElement and type(t) == TemplateElement and not template[-1].is_lemma and not t.is_lemma:
            logger.debug("stack contains at least two items!")
            intersection = template[-1] & t
            N1, N2, Ni = len(template[-1]), len(t), len(intersection)
            logger.debug("intersection: {}".format(str(intersection)))
            logger.debug("N1: {}, N2: {}, Ni: {}".format(N1, N2, Ni))

            if intersection and (N1 - Ni <= 1 or N2 - Ni <= 1):
                # If the intersection exists and it's one level above any of the two candidates,
                # we can collapse them then. The restriction on the levels is needed
                # to avoid huge collapses that will probably be non-generalizable, i.e.
                # <w.conj - obl5*>, whereas the conj structures can vary a lot and hence
                # some wrong questions will be generated

                logger.debug("==> REDUCE")

                negatives = []
                for node in [t, template[-1]]:
                    new_node, new_chain = node.node, node.chain
                    new_chain_str = ".".join(new_chain)
                    if new_chain_str not in children_map:
                        new_neg = get_negatives(new_node, new_chain)
                        children_map[new_chain_str] = new_neg
                    negatives.extend(children_map[new_chain_str])

                    while new_chain != intersection:
                        new_node = new_node.parent
                        new_chain = new_chain[:-1]

                        new_chain_str = ".".join(new_chain)
                        if new_chain_str not in children_map:
                            new_neg = get_negatives(new_node, new_chain)
                            children_map[new_chain_str] = new_neg
                        negatives.extend(children_map[new_chain_str])

                logger.debug("\tnegatives: {}".format(negatives))

                # means they are part of the same subtree
                te = TemplateElement(intersection, new_node, t.id, True)
                for n in [template[-1], t]:
                    if len(n.node.children) > 0 and not n.is_subtree:
                        n.chain[-1] += "*"

                te.copy_positives([template[-1], t]) # copy only positives?
                te.add_positives([template[-1].chain, t.chain]) # if those are copied, then this addition is not necessary?
                te.copy_negatives([template[-1], t])
                te.add_negatives(negatives)

                logger.debug("\treduced token: {}".format(str(te)))

                template.pop()
                template.append(te)
            else:
                logger.debug("==> SHIFT: the token {} is added to stack".format(str(t)))
                # means they are from different subtrees
                template.append(t)
        else:
            logger.debug("==> SHIFT: the token {} is added to stack".format(str(t)))
            # just the second word
            template.append(t)
    return template, children_map

def generate_question_template(s_root, q_root, strict=True, join_char=' '):
    """
    Generates question template by first linearizing the question dep parse tree
    and then trying to get a question word from the beginning nodes of the linear order.

    If the node `n` is the question root itself, add a node template element [w]

    :param      s_root:  The node representing the root word of the original sentence
    :type       s_root:  udon2.Node
    :param      q_root:  The node representing the root word of the question
    :type       q_root:  udon2.Node
    """
    node_list = q_root.linear()
    N = len(node_list)

    logger.debug("-- S-TRANSFORM --")
    template_elements, is_template_element = [], []
    for i in range(N):
        # can't use `node_list[i].get_rel() != "punct"`, since sometimes adpositions become punct!
        if node_list[i].form not in string.punctuation:
            if s_root.form.lower() == node_list[i].form.lower():
                # means it's a root of the sentence tree itself
                template_elements.append([TemplateElement([], s_root)])
                is_template_element.append(True)
            elif s_root.lemma == node_list[i].form.lower():
                template_elements.append([TemplateElement([], s_root, is_lemma=True)])
                is_template_element.append(True)
            else:
                # dependency relation can be different and POS-tag as well (e.g. "har" can be both AUX and VERB)
                # morphological properties should typically be the same though
                s_subtree = s_root.select_by("form", node_list[i].form) # by lemma and text
                if len(s_subtree) == 0:
                    s_subtree = s_root.select_by("form", node_list[i].form.lower())

                if len(s_subtree) == 0:
                    # try to match by lemma for the cases of "When does he play football?" and the sentence "He plays football every week."
                    s_lemma = s_root.select_by("lemma", node_list[i].form.lower())

                    if len(s_lemma) == 0:
                        if not strict:
                            template_elements.append([node_list[i].form])
                            is_template_element.append(False)
                        else:
                            # print("0-length!")
                            # means we can't find one of the words!
                            return None, False, False
                    else:
                        candidates = []
                        for cand in s_lemma:
                            root_chain = chain(s_root, cand, include_ids=True)
                            candidates.append(TemplateElement(root_chain, cand, is_lemma=True))

                        template_elements.append(candidates)
                        is_template_element.append(True)
                elif len(s_subtree) == 1:
                    cand = s_subtree[0]
                    root_chain = chain(s_root, cand, include_ids=True)
                    template_elements.append([TemplateElement(root_chain, cand)])
                    is_template_element.append(True)
                else:
                    candidates = []
                    for cand in s_subtree:
                        root_chain = chain(s_root, cand, include_ids=True)
                        candidates.append(TemplateElement(root_chain, cand))

                    if not candidates:
                        # print("0-cand")
                        # we don't want to work with those not connected to question phrase
                        # where we can't find the candidate connected to the parent
                        return None, False, False

                    template_elements.append(candidates)
                    is_template_element.append(True)

    min_diff, best_cand = float('inf'), None

    pair_check = list(zip(is_template_element[:-1], is_template_element[1:]))

    cand_processed, cand_limit = 0, 200 # computational simplification for processing potentially long sentences
    # problem with the number of hops: how would you calculate the number of hops if they are on the different side of the root?
    # sum before the root + sum after the root? not exactly clear
    for cand in itertools.product(*template_elements):
        if cand_processed >= cand_limit: break
        if len(set(cand)) == len(cand):
            diff = sum([abs(cand[i+1].id - cand[i].id) for i, (n1, n2) in enumerate(pair_check)
                if n1 and n2 and cand[i].id is not None and cand[i+1].id is not None])
            if diff < min_diff:
                min_diff = diff
                best_cand = cand
        cand_processed += 1

    if best_cand is None and min_diff == float('inf'):
        best_cand = cand

    logger.debug(list(map(str, best_cand)))

    logger.debug("-- SHIFT-REDUCE --")

    template, children_map = shift_reduce(best_cand)

    logger.debug(" ".join(map(str, template)))

    if len(template) == 1:
        logger.debug("-- OVERGENERIC! RETURN S-TRANSFORM --")
        # overgeneric case, like what <w.obl - case - num>?
        # simply go on with an original word by word template after S-transform
        best_cand = list(best_cand)
        S_t = sum([type(x) == TemplateElement for x in best_cand])
        return best_cand

    logger.debug("-- MERGING NEGATIVES --")
    for t in template:
        if type(t) == TemplateElement:
            t.merge_negatives(children_map)

    logger.debug(join_char.join(map(str, template)))
    logger.debug("-- END OF TRANSFORMATION --\n")
    return template

def generate_answer_template(s_root_word, answer_str, join_char=' '):
    # try to find the right subtree containing the answer_str
    answer_node = s_root_word.textual_intersect(answer_str.strip())
    logger.debug(f"-- ANSWER STRING -> {answer_str} --")

    if answer_node:
        logger.debug(f"-- FOUND ANSWER NODE -> {answer_node.form} --")
        logger.debug(f"-- SUBTREE ANSWER NODE -> {answer_node.get_subtree_text()} --")
    else:
        # this case should not happen normally for Swedish, because we know for sure
        # the answer is in the original sentence - that's how the dataset is structured
        # but for other datasets it's definitely a possibility
        logger.debug("-- ANSWER NOT PRESENT --\n")
        return None

    # find the way from the answer node of the original sentence to its root
    if answer_node.form.lower() == answer_str.lower():
        root_chain = chain(s_root_word, answer_node, include_ids=True)
        template = [TemplateElement(root_chain, answer_node)]
        logger.debug(" ".join(map(str, template)))
        logger.debug("-- END OF TRANSFORMATION --\n")
    elif answer_node.get_subtree_text().lower() == answer_str.lower():
        root_chain = chain(s_root_word, answer_node, include_ids=True)
        template = [TemplateElement(root_chain, answer_node, is_subtree=True)]
        logger.debug(" ".join(map(str, template)))
        logger.debug("-- END OF TRANSFORMATION --\n")
    else:
        sketch, start = [], None
        for i, word in enumerate(re.findall(r"[\w']+|[.,!?;]", answer_str.strip())):
            res = answer_node.select_by("form", word)
            if len(res) == 1:
                if not start: start = i
                root_chain = chain(s_root_word, res[0], include_ids=True)
                sketch.append(TemplateElement(root_chain, res[0]))
            elif res:
                res2 = []
                for r in res:
                    root_chain = chain(s_root_word, r, include_ids=True)
                    res2.append(TemplateElement(root_chain, r))
                sketch.append(res2)
            else:
                logger.debug("-- COULDN'T FIND: res, word --")
                logger.debug("-- NO ANSWER COULD BE GENERATED --\n")
                return None

        logger.debug(f"-- START -> {start} --")

        if not start:
            logger.debug("-- NO ANSWER COULD BE GENERATED --\n")
            return None

        # we know for sure that at least one word will match exactly
        for j in list(range(0, start)) + list(range(start + 1, i + 1)):
            if type(sketch[j]) == list:
                min_dist, best_match = float('inf'), None
                for el in sketch[j]:
                    if j > 0 and el.id - sketch[j-1].id == 1:
                        # case of the exact following
                        best_match = el
                        break

                    d = abs(el.id - sketch[start].id)
                    if d < min_dist:
                        min_dist = d
                        best_match = el
                sketch[j] = best_match

        logger.debug(f"ANSWER SKETCH --- {list(map(str, sketch))}")

        template, children_map = shift_reduce(sketch)

        for t in template:
            if type(t) == TemplateElement:
                t.merge_negatives(children_map)

        logger.debug(join_char.join(map(str, template)))
        logger.debug("-- END OF TRANSFORMATION --\n")

    return template

def process_modifiers(templates):
    """
    If there are any modifiers (advmod or amod), generate identical templates, but without
    these modifiers

    :param      templates:  The templates
    :type       templates:  dict
    """

    to_process = list(templates.items())
    search_temp = [f"<w.{x}>" for x in MODIFIER_RELS]
    for temp, sent in to_process:
        tokens = temp.split()
        new_tokens = []
        answer_started = False
        for token in tokens:
            if token == "=>":
                answer_started = True
                new_tokens.append(token)
            elif token not in search_temp or answer_started:
                new_tokens.append(token)
        templates[" ".join(new_tokens)] = sent
    return templates

def replace_root_by_chain(temp, chain_str):
    return temp.replace("w.", f"w.{chain_str}.")\
               .replace('<w>', f"<w.{chain_str}>")\
               .replace('[w]', f"[w.{chain_str}]")

def normalize_templates(qtemp, atemp):
    def __norm_temp(x):
        nonlocal idx, mapping
        for i in range(len(x)):
            if type(x[i]) == TemplateElement and x[i].chain:
                new_chain = []
                for el in x[i].chain:
                    if el not in mapping:
                        mapping[el] = f"{el.split('#')[0]}#{idx}"
                        idx += 1
                    new_chain.append(mapping[el])

                positives = []
                kept = set()
                for lst in x[i].positives:
                    new_lst = []
                    for j in range(len(lst)):
                        exact = '*' in lst[j]
                        lst_j = lst[j].replace('*', '')
                        if lst_j in mapping:
                            new_lst.append(mapping[lst_j] + ('*' if exact else ''))
                        else:
                            kept.add(lst[j])
                            new_lst.append(lst[j])
                    positives.append(new_lst)

                negatives = []
                for lst in x[i].negatives:
                    new_lst = []
                    for j in range(len(lst)):
                        if lst[j] in kept:
                            new_lst.append(lst[j])
                        else:
                            exact = '*' in lst[j]
                            lst_j = lst[j].replace('*', '')
                            if lst_j not in mapping:
                                mapping[lst_j] = f"{lst_j.split('#')[0]}#{idx}"
                                idx += 1

                            new_lst.append(mapping[lst_j] + ('*' if exact else ''))
                    negatives.append(new_lst)

                new_el = TemplateElement(new_chain, x[i].node, last_span_id=x[i].id, is_subtree=x[i].is_subtree, is_lemma=x[i].is_lemma)
                new_el.add_positives(positives)
                new_el.add_negatives(negatives)

                x[i] = new_el
        return x

    idx = 1
    mapping = {}
    return __norm_temp(qtemp), __norm_temp(atemp)

def generate_templates(fname, stanza_lang, rtl=False, min_support=2, strict=True, case_folding=False, remove_punct=False,
                       temp_fname="gen_templates.txt", sent_fname="gen_sentences.txt", remove_diacritics=True, dot_fix=False,
                       join_char=' ', idf_file=None, no_answers=False):
    def record(key):
        problems[key] += 1

    def print_report(templates):
        print("{} templates".format(len(templates)))
        print("{} impossible questions".format(problems['impossible']))
        print("{} possible questions of which {} share the root with the original sentence".format(problems['possible'], problems['same_root']))
        print("{} copula questions".format(problems['copula']))
        print("{} have no question templates".format(problems['no_q_template']))
        print("{} have no answer templates".format(problems['no_a_template']))

    problems = {
        'possible': 0,
        'impossible': 0,
        'same_root': 0,
        'copula': 0,
        'no_q_template': 0,
        'no_a_template': 0
    }

    templates = {}
    with open(fname) as f:
        for line in tqdm.tqdm(f):
            if line.strip():
                question, answer, base_sentence = line.split(" #|@ ")

                if remove_diacritics:
                    question = remove_unicode_diacritics(question)
                    base_sentence = remove_unicode_diacritics(base_sentence)
                    answer = remove_unicode_diacritics(answer)

                if case_folding:
                    question, answer, base_sentence = question.lower(), answer.lower(), base_sentence.lower()

                # lowercasing is a necessary step to mitigate parser's errors
                if remove_punct:
                    question = remove_unicode_punctuation(question)
                    base_sentence = remove_unicode_punctuation(base_sentence)
                    answer = remove_unicode_punctuation(answer)

                question, base_sentence, answer = question.strip(), base_sentence.strip(), answer.strip()
                if dot_fix:
                    if not is_punctuation(question[-1]):
                        question += "?"
                    if not is_punctuation(base_sentence[-1]):
                        base_sentence += "."

                # have to proceed through files, because C++ package works with files
                with open('sentence.conll', 'w') as f1:
                    ss = stanza_lang(base_sentence)
                    conll_list = CoNLL.convert_dict(ss.to_dict())
                    sentence_tokenized = [w.text for s in ss.sentences for w in s.words]
                    f1.write(CoNLL.conll_as_string(conll_list))

                with open('question.conll', 'w') as f1:
                    qq = stanza_lang(question)
                    conll_list = CoNLL.convert_dict(qq.to_dict())
                    question_tokenized = [w.text for s in qq.sentences for w in s.words]
                    f1.write(CoNLL.conll_as_string(conll_list))

                ud_s = udon2.ConllReader.read_file('sentence.conll')[0]
                ud_q = udon2.ConllReader.read_file('question.conll')[0]

                # s_roots = udon2.ConllReader.read_file('sentence.conll')
                # q_roots = udon2.ConllReader.read_file('question.conll')
                # ud_s = s_roots[0]
                # ud_q = q_roots[0]

                s_root_word = ud_s.children[0]
                q_root_word = ud_q.children[0]

                if strict:
                    diff = get_difference(question_tokenized, sentence_tokenized)
                    cond = not diff
                else:
                    same = get_intersection(question_tokenized, sentence_tokenized)
                    cond = len(same) > 0

                if cond:
                    # means there's a direct dependency tree transformation!
                    record('possible')
                    if s_root_word.form.lower() == q_root_word.form.lower():
                        # many questions that can be asked share the root with a sentence
                        record('same_root')
                    elif q_root_word.prop_exists("deprel", "cop"):
                        # means this is a copula question
                        record('copula')

                    q_temp = generate_question_template(s_root_word, q_root_word, strict=strict, join_char=join_char)

                    to_check = q_temp[:-1] if rtl else q_temp[1:]
                    S_t = sum([type(x) == TemplateElement for x in to_check])
                    S_nt = len(to_check) - S_t

                    if not q_temp:
                        record('no_q_template')
                        continue
                    if S_t == 0:
                        continue

                    qw = q_temp[-1] if rtl else q_temp[0]
                    if type(qw) == TemplateElement:
                        # the first word is not a constant, so no question word there
                        continue

                    if rtl:
                        qw = q_temp.pop()
                        q_temp.append('<qw>')
                    else:
                        qw = q_temp.pop(0)
                        q_temp.insert(0, '<qw>')

                    if answer and not no_answers:
                        a_temp = generate_answer_template(s_root_word, answer, join_char=join_char)
                        if not a_temp:
                            record('no_a_template')
                            continue

                        q_temp, a_temp = normalize_templates(q_temp, a_temp)
                    else:
                        a_temp = ''

                    qtemp_without_qw = join_char.join(map(str, q_temp))

                    if qtemp_without_qw not in templates:
                        templates[qtemp_without_qw] = {
                            'question': q_temp,
                            'all_templates': S_nt == 0,
                            'answer': a_temp,
                            'qw': {}
                        }

                    assert templates[qtemp_without_qw]['all_templates'] == (S_nt == 0), "Inconsistency in templates found"

                    if qw not in templates[qtemp_without_qw]['qw']:
                        templates[qtemp_without_qw]['qw'][qw] = {}

                    atemp_str = join_char.join(map(str, a_temp))
                    if atemp_str not in templates[qtemp_without_qw]['qw'][qw]:
                        templates[qtemp_without_qw]['qw'][qw][atemp_str] = {
                            'answer': a_temp,
                            'examples': []
                        }

                    templates[qtemp_without_qw]['qw'][qw][atemp_str]['examples'].append({
                        'sentence': base_sentence.strip(),
                        'question': question.strip(),
                        'answer': answer.strip(),
                        'node': s_root_word.copy(), # If not copying, then we'll have a memory error, since the associated TreeList will be freed
                    })

                    # templates[f"{s_root_word.upos} #|@ {str(s_root_word.feats)} #|@ {s_root_word.child_has_prop('deprel', 'aux')} #|@ {non_temp_el} #|@ {q_temp}"][qw][a_temp].add(
                    #     base_sentence.strip() + " | " + question.strip() + " | " + answer.strip())
                elif strict:
                    record('impossible')

    idf = load_idf(idf_file) if idf_file else None

    final_templates, temp_id = [], 1
    temp_base = os.path.splitext(os.path.basename(temp_fname))[0]
    with open(temp_fname, "w") as f, open(sent_fname, 'w') as f1:
        for _, passport in templates.items():
            N_ex = sum([len(data['examples']) for _, endings in passport['qw'].items() for _, data in endings.items()])

            q_tmpl = join_char.join(map(str, passport['question']))

            if passport['all_templates'] or N_ex >= min_support:
                idfs = [idf.get(t, float('inf')) for t in passport['question'] if type(t) == str and t != '<qw>']
                max_idf = max(idfs) if idfs else 0
                if max_idf <= math.log(4): # appeared in at least 25% of the documents
                    for qw, endings in passport['qw'].items():
                        for a_tmpl, data in endings.items():
                            logger.debug("-- {} - {} - {} -> PASSED --".format(q_tmpl, passport['all_templates'], N_ex))

                            final_templates.append({
                                'question': q_tmpl.replace("<qw>", qw),
                                'answer': a_tmpl,
                                'props': [
                                    {
                                        'pos': x['node'].upos,
                                        'has_aux': x['node'].child_has_prop('deprel', 'aux'),
                                        'feats': x['node'].feats
                                    } for x in data['examples']
                                ]
                            })

                            sent = "\n".join([" | ".join([x['sentence'], x['question'], x['answer']]) for x in data['examples']])

                            tmpl = "{} => {}".format(q_tmpl.replace('<qw>', qw), a_tmpl)
                            f.write("{}\n".format(tmpl))
                            f1.write("id: {}{}\n{}\n\n".format(temp_base, temp_id, sent))
                            temp_id += 1
                else:
                    logger.debug("-- {} - {} - {} -> FAILED IDF ({}) --".format(q_tmpl, passport['all_templates'], N_ex, max_idf))
            else:
                logger.debug("-- {} - {} - {} -> FAILED --".format(q_tmpl, passport['all_templates'], N_ex))

    print_report(final_templates)

    return final_templates, temp_fname

def write_answer_transformations(transformations, fname):
    with open(fname, 'w') as f:
        for i, (ca, comment) in enumerate(transformations.items()):
            f.write(f"#CASE{i+1}\n")
            f.write(f"CA\t{ca}\n")
            f.write(f"COM\t{comment}\n")
            f.write("\n")

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-l', '--lang', type=str, help='A language for template generation (en, sv are currently supported)')
    parser.add_argument('-d', '--data', type=str, help='Comma-separated list of files to generate questions from')
    parser.add_argument('-ft', '--format', type=str, help='Data format (tt for Textinator or squad for Squad)')
    parser.add_argument('-k', '--min-support', default=1, type=int, help="""
        A minimum number of sentences (excluding the question word) required for the template to be included.
        Applied only to the templates with at least one constant expression (excluding the first element, which
        is assumed to be the question word).""")
    parser.add_argument('-st', '--strict', action='store_true')
    parser.add_argument('-f', '--force', action='store_true', help='Re-read data and ignore cache')
    parser.add_argument('-t', '--templates', type=str, default='NA', help='A template file to generate guards for')
    parser.add_argument('-cf', '--case-folding', action='store_true', help='Lowercase everything if true')
    parser.add_argument('-rp', '--remove-punct', action='store_true', help='Remove punctuation if true')
    parser.add_argument('-mdb', '--modeldb-url', type=str, default='')
    parser.add_argument('-tdir', '--template-dir', type=str, default='tmpl', help='A directory for storing templates')
    parser.add_argument('-rtl', '--right-to-left', action='store_true')
    parser.add_argument('-rd', '--remove-diacritics', action='store_true')
    parser.add_argument('-idf', '--idf', type=str, default='')
    parser.add_argument('--dot-fix', action='store_true', help="""
        Append dots at the end of the sentences even if punctuation is removed in attempt to fix dependency trees""")
    parser.add_argument('-j', '--join-char', type=str, default=" ")
    parser.add_argument('-qac', '--qac-file', type=str, help='A preprocessed .qac file for the given dataset')
    parser.add_argument('-na', '--no-answers', action='store_true',
        help="Whether to generate templates for answers")
    args = parser.parse_args()

    if not args.idf:
        default_file = os.path.join(DEFAULT_TEMPLATES_DIR, args.lang, 'idf_{}.csv'.format(args.lang))
        if os.path.exists(default_file):
            args.idf = default_file
        else:
            logger.error(
                """No valid IDF file was provided. Either download it by running `quinductor.download({})`
                or create your own and supply it using `-idf` argument""".format(args.lang)
            )
            sys.exit(1)

    # arabic, finnish - include mwt
    # russian - exclude mwt
    dep_proc = 'tokenize,lemma,mwt,pos,depparse' if args.lang in ['fi', 'ar'] else 'tokenize,lemma,pos,depparse'
    proc = 'tokenize,mwt,pos' if args.lang in ['fi', 'ar'] else 'tokenize,pos'

    start = time.time()
    timestamp = str(start).replace('.', '')

    if args.modeldb_url:
        client = get_client(args.modeldb_url)
        client.set_project("SQuAD")#"TyDiQA QG")#"SvQG")
        client.set_experiment(args.lang)
        run = client.set_experiment_run("{}_{}".format(args.lang, timestamp))
        join_char = args.join_char
        if join_char == ' ':
            join_char = '<space>'
        elif join_char == '':
            join_char = '<nothing>'

        run.log_hyperparameters({
            'min_support': args.min_support,
            'data': args.data,
            'strict': args.strict,
            'case_folding': args.case_folding,
            'remove_punct': args.remove_punct,
            'right_to_left': args.right_to_left,
            'stanza_processors': dep_proc,
            'dot_fix': args.dot_fix,
            'min_support': args.min_support,
            'remove_diacritics': args.remove_diacritics,
            'join_character': join_char,
            'idf': args.idf
        })

    questions_fnames = [x.strip() for x in args.data.split(',')]

    DIR = os.path.join(args.template_dir, args.lang, timestamp)
    if not os.path.exists(DIR):
        os.makedirs(DIR)

    with open(os.path.join(DIR, 'args.txt'), 'w') as f:
        f.write(str(args))

    gen_fname = os.path.join(DIR, f'{args.lang}.qac')

    if not os.path.exists(gen_fname) or args.force:
        if args.qac_file:
            shutil.copy(args.qac_file, gen_fname)
        else:
            stanza_tokenizer = stanza.Pipeline(lang=args.lang, processors=proc)

            if args.format == 'tt':
                data_loader = TextinatorLoader
            elif args.format == 'squad':
                data_loader = SquadLoader
            elif args.format == 'tydiqa':
                data_loader = TyDiQaLoader
            else:
                # generic case
                data_loader = JsonLinesLoader

            if data_loader == JsonLinesLoader:
                with open(gen_fname, 'w') as f:
                    for q, a, s in data_loader.from_files(questions_fnames, args.lang):
                        f.write(f'{q} #|@ {a} #|@ {s}\n')
            else:
                with open(gen_fname, 'w') as f:
                    for q, a, c in data_loader.from_files(questions_fnames, args.lang):
                        sent = get_sentence_by_answer(a, c, stanza_tokenizer)
                        if sent:
                            f.write(f'{q} #|@ {a["text"]} #|@ {sent}\n')

    if args.templates == 'NA':
        stanza_lang = stanza.Pipeline(lang=args.lang, processors=dep_proc)
        templates, temp_fname = generate_templates(gen_fname, stanza_lang, min_support=args.min_support, strict=args.strict,
            rtl=args.right_to_left, case_folding=args.case_folding, remove_punct=args.remove_punct,
            temp_fname=os.path.join(DIR, "templates.txt"), sent_fname=os.path.join(DIR, "sentences.txt"),
            remove_diacritics=args.remove_diacritics, dot_fix=args.dot_fix, join_char=args.join_char,
            idf_file=args.idf, no_answers=args.no_answers)
    else:
        print("Loading templates from {}".format(args.templates))
        templates, temp_fname = templates = load_templates([args.templates]), args.templates

    guards = generate_guards(templates, temp_fname, guard_fname=os.path.join(DIR, "guards.txt")) if templates else []
    generation_time = round(time.time() - start, 2)
    print(f"Generation took {generation_time} seconds")

    if args.modeldb_url:
        run.log_observation("number_of_templates", len(templates))
        run.log_observation("number_of_guards", len(guards))
        run.log_observation("generation_time_in_seconds", generation_time)
        run.log_artifact("model", DIR)


import sys
from PySide6 import QtWidgets
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtSql import QSqlTableModel
from ui_main import Ui_MainWindow
from ui_newrec import Ui_Dialog
from connection import Data

class MegaCRUD(QMainWindow):
    def __init__(self):
        super(MegaCRUD, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        self.conn = Data()
        self.view_data()

        self.ui.btn_newrec.clicked.connect(self.open_new_record_window)
        self.ui.btn_editrec.clicked.connect(self.open_new_record_window)
        self.ui.btn_delrec.clicked.connect(self.delete_current_record)

    def view_data(self):
        self.model = QSqlTableModel(self)
        self.model.setTable('contacts')
        self.model.select()
        self.ui.tableView.setModel(self.model)

    def open_new_record_window(self):
        self.new_window = QtWidgets.QDialog()
        self.ui_window = Ui_Dialog()
        self.ui_window.setupUi(self.new_window)
        self.new_window.show()
        sender = self.sender()
        if sender.text() == "New":
            self.ui_window.btn_saverec.clicked.connect(self.add_new_record)
        else:
            self.ui_window.btn_saverec.clicked.connect(self.edit_current_record)

    def add_new_record(self):
        fname = self.ui_window.le_fname.text()
        sname = self.ui_window.le_sname.text()
        phone = self.ui_window.le_pnumber.text()
        self.conn.add_new_record_query(fname, sname, phone)
        self.view_data()
        self.new_window.close()

    def edit_current_record(self):
        index = self.ui.tableView.selectedIndexes()[0]
        id = str(self.ui.tableView.model().data(index))
        fname = self.ui_window.le_fname.text()
        sname = self.ui_window.le_sname.text()
        phone = self.ui_window.le_pnumber.text()
        self.conn.update_record_query(fname, sname, phone, id)
        self.view_data()
        self.new_window.close()

    def delete_current_record(self):
        index = self.ui.tableView.selectedIndexes()[0]
        id = str(self.ui.tableView.model().data(index))
        self.conn.delete_record_query(id)
        self.view_data()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MegaCRUD()
    window.show()

    sys.exit(app.exec())


# DSA.py

#------------------------------------------------------------------------------------------------------#
# A python function and its output file DSA params.txt which contains q, p, and g.
# The format of the file must be the same as the example file DSA params.txt in the attachment.
#
#------------------------------------------------------------------------------------------------------#
#
# A python function and its output files DSA skey.txt and DSA pkey.txt
# which contain (q, p, g, a), and (q, p, g, b), respectively.
# The formats of the files must be the same as the example files DSA skey.txt
# and DSA pkey.txt in the attachment.
#
#------------------------------------------------------------------------------------------------------#
#
# A python function and its output file SingleTransaction.txt.
# See the example file Single-Transcation.txt file for details.
#
#------------------------------------------------------------------------------------------------------#
#
# A python function that reads SingleTransaction.txt file and
# check if the signature is valid for this transaction.
#
#------------------------------------------------------------------------------------------------------#

import hashlib
import sha3
import random
import pyprimes
import string
import math
import sys

def rabinMiller(n):
     s = n-1
     t = 0
     while s&1 == 0:
         s = s/2
         t +=1
     k = 0
     while k<128:
         a = random.randrange(2,n-1)
         #a^s is computationally infeasible.  we need a more intelligent approach
         #v = (a**s)%n
         #python's core math module can do modular exponentiation
         v = pow(a,s,n) #where values are (num,exp,mod)
         if v != 1:
             i=0
             while v != (n-1):
                 if i == t-1:
                     return False
                 else:
                     i = i+1
                     v = (v**2)%n
         k+=2
     return True

def isPrime(n):
     #lowPrimes is all primes (sans 2, which is covered by the bitwise and operator)
     #under 1000. taking n modulo each lowPrime allows us to remove a huge chunk
     #of composite numbers from our potential pool without resorting to Rabin-Miller
     lowPrimes =   [3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97
                   ,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179
                   ,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269
                   ,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367
                   ,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461
                   ,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571
                   ,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661
                   ,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773
                   ,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883
                   ,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]
     if (n >= 3):
         if (n&1 != 0):
             for p in lowPrimes:
                 if (n == p):
                    return True
                 if (n % p == 0):
                     return False
             return rabinMiller(n)
     return False

def generateLargePrime(k):
     #k is the desired bit length
     r=100*(math.log(k,2)+1) #number of attempts max
     r_ = r
     while r>0:
        #randrange is mersenne twister and is completely deterministic
        #unusable for serious crypto purposes
         n = random.randrange(2**(k-1),2**(k))
         r-=1
         if isPrime(n) == True:
             return n
     return -1

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

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

#-----------------------------------------------------------------

def DL_Param_Generator(small_bound, large_bound):
	s = small_bound.bit_length()
	l = large_bound.bit_length()
	q = generateLargePrime(s)
	while q == -1:
		q = generateLargePrime(s)

	p = 1

	while not (pyprimes.isprime(p) and p.bit_length() == l):
			r = random.getrandbits(l-s)
			p = q * r + 1
			if p.bit_length() == l-1:
				p = q*(r<<1)+1
			elif p.bit_length() == l+1:
				p = q*(r>>1)+1

	h = 2
	g = 1
	while g == 1:
		g = pow(h, (p-1)/q, p);
		h += 1

	#F=open("DSA_params.txt", 'w')
	#F.write(str(q)+'\n'+str(p)+'\n'+str(g)+'\n')
	#F.close()

	return (q, p, g)

def KeyGen(p, q, g):
	alpha = random.randint(0,q-1);
	beta = pow(g,alpha,p);
	#file = open("DSA_skey.txt", 'w')
	#file.write(str(q)+'\n'+ str(p)+'\n'+ str(g)+'\n'+ str(alpha)+'\n')
	#file.close()

	#file2 = open("DSA_pkey.txt", 'w')
	#file2.write(str(q)+'\n'+str(p)+'\n'+str(g)+'\n'+str(beta)+'\n')
	#file2.close()

	return (alpha, beta)

def SignGen(m, p, q, g, alpha, beta):
	h = int(hashlib.sha3_256(m).hexdigest(), 16)
	h = h % q
	k = random.randint(0,q-1);
	r = pow(g,k,p) % q
	s = ( alpha*r + k*h ) % q
	return (r,s)

def SignVer(m, r, s, p, q, g, beta):

	h = int(hashlib.sha3_256(m).hexdigest(), 16)
	h = pow(h, 1, q)
	v = modinv(h,q)
	z1 = pow(s*v, 1, q)
	z2 = pow((q-r)*v, 1, q)
	u =  pow(g,z1,p)*pow(beta,z2,p) % p

	if r == u%q:
		return 1;
	else:
		return 0;

"""
def create_single_transaction(message):

	q, p, g = DL_Param_Generator(1 << 256, 1 << 2048)
	alpha, beta = KeyGen(q, p, g);
	r, s = SignGen(message, q, p, g, alpha);

	ff = open("SingleTransaction.txt", 'w')
	ff.write("*** Bitcoin transaction ***\n")
	ff.write("Serial number: " + str(random.getrandbits(128)) + "\n")
	ff.write("Payer: " + id_generator() + "\n")
	ff.write("Payee: " + id_generator() + "\n")
	ff.write("Amount: " + str(random.randint(0, 1000)) + " Satoshi\n")
	ff.write("p: " + str(p) + '\n')
	ff.write("q: " + str(q) + '\n')
	ff.write("g: " + str(g) + '\n')
	ff.write("Public Key (beta): " + str(beta) + '\n')
	ff.write("Signature (r): " + str(r) + '\n')
	ff.write("Signature (s): " + str(s)  + '\n')
	ff.close()
"""


import circlify
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.patches as mpatches
from matplotlib.legend_handler import HandlerPatch

def College(name, short = True):
    name = "".join(i.lower() for i in name if i.isalpha())
    if name in ('a', 'collegeofdesign', 'cod', 'design'):
        if short:
            return 'COD'
        else:
            return 'College of Design'
    if name in ('s', 'collegeofsciences', 'cos', 'sciences'):
        if short:
            return 'COS'
        else:
            return 'College of Sciences'
    if name in ('c', 'collegeofcomputing', 'coc', 'computing'):
        if short:
            return 'COC'
        else:
            return 'College of Computing'
    if name in ('e', 'collegeofengineering', 'coe', 'engineering'):
        if short:
            return 'COE'
        else:
            return 'College of Engineering'
    if name in ('m', 'schellercollegeofbusiness', 'scob', 'business'):
        if short:
            return 'SCOB'
        else:
            return 'Scheller College of Business'
    if name in ('i', 'ivanallencollege', 'iac', 'ivan'):
        if short:
            return 'IAC'
        else:
            return 'Ivan Allen College'
    else:
        return 'Other'

def Children (source, school):
    data = source[school]
    alist = ['COD', 'COE', 'COC', 'IAC', 'SCOB', 'COS']
    l_dict = {}
    return_dict = {}
    return_dict['id'] = College(school, False)
    return_dict['datum'] = data['Enroll']
    children = []
    lost = 0
    gained = 0
    for i in data.keys():
        if i == 'Admit':
            temp_dict = {}
            temp_dict['id'] = 'Admitted'
            temp_dict['datum'] = data[i]
            children.append(temp_dict)
        if i in alist and i != school:
            lost -= data[i]
        if i == 'Enroll':
            temp_dict = {}
            temp_dict['id'] = 'Enroll'
            temp_dict['datum'] = data[i]
            children.append(temp_dict)
        if i == school:
            temp_dict = {}
            temp_dict['id'] = 'Gained'
            temp_dict['datum'] = data['Enroll'] - data[i]
            children.append(temp_dict)
    l_dict['id'] = 'Lost'
    l_dict['datum'] = abs(lost)
    children.append(l_dict)
    return_dict['children'] = children
    return return_dict

def get_data(source_file):
    infile = open(source_file , 'r')
    header = infile.readline()
    data = infile.readlines()
    data_list = []
    return_list = []
    enrollment_dict = {}
    school_set = set()
    for line in data:
        line_pieces = line.strip().split(',')
        in_college = line_pieces[1]
        out_college = line_pieces[-1]
        in_code = College(in_college, True)
        school_set.add(in_code)
        out_code = College(out_college, True)
        if in_code not in enrollment_dict:
            enrollment_dict[in_code] = {}
            enrollment_dict[in_code]['COD'] = 0
            enrollment_dict[in_code]['COS'] = 0
            enrollment_dict[in_code]['COE'] = 0
            enrollment_dict[in_code]['IAC'] = 0
            enrollment_dict[in_code]['SCOB'] = 0
            enrollment_dict[in_code]['COC'] = 0
            enrollment_dict[in_code][out_code] += 1
            enrollment_dict[in_code]['Admit'] = 1
            enrollment_dict[in_code]['Enroll'] = 0
        else:
            enrollment_dict[in_code][out_code] += 1
            enrollment_dict[in_code]['Admit'] += 1
    for line in data:
        line_pieces = line.strip().split(',')
        out_code = College(line_pieces[-1], True)
        enrollment_dict[out_code]['Enroll'] += 1
    for school in enrollment_dict.keys():
        data_list.append(Children(enrollment_dict, school))
    return_dict = {}
    return_dict['id'] = 'Georgia Tech'
    return_dict['datum'] = 9000
    return_dict['children'] = data_list
    return_list.append(return_dict)
    return return_list

def Format(name, Color = True):
    if name == 'College of Design':
        if Color:
            return '#B2BEB5'
        else:
            return (x-0.005,y+0.185)
    if name == 'College of Sciences':
        if Color:
            return '#36454F'
        else:
            return (x-0.01,y+0.345)
    if name == 'College of Computing':
        if Color:
            return '#7393B3'
        else:
            return (x-0.02,y-0.43)
    if name == 'College of Engineering':
        if Color:
            return '#6082B6'
        else:
            return (x,y-0.6)
    if name == 'Scheller College of Business':
        if Color:
            return '#A9A9A9'
        else:
            return (x,y-0.24)
    if name == 'Ivan Allen College':
        if Color:
            return '#818589'
        else:
            return (x-0.01,y+0.22)
    if name == 'Admitted':
        return '#B3A369'
    if name == 'Enroll':
        return '#003057'
    if name == 'Gained':
        return '#2961FF'
    if name == 'Lost':
        return '#E04F39'
    else:
        return '#003057'

file = 'Major Change Viz.csv'
data = get_data(file)

circles = circlify.circlify(
    data,
    show_enclosure=False,
    target_enclosure=circlify.Circle(x=0, y=0, r=1)
)

fig, ax = plt.subplots(figsize=(20,20))
fig.patch.set_facecolor('#F9F6E5')

ax.set_title('Incoming First-Year Students AY2024 by College', fontsize = 36)

ax.axis('off')

lim = max(
    max(
        abs(circle.x) + circle.r,
        abs(circle.y) + circle.r,
    )
    for circle in circles
)
plt.xlim(-lim, lim)
plt.ylim(-lim, lim)

for circle in circles:
    if circle.level != 2:
      continue
    x, y, r = circle
    ax.add_patch( plt.Circle((x, y), r, alpha=0.25, linewidth=2, color= Format(circle.ex['id'])))

for circle in circles:
    if circle.level != 3:
      continue
    x, y, r = circle
    label = circle.ex["id"]
    label2 = circle.ex['datum']
    ax.add_patch( plt.Circle((x, y), r, alpha=1, linewidth=2, color= Format(label)))
    plt.annotate(label2, (x,y-0.01 ), ha='center', color="white", fontsize=22)

for circle in circles:
    if circle.level != 2:
      continue
    x, y, r = circle
    label = circle.ex["id"]
    plt.annotate(label, Format(label, False) ,va='center', ha='center', fontsize = 18, bbox=dict(facecolor='white', edgecolor='black', boxstyle='round', pad=.5))

colors = ["#B3A369", "#003057", "#E04F39", "#2961FF"]
texts = ["Admitted Into College", "Confirmed in College", "Students Lost", "Students Gained"]
class HandlerEllipse(HandlerPatch):
    def create_artists(self, legend, orig_handle,
                       xdescent, ydescent, width, height, fontsize, trans):
        center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
        p = mpatches.Ellipse(xy=center, width=height + xdescent,
                             height=height + ydescent)
        self.update_prop(p, orig_handle, legend)
        p.set_transform(trans)
        return [p]

c = [mpatches.Circle((1, 1), radius = 0.5, facecolor=colors[i], edgecolor="none" ) for i in range(len(texts))]
plt.legend(c, texts,bbox_to_anchor=(0.2, 0.86), edgecolor = '#003057', loc='lower right', ncol=1, fontsize = 18, labelcolor = ["#B3A369", "#003057", "#E04F39", "#2961FF"], handler_map={mpatches.Circle: HandlerEllipse()}).get_frame().set_facecolor('white')
ax.annotate('"Lost" refers to a student who was admitted into that College but confirmed a major in another College', xy = (0.18, 0), xycoords='axes fraction', fontsize = 14)
plt.savefig('bubbles.png', bbox_inches='tight')


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

"""
Implements messages returned as the result
of the command
"""
from telegram import Update
from bot.commands.base import BaseView
from bot.bot import commands

class View(BaseView):

    @classmethod
    def help_message(cls, context: Update):

        command_list = ''

        for key in sorted(commands.keys()):
            command = commands[key]
            command_list += '- {handler} : {about}\n'.format(handler=command.get_handler(), about=command.get_about())

        content = '''
Hola soy *Quirquincho* <')))~ :thumbs_up:

Soy el protector de las chauchas.

Ésta es mi lista de comandos:

''' + command_list

        return cls(content, context)


from modules.CommandMaker.Base import CommandBase
import wikipediaapi
import logging

class Wiki(CommandBase):
    def __init__(self,update,context) -> None:
        self.update = update
        self.context = context
        self.wikipedia = wikipediaapi.Wikipedia('All Me bot')

    async def run(self):

        query:str = " ".join(self.context.args)
        await self.update.effective_message.reply_text(f"Searching for {query}....")

        try:
            page = self.wikipedia.page(query)
            if page.exists():
                result:str = page.summary[:2024]
                await self.update.message.reply_text(f"According to wikipedia {result} \n For more:{page.fullurl}")

            else:
                await self.update.effective_message.reply_text("Sorry I couldn't find any info on this topic on wikipedia")

        except Exception as e:
            logging.error(e)
            await self.update.effective_message.reply_text("Sorry I couldn't find any info on this topic on wikipedia")


import  os
import json
if os.path.exists('weather.json'):    # 在本地文件取json
    f = open('weather.json', encoding='utf-8')
    city_list = json.loads(f.read())
    for x in range(len(city_list)):
        a = len(city_list)
        # 取到值，然后 根据键名找数据
        city_code = city_list[x]["city_code"]
        city_name = city_list[x]['city_name']
        # 创建个字典，往字典里添加数据
        dict_city = {
            city_name:int(city_code),
        }
        print(dict_city)
        # 创建文件，写入数据
        c = open('list_city','a',encoding='utf-8')
        #
        c.write(str(dict_city).replace('{','').replace('}','')+',')
        c.close()


from ogspy.base import OGS_File

class MMP(OGS_File):

    """
    Class for the ogs medium property file.

    History
    -------
    Written,  JM, Jul 2015
    Modified, FH, Jul 2015 - rewritten for geneneral purpose ogs projects

    """

    def __init__(self, **OGS_Config):
        '''
        Input
        ----------

        OGS_Config dictonary

        '''

        OGS_File.__init__(self, **OGS_Config)

        self.f_type = '.mmp'

        self.geo_dim = []
        self.geo_area = []
        self.storage_flag = []
        self.storage = []
        self.per_sat_flag = []
        self.per_sat = []
        self.per_tensor = []
        self.per_dist = []
        self.porosity_flag = []
        self.porosity = []
        self.tortuosity_flag = []
        self.tortuosity = []
        self.mass_disp_flag = []
        self.mass_disp_x = []
        self.mass_disp_y = []
        self.mass_disp_z = []
        self.density_flag = []
        self.density = []
        self.block_no = 0

    def add_block(self, geo_dim = None, geo_area = '',
                  storage_flag = '1', storage = '',
                  per_sat_flag = '1', per_sat = '',
                  per_tensor = None, per_dist = '',
                  porosity_flag = '1', porosity = '',
                  tortuosity_flag = '1', tortuosity = '',
                  mass_disp_flag = '1',
                  mass_disp_x = '', mass_disp_y = '', mass_disp_z = '',
                  density_flag = '1', density = ''):

        self.geo_dim.append(str(geo_dim))
        self.geo_area.append(str(geo_area))
        self.storage_flag.append(str(storage_flag))
        self.storage.append(str(storage))
        self.per_sat_flag.append(str(per_sat_flag))
        self.per_sat.append(str(per_sat))
        self.per_tensor.append(str(per_tensor))
        self.per_dist.append(str(per_dist))
        self.porosity_flag.append(str(porosity_flag))
        self.porosity.append(str(porosity))
        self.tortuosity_flag.append(str(tortuosity_flag))
        self.tortuosity.append(str(tortuosity))
        self.mass_disp_flag.append(str(mass_disp_flag))
        self.mass_disp_x.append(str(mass_disp_x))
        self.mass_disp_y.append(str(mass_disp_y))
        self.mass_disp_z.append(str(mass_disp_z))
        self.density_flag.append(str(density_flag))
        self.density.append(str(density))

        self.block_no += 1

    def gen_f_str(self):

        f_out = ''
        for block_i in range (0, self.block_no):

            f_out += '#MEDIUM_PROPERTIES' + self.eol

            f_out += self.sid + '$GEOMETRY_DIMENSION' + self.eol
            f_out += self.did + self.geo_dim[block_i] + self.eol

            if self.geo_area[block_i]:
                f_out += self.sid + '$GEOMETRY_AREA' + self.eol
                f_out += self.did + self.geo_area[block_i] + self.eol

            f_out += self.sid + '$STORAGE' + self.eol
            f_out += self.did + self.storage_flag[block_i] + ' '
            f_out += self.storage[block_i] + self.eol

            if self.per_sat[block_i]:
                f_out += self.sid + '$PERMEABILITY_SATURATION' + self.eol
                f_out += self.did + self.per_sat_flag[block_i] + ' '
                f_out += self.per_sat[block_i] + self.eol

            f_out += self.sid + '$PERMEABILITY_TENSOR' + self.eol
            f_out += self.did + self.per_tensor[block_i] + self.eol

            if self.per_dist[block_i]:
                f_out += self.sid + '$PERMEABILITY_DISTRIBUTION' + self.eol
                f_out += self.did + self.per_dist[block_i] + self.eol

            if self.porosity[block_i]:
                f_out += self.sid + '$POROSITY' + self.eol
                f_out += self.did + self.porosity_flag[block_i] + ' '
                f_out += self.porosity[block_i] + self.eol

            if self.tortuosity[block_i]:
                f_out += self.sid + '$TORTUOSITY' + self.eol
                f_out += self.did + self.tortuosity_flag[block_i] + ' '
                f_out += self.tortuosity[block_i] + self.eol

            if self.mass_disp_x[block_i]:
                f_out += self.sid + '$MASS_DISPERSION' + self.eol
                f_out += self.did + self.mass_disp_flag[block_i]
                if self.mass_disp_x[block_i]:
                    f_out += ' ' + self.mass_disp_x[block_i]
                if self.mass_disp_y[block_i]:
                    f_out += ' ' + self.mass_disp_y[block_i]
                if self.mass_disp_z[block_i]:
                    f_out += ' ' + self.mass_disp_z[block_i]
                f_out += self.eol

            if self.density[block_i]:
                f_out += self.sid + '$DENSITY' + self.eol
                f_out += self.did + self.density_flag[block_i] + ' '
                f_out += self.density[block_i] + self.eol

        f_out = f_out + '#STOP'

        self.f_str = f_out


# Imageio library simplifies reading images, like removing headers
# imageio returns a numpy array
import imageio.v3 as iio
# PIL allows me to quickly remove alpha values
from PIL import Image
# numpy arrays are in general faster than python lists, good for reading 4k images
import numpy
from math import sqrt

def save(img):
    x = img
    namefnew = input("Enter new name of the file: ")
    while True:
        print("Choose extension to store file as: ")
        print("1.JPG (Alpha data will be lost, smaller file size)")
        print("2.PNG (Alpha data will be retained, larger file size)")
        temp1 = input("Choose 1 or 2 ?: ")
        if temp1 == "1":
            img = Image.fromarray(img).convert("RGB")
            img = numpy.array(img)
            newext = ".jpg"
            break
        elif temp1 == "2":
            newext = ".png"
            break
        print("That is not an option. Please try again.")
    newname = namefnew + newext
    iio.imwrite(newname,img)

def blurimage(img,height,width,type):
    tempimage = img.copy()
    sumR = 0
    sumG = 0
    sumB = 0
    for i in range(0,height):
        for j in range(0,width):
            if i == 0 and j == 0: #Top left Corner
                for k in range(0,4):
                    for l in range(0,4):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 4.00)
                img[i][j][1] = round(sumG / 4.00)
                img[i][j][2] = round(sumB / 4.00)
            elif i == 0 and j > 0 and j < width - 1: # Top edge
                for k in range(0,2):
                    for l in range(j-1,j+2):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 6.00)
                img[i][j][1] = round(sumG / 6.00)
                img[i][j][2] = round(sumB / 6.00)

            elif j == 0 and i > 0 and i < height - 1: # Left edge
                for k in range(i-1,i+2):
                    for l in range(0,2):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 6.00)
                img[i][j][1] = round(sumG / 6.00)
                img[i][j][2] = round(sumB / 6.00)

            elif j == 0 and i == height - 1: # Bottom Left corner
                for k in range(i-1,i+1):
                    for l in range(j,j+2):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 4.00)
                img[i][j][1] = round(sumG / 4.00)
                img[i][j][2] = round(sumB / 4.00)
            elif i == 0 and j == width - 1: # Top right corner
                for k in range(0,2):
                    for l in range(j-1,j+1):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 4.00)
                img[i][j][1] = round(sumG / 4.00)
                img[i][j][2] = round(sumB / 4.00)

            elif i == height - 1 and j == width - 1: # Bottom right Corner
                for k in range(i-1,i+1):
                    for l in range(j-1,j+1):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 4.0)
                img[i][j][1] = round(sumG / 4.0)
                img[i][j][2] = round(sumB / 4.0)

            elif i == height - 1 and j > 0 and j < width - 1: # Bottom edge
                for k in range(i-1,i+1):
                    for l in range(j-1,j+2):

                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 6.00)
                img[i][j][1] = round(sumG / 6.00)
                img[i][j][2] = round(sumB / 6.00)

            elif j == width - 1 and i > 0 and i < height - 1: # Right edge
                for k in range(i-1,i+2):
                    for l in range(j-1,j+1):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 6.00)
                img[i][j][1] = round(sumG / 6.00)
                img[i][j][2] = round(sumB / 6.00)

            else:
                for k in range(i-1,i+2):
                    for l in range(j-1,j+2):
                        sumR = sumR + tempimage[k][l][0]
                        sumG = sumG + tempimage[k][l][1]
                        sumB = sumB + tempimage[k][l][2]

                img[i][j][0] = round(sumR / 9.00)
                img[i][j][1] = round(sumG / 9.00)
                img[i][j][2] = round(sumB / 9.00)

            sumR = 0
            sumG = 0
            sumB = 0

nameold = str(input("Enter name of the file with the file extension: "))
temp1 = iio.imread(nameold)
img = temp1.copy()
height = img.shape[0]
width = img.shape[1]
type = img.shape[2]
print()
blurimage(img,height,width,type)
save(img)


from tkinter import *
from tkinter.font import Font
import time
from PIL import ImageTk, ImageSequence, Image
from threading import Thread
import os
import pyttsx3 as tts
import datetime as dt
import time
import listen_n_speak as lns

# WELCOME SPLASH SCREEN

window = Tk()
window.title("Shaurya - Personal Assistant")
window.config(background="grey")
window.geometry("600x300+400+200")
window.resizable(0,0)
window.overrideredirect(True)

def greet(user):
    """ This Function greets the user whenever the user logins to the Shaurya Assistant. """

    hour = dt.datetime.now().hour

    daytime = ""
    if hour >= 0 and hour < 12:
        daytime = "good morning"
    elif hour >= 12 and hour < 17:
        daytime = "good afternoon"
    elif hour >= 17 and hour < 19:
        daytime = "good evening"
    elif hour >= 19 and hour < 24:
        daytime = "good night"

    greet = f" {daytime} {user}, please wait, i am getting ready. "
    lns.speak(greet)

def clear_frame():
    global welcome_screen
    for widgets in welcome_screen.winfo_children():
        widgets.destroy()

def speak(audio_data):
    """Speak the string value"""
    try:
        audio_engine = tts.init()
        voices = audio_engine.getProperty('voices')
        audio_engine.setProperty("voice", voices[0].id)
        audio_engine.setProperty("rate", 160)
    except:
        pass
    audio_engine.say(audio_data)
    audio_engine.runAndWait()

def proceed():
    Thread(target = os.startfile, args=("shaurya.py",)).start()
    time.sleep(8)
    Thread(target = clear_frame, args=()).start()
    window.destroy()

def gifimg(label, imgs, refresh_rate=0):
    """Animation : Play Gifs"""
    print("gifimg() called....")

    try:
        while True:
            for img in ImageSequence.Iterator(imgs):
                img_frame = ImageTk.PhotoImage(img)
                label.config(image = img_frame)
                window.update()
                time.sleep(0.045)
    except:
        pass

def next_frame():
    welcome_screen_frame()

label_img=PhotoImage(file="./images/label images/welcome_label.png")
btn_img=PhotoImage(file="./images/button images/takeoff_button.png")

def welcome_screen_frame():
    global welcome_screen
    global label_img
    global btn_img
    global user_name

    welcome_screen = Frame(window, background="white")
    welcome_screen.place(x=300,y=0, width=300, height=300)

    label1_text_font = Font(family = "RocknRoll One",size = 14, weight='normal')
    label2_text_font = Font(family = "RocknRoll One",size = 12, weight='normal')

    uname = user_name.get()
    uname = uname.split(" ")
    textlabel1=Label(welcome_screen, background="white", text=f"Hi {uname[0]}, I am Shaurya", foreground="#161467", font=label1_text_font)
    textlabel1.place(x=0, y=50, width=280, height=40)

    textlabel2=Label(welcome_screen, background="white", text="I am Your Personal Assistant", foreground="#161467", font=label2_text_font)
    textlabel2.place(x=0, y=90, width=280, height=40)

    textlabel3=Label(welcome_screen, image=label_img, background="white")
    textlabel3.place(x=0, y=140, width=280, height=40)

    greet(uname[0])
    time.sleep(1)
    statement=f"Hi {uname[0]}, I am Shaurya, your personal assistant. ready for the takeoff. lets go"
    speak_thread=Thread(target=speak, args=(statement,))
    speak_thread.start()

    proceed_btn = Button(welcome_screen, background="white", image=btn_img, activebackground="white", borderwidth=0, relief=FLAT,command = proceed)
    proceed_btn.place(x=60, y=200, width=200, height=40)

def user_intro_screen_frame():
    global user_intro_screen
    global label_img
    global btn_img
    global user_name

    user_name = StringVar()

    user_intro_screen = Frame(window, background="white")
    user_intro_screen.place(x=0,y=0, width=600, height=300)

    label1_text_font = Font(family = "RocknRoll One",size = 15, weight='bold')
    label2_text_font = Font(family = "RocknRoll One",size = 14, weight='normal')
    entry_text_font = Font(family = "RocknRoll One",size = 13, weight='normal')
    close_btn_font = Font(family = "RocknRoll One",size = 15, weight='bold')
    btn_text_font = Font(family = "RocknRoll One",size = 13, weight='normal')

    textlabel1=Label(user_intro_screen, background="white", text="Shaurya Assistant", foreground="#161467", font=label1_text_font)
    textlabel1.place(x=300, y=30, width=280, height=40)

    close_btn = Button(user_intro_screen, bg="#161467", fg= "white", activebackground="#161467", activeforeground="white", text = 'X', font=close_btn_font, command= window.destroy)
    close_btn.place(x=550, y=5, width=55, height=30)

    textlabel1=Label(user_intro_screen, background="white", text="Welcome, Lets Move...", foreground="#161467", font=label2_text_font)
    textlabel1.place(x=300, y=70, width=280, height=40)

    name_btn = Button(user_intro_screen, text = "Take My Name", fg="white", activeforeground="white",  background="#161467", activebackground="#1091ca", borderwidth=0, relief=FLAT, font = btn_text_font,  command= next_frame, state = DISABLED)
    name_btn.place(x=370, y=200, width=150, height=40)

    name_entry = Entry(user_intro_screen, textvariable= user_name, background="#1091ca", foreground="white", font=entry_text_font, relief= FLAT)
    name_entry.place(x=300, y=135, width=280, height=40)
    name_entry.bind('<FocusIn>', lambda e: name_btn.config(state=NORMAL))
    name_entry.bind('<Return>', lambda e: next_frame())
    name_entry.bind('<Button>', lambda e: user_name.set(""))
    user_name.set("  Your Name...")

    statement="Hi user, can you please tell me your name?"
    speak_thread=Thread(target=speak, args=(statement,))
    speak_thread.start()

    giflabel=Label(user_intro_screen, background="white")
    giflabel.place(x=0, y=22, width=300, height=225)

    imgpath = "./images/gifs/takeoff.gif"
    resolution = (300,225)
    imgs=Image.open(imgpath)
    imgs.resize(resolution)
    gifimg(giflabel, imgs)

user_intro_screen_frame()

window.mainloop()
mainloop()


"""
Sample Input:
aabbbccde

Sample Output:
b 3
a 2
c 2
"""

from collections import Counter, OrderedDict
from fileinput import input

class OrderedCounter(Counter, OrderedDict):
    def __init__(self, items):
        super(OrderedCounter, self).__init__(sorted(items))

def main(string):
    for letter, count in OrderedCounter(string).most_common(3):
        print(letter, count)

if __name__ == '__main__':
    main('aabbbccde')


from django.forms import widgets, ModelForm

from exchangeapp.models import MyExchange, ForeignCurrency, ForeignCurrencyTransaction

class MyExchangeCreationForm(ModelForm):
    class Meta:
        model = MyExchange
        fields = ['main_currency']

class ForeignCurrencyCreationForm(ModelForm):
    class Meta:
        model = ForeignCurrency
        fields = ['currency']

class ForeignCurrencyTransactionCreationForm(ModelForm):
    class Meta:
        model = ForeignCurrencyTransaction
        fields = ['transaction_type', 'quantity', 'exchange_rate', 'transaction_date', 'note']

        widgets = {
            'transaction_date': widgets.DateTimeInput(attrs={'type': 'date'}),
        }


# This is an exercise about how to use dictionary
# 6-1
person = {
    'first_name': 'Zongrui',
    'last_name': 'Li',
    'age': 23,
    'city': 'Singapore'
}
print(person)

# 6-2
favorite_number = {
    'Tide': 8,
    'Zhu Liang': 2,
    'Huang Zhikai': 7,
    'Zhao Jiawei': 10,
    'Yang Hao': 24
}
print(favorite_number)

# 6-3
word_python = {
    '字典': '字典是一系列键-值对。',
    '元组': '元组是不可变的列表。',
    '列表': '列表是一系列按特定顺序排列的元素。'
}

# 6-4
for key, value in word_python.items():
    print(key + ': ' + value)

# 6-5
rivers = {
    'nile': 'egypt',
    'rhine': 'switzerland',
    'chang': 'china'
}

for river, country in rivers.items():
    print('The ' + river.title() + ' runs through ' + country.title())

# 6-6
# 6-7
person_0 = {
    'first_name': 'Zongrui',
    'last_name': 'Li',
    'age': 23,
    'city': 'Singapore'
}

person_1 = {
    'first_name': 'Hao',
    'last_name': 'Yang',
    'age': 23,
    'city': 'Baiyin'
}

person_2 = {
    'first_name': 'Liang',
    'last_name': 'Zhu',
    'age': 23,
    'city': 'Changzhou'
}

persons = [person_0, person_1, person_2]

for person in persons:
    print(person)

# 6-8
pet_0 = {
    'kind': 'dog',
    'master': 'Li'
}

pet_1 = {
    'kind': 'cat',
    'master': 'Yang'
}

pet_2 = {
    'kind': 'akai',
    'matser': 'Zhu'
}

pets = [pet_0, pet_1, pet_2]
for pet in pets:
    print(pet)

# 6-9
favorite_places = {
    'Li': ['Jinsha', 'Shanghai', 'Singapore'],
    'Yang': ['Baiyin', 'Hengshui', 'Shanghai'],
    'Zhu': ['Changzhou', 'Shanghai']
}

for person, places in favorite_places.items():
    print(f"{person}'s favorite places are: ")
    for place in places:
        print(f' {place}')

# 6-10
favorite_number = {
    'Tide': [8, 24],
    'Zhu Liang': [2, 10],
    'Huang Zhikai': [7, 14],
    'Zhao Jiawei': [10, 58],
    'Yang Hao': [24,100]
}
for person, numbers in favorite_number.items():
    print(f"{person}'s favorite numbers are: ")
    for number in numbers:
        print(number)

# 6-11
cities = {
    'guiyang': {'country': 'china', 'population': 5987018},
    'shanghai': {'country': 'china', 'population': 24894300},
    'singapore': {'country': 'sinagpore', 'population': 5866100}
}

for city, fact in cities.items():
    print(f"{city.title()} is located in {fact['country'].title()}, and the population is {fact['population']}.")


from __future__ import annotations

from collections.abc import Iterable
import copy
from typing import List

import build123d as bd

from .topology import *
from .utils import to_list

__all__ = ["SkipClean", "Copy", "LazyAlgCompound", "AlgCompound", "create_compound"]

CTX = [None, bd.BuildLine, bd.BuildSketch, bd.BuildPart]

#
# SkipClean context
#

class SkipClean:
    clean = True

    def __enter__(self):
        SkipClean.clean = False
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        SkipClean.clean = True

class Copy:
    shallow = False

    def __enter__(self):
        Copy.shallow = True

    def __exit__(self, exception_type, exception_value, traceback):
        Copy.shallow = False

#
# Algebra operations enhanced Compound
#

def unwrap(compound: Compound) -> Compound:
    """remove enclosing Compound if it only holds one other Compund"""
    if (
        isinstance(compound, Compound)
        and isinstance(compound, Iterable)
        and len(list(compound)) == 1
        and isinstance(list(compound)[0], Compound)
    ):
        return list(compound)[0]
    else:
        return compound

class AlgCompound(Compound):
    def __init__(self, obj: Union[Compound, Solid, Face, Edge] = None, label=None):
        if isinstance(obj, Compound):
            objs = list(unwrap(obj))
        elif isinstance(obj, (Solid, Face, Edge, Wire)):
            objs = [obj]
        elif isinstance(obj, (tuple, list)):
            objs = obj
        elif obj is not None:
            raise TypeError(f"Unknown type {obj}")

        if obj is None:
            dummy = Solid.make_sphere(1)
            super().__init__(Compound.make_compound([dummy]).wrapped, label=label)
            self._dim = 0
            self.wrapped = None

        elif all([isinstance(obj, (Edge, Wire)) for obj in objs]):
            super().__init__(Compound.make_compound(objs).wrapped, label=label)
            self._dim = 1

        elif all([isinstance(obj, Face) for obj in objs]):
            super().__init__(Compound.make_compound(objs).wrapped, label=label)
            self._dim = 2

        elif all([isinstance(obj, Solid) for obj in objs]):
            super().__init__(Compound.make_compound(objs).wrapped, label=label)
            self._dim = 3
            self._coord = Location()
            self.metadata = {}

        else:
            raise RuntimeError(f"{objs} not supported")

    @classmethod
    def make_compound(cls, objs: Shape):
        compound = Compound.make_compound(objs)
        return cls(compound)

    def _create(self, ctx, cls, objects=None, part=None, params=None):
        if params is None:
            params = {}

        with ctx() as c:
            if part is not None:
                c._add_to_context(part)

            if objects is None:
                result = cls(**params, mode=Mode.PRIVATE)
            else:
                result = cls(*list(objects), **params, mode=Mode.PRIVATE)

        return result

    def _align(
        self, align: Union[Align, Tuple(Align, Align), Tuple(Align, Align, Align)]
    ) -> AlgCompound:
        if align is not None:
            if isinstance(align, Align):
                align = (align,) * self._dim

            bbox = self.bounding_box()
            align_offset = []
            for i in range(self._dim):
                if align[i] == Align.MIN:
                    align_offset.append(-bbox.min.to_tuple()[i])
                elif align[i] == Align.CENTER:
                    align_offset.append(
                        -(bbox.min.to_tuple()[i] + bbox.max.to_tuple()[i]) / 2
                    )
                elif align[i] == Align.MAX:
                    align_offset.append(-bbox.max.to_tuple()[i])
            self.move(Location(Vector(*align_offset)))

    def create_line(self, cls, objects=None, params=None):
        result = self._create(bd.BuildLine, cls, objects=objects, params=params)
        return Compound.make_compound(result.edges())

    def create_sketch(self, cls, objects=None, params=None):
        return self._create(bd.BuildSketch, cls, objects=objects, params=params)

    def create_part(self, cls, part=None, params=None):
        return self._create(bd.BuildPart, cls, part=part, params=params)

    def _place(self, mode: Mode, *objs: AlgCompound):
        objs = [o if isinstance(o, AlgCompound) else AlgCompound(o) for o in objs]

        if not (objs[0]._dim == 0 or self._dim == 0 or self._dim == objs[0]._dim):
            raise RuntimeError(
                f"Cannot combine objects of different dimensionality: {self._dim} and {objs[0]._dim}"
            )

        if self._dim == 0:  # Cover addition of empty AlgCompound with another object
            if mode == Mode.ADD:
                if len(objs) == 1:
                    compound = copy.deepcopy(objs[0])
                else:
                    compound = copy.deepcopy(objs.pop()).fuse(*objs)
            else:
                raise RuntimeError("Can only add to an empty AlgCompound object")
        elif objs[0]._dim == 0:  # Cover operation with empty AlgCompound object
            compound = self
        else:
            if mode == Mode.ADD:
                compound = self.fuse(*objs)

            elif self._dim == 1:
                raise RuntimeError("Lines can only be added")

            else:
                if mode == Mode.SUBTRACT:
                    compound = self.cut(*objs)
                elif mode == Mode.INTERSECT:
                    compound = self.intersect(*objs)

        if SkipClean.clean:
            compound = compound.clean()

        return AlgCompound(compound)

    def __add__(self, other: Union[AlgCompound, List[AlgCompound]]):
        return self._place(Mode.ADD, *to_list(other))

    def __sub__(self, other: Union[AlgCompound, List[AlgCompound]]):
        return self._place(Mode.SUBTRACT, *to_list(other))

    def __and__(self, other: Union[AlgCompound, List[AlgCompound]]):
        return self._place(Mode.INTERSECT, *to_list(other))

    def __matmul__(self, position):
        if self._dim == 1:
            return Wire.make_wire(self.edges()).position_at(position)
        else:
            raise TypeError("Only lines can access positions")

    def __mod__(self, position):
        if self._dim == 1:
            return Wire.make_wire(self.edges()).tangent_at(position)
        else:
            raise TypeError(f"Only lines can access positions")

    def __repr__(self):
        def r2(v):
            return tuple([round(e, 2) for e in v])

        if self._dim == 0:
            loc_str = "None"
        else:
            loc_str = f"(position={r2(self.location.position)}, rotation={r2(self.location.orientation)})"

        return f"obj={self.__class__.__name__}; loc={loc_str}; dim={self._dim}"

    def edge(self) -> Edge:
        if self._dim == 1:
            if len(self.edges()) == 1:
                return self.edges()[0]
            else:
                raise RuntimeError("Line has more than 1 edge")
        else:
            raise RuntimeError("edge() exists for dim==1 only")

    def wire(self) -> Wire:
        if self._dim == 1:
            return Wire.combine(self.edges())[0]
        else:
            raise RuntimeError("wire() exists for dim==1 only")

    def face(self) -> Face:
        if self._dim == 2:
            if len(self.faces()) == 1:
                return self.faces()[0]
            else:
                raise RuntimeError("Sketch has more than 1 face")
        else:
            raise RuntimeError("face() exists for dim==2 only")

    def solid(self) -> Solid:
        if self._dim == 3:
            if len(self.solids()) == 1:
                return self.solids()[0]
            else:
                raise RuntimeError("Part has more than 1 solid")
        else:
            raise RuntimeError("solid() exists for dim==3 only")

#
# Function wrapper
#

def create_compound(
    cls, objects=None, part=None, dim=None, faces=None, planes=None, params=None
) -> AlgCompound:
    if objects is None:
        objs = None
    else:
        if isinstance(objects, AlgCompound) and objects._dim == 1:
            objs = objects.edges()
            if dim is None:
                dim = 1 if part is None else part._dim
        elif isinstance(objects, (Wire, Edge)):
            objs = [objects]
            if dim is None:
                dim = 1 if part is None else part._dim
        elif isinstance(objects, (Face)):
            objs = [objects]
            if dim is None:
                dim = 2 if part is None else part._dim
        elif isinstance(objects, (Wire, Solid)):
            objs = [objects]
            if dim is None:
                dim = 3 if part is None else part._dim
        else:
            if isinstance(objects, (list, tuple)):
                objs = objects
            elif isinstance(objects, filter):
                objs = list(objects)
            else:
                objs = [objects]

            if dim is None:
                if part is None:
                    dim = max([o._dim for o in objs])
                else:
                    dim = part._dim

    with CTX[dim]() as ctx:
        if part is not None:
            if dim == 2:
                ctx.sketch_local = Compound.make_compound(part.faces()).clean()
            else:
                ctx._add_to_context(Compound(part.wrapped))

        if faces is not None:
            ctx.pending_faces = faces

        if planes is not None:
            ctx.pending_face_planes = planes

        compound = unwrap(cls(**params) if objs is None else cls(*objs, **params))

        if part is not None and dim != 2:
            compound = unwrap(ctx._obj)

    solids = compound.solids()
    if len(solids) > 1:
        result = AlgCompound(solids[0].fuse(*solids[1:]))
    else:
        result = AlgCompound(compound)

    if SkipClean.clean:
        return result.clean()
    else:
        return result

#
# LazyCompound
#

class LazyAlgCompound(AlgCompound):
    def __init__(self):
        super().__init__()

    def __enter__(self):
        self._collected_objects = []
        return self

    def __add__(self, other):
        if hasattr(self, "_collected_objects"):
            if other.solids():
                dim = 3
            elif other.faces():
                dim = 2
            else:
                dim = 1

            if self._dim == 0:
                self._dim = dim
            else:
                if dim != self._dim:
                    raise RuntimeError("Cannot add objects with different dimensions")

            self._collected_objects.append(other)
            return self

        else:
            return super().__add__(other)

    def __exit__(self, exception_type, exception_value, traceback):
        self.wrapped = (
            self._collected_objects.pop().fuse(*self._collected_objects).clean().wrapped
        )
        del self._collected_objects


import pytest
from tabela_fipe.FipeApi import FipeApi

@pytest.fixture(scope="module")
def fipe_api():
    return FipeApi()

def test_chamada_com_endpoint(fipe_api):
    marcas = fipe_api.get(
        "carros/marcas",
    )
    assert isinstance(marcas, list)

def test_chamada_url_completa(fipe_api):
    marcas = fipe_api.get(
        "https://parallelum.com.br/fipe/api/v1/carros/marcas",
    )
    assert isinstance(marcas, list)


import cv2 as cv
import numpy as np

# Load the image
img = cv.imread('Images/1.jpg')

#  Function to translate the image
def translate(image, x, y):
    # Define the translation matrix and perform the translation
    M = np.float32([[1, 0, x], [0, 1, y]])
    shifted = cv.warpAffine(image, M, (image.shape[1], image.shape[0]))
    return shifted

# Function to rotate the image
def rotate(image, angle, center=None, scale=1.0):
    # Get the dimensions of the image
    (h, w) = image.shape[:2]

    # If the center is None, initialize it as the center of the image
    if center is None:
        center = (w / 2, h / 2)

    # Perform the rotation
    M = cv.getRotationMatrix2D(center, angle, scale)
    rotated = cv.warpAffine(image, M, (w, h))
    return rotated

cv.waitKey(0)


#Saiss Cruz
#saiss.cruz10@myhunter.cuny.edu
#11/4/2021

def worldRecord(gender, eventDistance):
  """
  Takes as two parameters: the gender and the eventDistance.
  Returns the AMNH worldRecord, as follows:
  If the eventDistance is is "100" and the gender is "men", record is 9.63 seconds.
  If the eventDistance is is "200" and the gender is  "men", record is 19.30 seconds.
  If the eventDistance is is "400" and the gender is  "men", record is 43.03 seconds.
  If the eventDistance is is "100" and the gender is "women", record is 10.62 seconds.
  If the eventDistance is is "200" and the gender is  "women", record is 21.34 seconds.
  If the eventDistance is is "400" and the gender is  "women", record is 48.25 seconds.
  """

  men = {"100":9.63, "200":19.30, "400":43.03}
  women = {"100":10.62, "200":21.34, "400":48.25}

  if gender == "men":
    return men[eventDistance]
  elif gender == "women":
    return women[eventDistance]
  else:
    print("Error Encountered")

def main():
  a = input('Enter the gender (men, women): ')
  t = input('Enter the eventDistance (100,200,400): ')
  print(f'The world record is {worldRecord(a,t)}')

#Allow script to be run directly:
if __name__ == "__main__":
  main()


import ctmcommands.cmd
from ctmcommands.param import Param

class ReversionPackageRevision(ctmcommands.cmd.CSKCommand):

    Description = 'Changes the Version of the specified (and all matching previous) Package Revisions.'
    API = 'reversion_packagerevision'
    Examples = '''
    ctm-reversion-packagerevision -p "Some Package Name" -r 205 -v 15.1

    - or -

    ctm-promote-revision -p "Some Package Name" -f "15.2.3.205" -v 15.1

    - or -

    ctm-promote-revision -p "Some Package Name" -f "15.2.3.205" -v 15.1 -n 15.2.3.206
    '''

    Options = [Param(name='package', short_name='p', long_name='package',
                     optional=False, ptype='string',
                     doc='Name of the Package to promote to a Progression Phase.'),
               Param(name='revision', short_name='r', long_name='revision',
                     optional=True, ptype='string',
                     doc='Revision number of a Package to promote, takes precedence over "full_version".'),
               Param(name='full_version', short_name='f', long_name='full_version',
                     optional=True, ptype='string',
                     doc='Optional Full Version of package to promote, optional alternative selector to "revision".'),
               Param(name='new_version', short_name='v', long_name='new_version',
                     optional=False, ptype='string',
                     doc='Optional "new version" for this and all prior revisions of this Package.'),
               Param(name='new_full_version', short_name='n', long_name='new_full_version',
                     optional=True, ptype='string',
                     doc='Optional "new full version" for this and all prior revisions of this Package.')
               ]

    def main(self):
        results = self.call_api(self.API, ['package', 'revision', 'full_version', 'new_version', 'new_full_version'])
        print(results)


import numpy as np
from collections import defaultdict
from scipy.spatial.distance import euclidean
import math

def neighbours(x, y):
    offsets = [(-1, 0), (0, -1), (0, 1), (1, 0)]
    neigh = [(x + dx, y + dy) for dx, dy in offsets]
    return neigh

def neighbours_values(x, y, grid):
    if is_edge(x, y, grid.shape):
        return [-1]
    return [grid[nx, ny] for nx, ny in neighbours(x, y)]

def is_edge(x, y, shape):
    return x == 0 or y == 0 or (x == shape[0] - 1) or (y == shape[1] - 1)

def get_borders(countries):
    borders = defaultdict(lambda: np.zeros_like(countries))

    for (x, y), cid in np.ndenumerate(countries):
        edge = is_edge(x, y, countries.shape)
        nids = neighbours_values(x, y, countries)

        border = any([nid != cid for nid in nids])
        borders[cid][x, y] = edge or border

    return dict(borders)

def get_country_area_coords(countries, borders, cid):
    area = (countries == cid)
    area = area - borders[cid]
    return area.astype(int)

def get_closest(countries, borders, cid, cx, cy):
    distances = np.full_like(countries, np.inf, dtype=float)
    valid = get_country_area_coords(countries, borders, cid)
    for (x, y) in np.argwhere(valid == 1):
        distances[x, y] = euclidean([x, y], [cx, cy])

    minima = np.argwhere(distances == distances.min())
    closest = sorted(minima, key=lambda xy: (xy[1], xy[0]))[0]  # row, column sort (but transposed)
    return closest

def get_dominance(direction):  # 0: x, 1: y
    dx, dy = direction
    if dx > dy:
        return 0
    elif dy > dx:
        return 1
    else:
        return 0

def iter_in_dominance(cells, dominance):  # 0: x, 1: y
    if dominance == 1:
        return sorted(cells, key=lambda e: (e[0], e[1]))
    else:
        return sorted(cells, key=lambda e: (e[1], e[0]))

def gasser_kahlhofer(origin, direction, shape):
    ox, oy = origin
    dx, dy = direction

    maxx, maxy = shape

    cells = {(int(math.floor(ox)), int(math.floor(oy)))}

    px = int(math.ceil(ox))
    while px < maxx:
        lmb = (px - ox) / dx
        py = oy + lmb * dy

        is_cross = py % 1 == 0
        py = int(math.floor(py))

        if py >= maxy:
            break

        cells.add((px, py))
        if is_cross:
            cells.add((px, py - 1))

        px += 1

    py = int(math.ceil(oy))
    while py < maxy:
        lmb = (py - oy) / dy
        px = ox + lmb * dx

        is_cross = px % 1 == 0
        px = int(math.floor(px))

        if px >= maxx:
            break

        cells.add((px, py))
        if is_cross:
            cells.add((px - 1, py))

        py += 1

    # lmb = (px - ox) / dx
    # lmb = (py - oy) / dy

    # px = ox + lmb * dx
    # py = oy + lmb * dy

    return list(cells)

def solve(data):
    nrows, ncols, queries = data["nrows"], data["ncols"], data["queries"]

    solutions = []

    for query in queries:
        origin, direction = query
        origin = (origin[0] + 0.5, origin[1] + 0.5)
        gk = gasser_kahlhofer(origin, direction, [nrows, ncols])
        itered = iter_in_dominance(gk, get_dominance(direction))
        solutions.append(itered)

    return "\n".join([" ".join([f"{x} {y}" for x, y in xy]) for xy in solutions])


# Checks accuracy of predicitions.txt

pred = open('predictions.txt', "r")
f = open('iris.vw', "r")
numCorrect = 0
numAttempts = 0
predictions = []
test = []

for line in pred:
	predictions.append(line.split(None,1)[0])

for l in f:
	test.append(l.split(None,1)[0])

pred.close()
f.close()

for i in range(0,len(predictions)):
	if predictions[i] == test[i]:
		numCorrect = numCorrect+1
		numAttempts = numAttempts+1
	else:
		numAttempts = numAttempts+1

accuracy = (numCorrect/numAttempts)*100
print("Accuracy is: ", accuracy,"%")


import unittest
import sys
import re

from os import getcwd
from os.path import abspath, dirname, join

from logilab.common import testlib

from pylint.testutils import (make_tests, LintTestUsingModule, LintTestUsingFile,
    LintTestUpdate, cb_test_gen, linter, test_reporter)

PY3K = sys.version_info >= (3, 0)

# Configure paths
INPUT_DIR = join(dirname(abspath(__file__)), 'input')
MSG_DIR = join(dirname(abspath(__file__)), 'messages')

# Classes

quote = "'" if sys.version_info >= (3, 3) else ''

class LintTestNonExistentModuleTC(LintTestUsingModule):
    module = 'nonexistent'
    _get_expected = lambda self: 'F:  1: No module named %snonexistent%s\n' % (quote, quote)
    tags = testlib.Tags(('generated','pylint_input_%s' % module))

class TestTests(testlib.TestCase):
    """check that all testable messages have been checked"""
    PORTED = set(['I0001', 'I0010', 'W0712', 'E1001', 'W1402', 'E1310', 'E0202',
                  'W0711', 'W0108', 'E0603', 'W0710', 'E0710', 'E0711', 'W1001',
                  'E1124', 'E1120', 'E1121', 'E1123', 'E1003', 'E1002', 'W0212',
                  'W0109', 'E1004', 'W0604', 'W0601', 'W0602', 'C0112', 'C0330'])

    @testlib.tag('coverage')
    def test_exhaustivity(self):
        # skip fatal messages
        not_tested = set(msg.msgid for msg in linter.msgs_store.messages
                         if msg.msgid[0] != 'F' and msg.may_be_emitted())
        for msgid in test_reporter.message_ids:
            try:
                not_tested.remove(msgid)
            except KeyError:
                continue
        not_tested -= self.PORTED
        self.assertFalse(not_tested)

class LintBuiltinModuleTest(LintTestUsingModule):
    output = join(MSG_DIR, 'builtin_module.txt')
    module = 'sys'
    def test_functionality(self):
        self._test(['sys'])

def gen_tests(filter_rgx):
    if UPDATE:
        callbacks = [cb_test_gen(LintTestUpdate)]
    else:
        callbacks = [cb_test_gen(LintTestUsingModule)]
    tests = make_tests(INPUT_DIR, MSG_DIR, filter_rgx, callbacks)
    if UPDATE:
        return tests

    if filter_rgx:
        is_to_run = re.compile(filter_rgx).search
    else:
        is_to_run = lambda x: 1

    if is_to_run('nonexistent'):
        tests.append(LintTestNonExistentModuleTC)

    tests.append(LintBuiltinModuleTest)

    if not filter_rgx:
        # test all features are tested :)
        tests.append(TestTests)

    assert len(tests) < 196, "Please do not add new test cases here."
    return tests

# Create suite

FILTER_RGX = None
UPDATE = False

def suite():
    return testlib.TestSuite([unittest.makeSuite(test, suiteClass=testlib.TestSuite)
                              for test in gen_tests(FILTER_RGX)])

if __name__=='__main__':
    if '-u' in sys.argv:
        UPDATE = True
        sys.argv.remove('-u')

    if len(sys.argv) > 1:
        FILTER_RGX = sys.argv[1]
        del sys.argv[1]
    testlib.unittest_main(defaultTest='suite')


# Question: https://leetcode.com/problems/count-odd-numbers-in-an-interval-range/
# Easy

class Solution:
    # O(1) time and O(1) space
    def countOdds(self, low: int, high: int) -> int:
        if low == high: return int(low % 2 != 0)
        low += 1 if low % 2 == 0 else 0
        answer = ((high - low) // 2) + 1
        return answer

# February 13, 2023

'''

# Kunal Wadhwa

'''

Questions/[Math] Count Odd Numbers in an Interval Range.py


import asyncio
import argparse
import aiofiles
from aiofiles.os import path
from base64 import urlsafe_b64encode
import tqdm
from os.path import join as path_join

from main import create_filename, requester

async def download(base_dir: str, url: str):
    img_url = urlsafe_b64encode(url.encode('ascii')).rstrip(b'=')
    file_path = path_join(base_dir, create_filename(img_url).decode('ascii'))
    if await path.isfile(file_path):
        return
    await requester.save(img_url.decode('ascii'), file_path)

async def main():
    parser = argparse.ArgumentParser(description='Fetch images from text file')
    parser.add_argument('dest', help='base path to store images at')
    parser.add_argument('file', help='text file with links to import, one per line')
    parser.add_argument('--disable-parallel', '-x', action='store_true',
                        help='do not do multiple requests at the same time')
    args = parser.parse_args()

    if args.disable_parallel:
        worker_slots = 1
    else:
        worker_slots = 100
    sem = asyncio.Semaphore(worker_slots)
    success = []
    failed = []
    pbar = tqdm.tqdm(unit='req', postfix={'success': 0, 'failed': 0})

    def callback(task):
        try:
            task.result()
        except Exception:
            failed.append(task.get_name())
        else:
            success.append(task.get_name())
        finally:
            sem.release()
            pbar.update(1)
            pbar.set_postfix(success=len(success), failed=len(failed))

    i = 0
    async with aiofiles.open(args.file, 'r') as fh:
        async for line in fh:
            i += 1
            await sem.acquire()
            url = line.rstrip('\n')
            task = asyncio.create_task(download(args.dest, url))
            task.set_name(url)
            task.add_done_callback(callback)

    for x in range(worker_slots):
        # acquire all slots so no more tasks can be active
        await sem.acquire()

    pbar.close()

    async with aiofiles.open(args.file + '.success', 'w') as fh:
        await fh.writelines(x + '\n' for x in success)
    async with aiofiles.open(args.file + '.failed', 'w') as fh:
        await fh.writelines(x + '\n' for x in failed)

    print(f"all done. total {i}, success: {len(success)}, failed: {len(failed)}")

if __name__ == '__main__':
    asyncio.run(main())