from cravat.cravat_report import CravatReport
import sys
import datetime
import re
import pandas as pd
import cravat
import json
import pyreadr
import os

class Reporter(CravatReport):

    def setup (self):
        self.filenames = []
        self.filename = None
        self.filename_prefix = None
        if self.savepath == None:
            self.filename_prefix = "cravat_result"
        else:
            self.filename_prefix = self.savepath
        self.levels_to_write = self.get_standardized_module_option(
            self.confs.get("pages", ["variant"])
        )
        self.separate_header_file = (
            self.get_standardized_module_option(
                self.confs.get("separate-header-file", "true")
            )
            == True
        )
        self.zip = (
            self.get_standardized_module_option(self.confs.get("zip", "false")) == True
        )
        self.show_default_cols_only = (
            self.get_standardized_module_option(
                self.confs.get("show-default-columns-only", "true")
            )
            == True
        )
        self.cols_to_display = [
            'base__hugo',
            'base__chrom',
            'base__pos',
            'base__ref_base',
            'base__alt_base',
        ]
        self.colno_to_display_hugo = self.cols_to_display.index('base__hugo')
        self.colno_to_display_chrom = self.cols_to_display.index('base__chrom')
        self.colno_to_display_pos = self.cols_to_display.index('base__pos')
        self.colno_to_display_ref = self.cols_to_display.index('base__ref_base')
        self.colno_to_display_alt = self.cols_to_display.index('base__alt_base')
        self.colname_display_dict = {
            'base__hugo': 'group_id',
            'base__chrom': 'chr',
            'base__pos': 'pos',
            'base__ref_base': 'ref',
            'base__alt_base': 'alt',
        }
        self.display_select_columns = {}
        self.display_select_columns['variant'] = len(self.cols_to_display) > 0
        self.module_col_sep = "."
        self.colnos_to_display = {}
        self.colnames_to_display = {}
        if self.display_select_columns['variant'] == False and self.show_default_cols_only:
            db = sqlite3.connect(self.dbpath)
            c = db.cursor()
            q = f'select name from sqlite_master where name like "%_header"'
            c.execute(q)
            levels = [v[0].split("_")[0] for v in c.fetchall()]
            for level in levels:
                q = f"select col_name, col_def from {level}_header"
                c.execute(q)
                for row in c.fetchall():
                    (col_name, col_def) = row
                    col_def = json.loads(col_def)
                    if "hidden" not in col_def or col_def["hidden"] == False:
                        if col_name not in self.cols_to_display:
                            self.cols_to_display.append(col_name)
        self.headers = {}
        self.dataframe_cols = {}
        self.dataframe_colnos = {}
        self.dataframe_headers = {}
        self.colno_to_colname = {}
        self.filename_postfix = 'cfgenesis.RData'
        self.data = {}
        self.wgs_reader = cravat.get_wgs_reader('hg38')
        self.enstnov_ensgnov = {}
        data_path = os.path.dirname(os.path.abspath(__file__))
        # hugo synonyms
        f = open(os.path.join(data_path, 'data', 'hugo_synonyms.txt'))
        line = f.readline()
        toks = line.split('\t')
        app_symbol_colno = toks.index('Approved symbol')
        prev_symbols_colno = toks.index('Previous symbols')
        #alias_symbols_colno = toks.index('Alias symbols')
        self.hugo_synonyms = {}
        for line in f:
            toks = line.split('\t')
            app_symbol = toks[app_symbol_colno]
            prev_symbols = [v.strip() for v in toks[prev_symbols_colno].split(',')]
            #alias_symbols = [v.strip() for v in toks[alias_symbols_colno].split(',')]
            for symbol in prev_symbols:
                self.hugo_synonyms[symbol] = app_symbol
            #for symbol in alias_symbols:
            #    self.hugo_synonyms[symbol] = app_symbol
        f.close()
        # enst to ensg
        f = open(os.path.join(data_path, 'data', 'ensg_enst.txt'))
        for line in f:
            [ensg, enst] = line[:-1].split('\t')
            self.enstnov_ensgnov[self.remove_version(enst)] = self.remove_version(ensg)
        f.close()
        # canonical enst
        f = open(os.path.join(data_path, 'data', 'MANE.GRCh38.v0.9.summary.txt'))
        f.readline()
        self.mane_ensgnv_to_enstnv = {}
        self.mane_hugo_to_canonical_enst = {}
        self.mane_hugos = []
        self.mane_hugo_to_ensg = {}
        for line in f:
            toks = line[:-1].split('\t')
            ensg = toks[1]
            hugo = toks[3]
            #if hugo in self.hugo_synonyms:
            #    hugo = self.hugo_synonyms[hugo]
            enst = toks[7]
            ensgnv = self.remove_version(ensg)
            enstnv = self.remove_version(enst)
            self.mane_ensgnv_to_enstnv[ensgnv] = enstnv
            self.mane_hugos.append(hugo)
            self.mane_hugo_to_canonical_enst[hugo] = enst
            self.mane_hugo_to_ensg[hugo] = ensgnv
        f.close()
        # enst alen
        f = open(os.path.join(data_path, 'data', 'enst_alen.txt'))
        self.enstnv_to_alens = {}
        for line in f:
            [enst, alen] = line[:-1].split('\t')
            self.enstnv_to_alens[self.remove_version(enst)] = int(alen)
        f.close()
        # hugo to ensg
        f = open(os.path.join(data_path, 'data', 'hugo_ensg_chrom.txt'))
        self.hugo_to_ensg = {}
        self.hugo_to_chrom = {}
        self.ensg_to_chrom = {}
        for line in f:
            [hugo, ensg, chrom] = line[:-1].split('\t')
            ensg = ensg.split('.')[0]
            #if hugo in self.hugo_synonyms:
            #    hugo = self.hugo_synonyms[hugo]
            self.hugo_to_ensg[hugo] = ensg
            if hugo not in self.hugo_to_chrom:
                self.hugo_to_chrom[hugo] = []
            if ensg not in self.ensg_to_chrom:
                self.ensg_to_chrom[ensg] = chrom
            self.hugo_to_chrom[hugo].append(chrom)
        f.close()
        self.csq_consequence_to_oc_so = {
            'splice_acceptor_variant': 'splice_site_variant',
            'splice_donor_variant': 'splice_site_variant',
            'frameshift_variant': 'frameshift_elongation,frameshift_truncation'
        }
        self.no_mane_hugos = {}
        if self.filterpath is None:
            self.filter_name = None
        else:
            self.filter_name = os.path.basename(self.filterpath)
        if self.filter_name not in ['coding1.json', 'coding2.json', 'coding3.json', 'coding_noncoding_1.json', 'conding_noncoding_2.json']:
            print('\nfilter filename should be one of coding1.json, coding2.json, coding3.json, coding_noncoding_1.json, and coding_noncoding_2.json. Exiting.')
            return False

    def get_standardized_module_option(self, v):
        tv = type(v)
        if tv == str:
            if "," in v:
                v = [val for val in v.split(",") if val != ""]
        if v == "true":
            v = True
        elif v == "false":
            v = False
        return v

    def should_write_level(self, level):
        if self.levels_to_write is None:
            return True
        elif level in self.levels_to_write:
            return True
        else:
            return False

    def write_preface (self, level):
        self.level = level
        if self.should_write_level(level) == False:
            return

    def write_header (self, level):
        if self.should_write_level(level) == False:
            return
        self.headers[self.level] = []
        self.dataframe_colnos[self.level] = []
        self.dataframe_cols[self.level] = []
        self.dataframe_headers[self.level] = {}
        self.colno_to_colname[self.level] = {}
        # table columns
        for colgroup_dict in self.colinfo[self.level]['colgroups']:
            colgroup_name = colgroup_dict['name']
            minfo = cravat.admin_util.get_local_module_info(colgroup_name)
            if minfo is None:
                continue
            conf = minfo.conf
            if 'output_columns' not in conf:
                continue
            for output_dict in conf['output_columns']:
                if output_dict.get('table', False) == True:
                    colname = colgroup_name + '__' + output_dict['name']
                    if colname in self.cols_to_display:
                        self.cols_to_display.remove(colname)
                    self.dataframe_cols[self.level].append(colname)
                    self.dataframe_headers[self.level][colname] = [v['name'] for v in output_dict['table_headers']]
        colno = 0
        columns = self.colinfo[level]["columns"]
        for i in range(len(columns)):
            col = columns[i]
            colname = col['col_name']
            self.colno_to_colname[self.level][colno] = colname
            self.headers[self.level].append(colname)
            if colname in self.dataframe_cols[self.level]:
                self.dataframe_colnos[self.level].append(colno)
            if colname == 'genehancer__target_genes':
                self.colno_genehancertargetgenes = colno
            elif colname == 'base__so':
                self.colno_so = colno
            elif colname == 'base__coding':
                self.colno_coding = colno
            elif colname == 'extra_vcf_info__CSQ':
                self.colno_csq = colno
            elif colname == 'extra_vcf_info__CSQ_SYMBOL':
                self.colno_csq_symbol = colno
            elif colname == 'extra_vcf_info__CSQ_Consequence':
                self.colno_csq_consequence = colno
            elif colname == 'extra_vcf_info__CSQ_LoF':
                self.colno_csq_lofs = colno
            elif colname == 'extra_vcf_info__CSQ_Gene':
                self.colno_csq_gene = colno
            elif colname == 'extra_vcf_info__CSQ_BIOTYPE':
                self.colno_csq_biotype = colno
            elif colname == 'extra_vcf_info__CSQ_Feature':
                self.colno_csq_ensts = colno
            elif colname == 'base__transcript':
                self.colno_transcript = colno
            elif colname == 'base__all_mappings':
                self.colno_all_mappings = colno
            elif colname == 'metasvm__score':
                self.colno_metasvm_score = colno
            elif colname == 'fathmm_xf__score':
                self.colno_fathmm_xf_score = colno
            elif colname == 'sift__prediction':
                self.colno_sift_prediction = colno
            elif colname == 'lrt__lrt_pred':
                self.colno_lrt_lrt_pred = colno
            elif colname == 'polyphen2__hdiv_pred':
                self.colno_polyphen2_hdiv_pred = colno
            elif colname == 'polyphen2__hvar_pred':
                self.colno_polyphen2_hvar_pred = colno
            elif colname == 'genehancer__feature_name':
                self.colno_genehancer_feature_name = colno
            elif colname == 'ensembl_regulatory_build__region':
                self.colno_ensembl_regulatory_build_region = colno
            colno += 1
        colno = 0
        self.colnos_to_display[level] = []
        self.colnames_to_display[level] = []
        for module_col_name in self.cols_to_display:
            [module_name, col_name] = module_col_name.split('__')
            for colno in range(len(columns)):
                if columns[colno]["col_name"] == module_col_name:
                    self.colnos_to_display[level].append(colno)
                    self.colnames_to_display[level].append(self.colname_display_dict[module_col_name])
                    break
        self.data[self.level] = []

    def remove_version (self, uid):
        return uid.split('.')[0]

    def convert_csq_consequence (self, c):
        cs = []
        for tok in c.split('&'):
            cs.append(self.csq_consequence_to_oc_so.get(c, c))
        cs = '&'.join(cs)
        return cs

    def has_coding_so (self, sos):
        if 'frameshift_elongation' in sos \
                or 'frameshift_truncation' in sos \
                or 'complex_substitution' in sos \
                or 'splice_site_variant' in sos \
                or 'start_lost' in sos \
                or 'stop_gained' in sos \
                or 'stop_lost' in sos \
                or 'transcript_ablation' in sos \
                or 'inframe_insertion' in sos \
                or 'inframe_deletion' in sos \
                or 'exon_loss_variant' in sos \
                or 'missense_variant' in sos:
            return True
        else:
            return False

    def parse_mapping (self, mapping):
        [enst, _, _, sos, _, _] = mapping
        return enst, sos

    def find_canonical_mapping (self, hugo, all_mappings, canonical_enstnv):
        for mapping in all_mappings[hugo]:
            enst, sos = self.parse_mapping(mapping)
            if self.remove_version(enst) == canonical_enstnv:
                return mapping
        return None

    def parse_all_mappings_str (self, all_mappings_str):
        all_mappings_t = [v.strip() for v in all_mappings_str.split(';')]
        all_mappings = {}
        for mapping_t in all_mappings_t:
            mapping = mapping_t.split(':')
            try:
                hugo = mapping[1]
                #if hugo in self.hugo_synonyms:
                #    hugo = self.hugo_synonyms[hugo]
            except:
                print(f'#####################|\nAn exception occurred. Please contact the OpenCRAVAT team with the following information:')
                print(f'#exception: getting hugo from mapping\nall_mappings_t={all_mappings_t}')
                print(f'mapping={mapping}')
                return {}
            if hugo not in all_mappings:
                all_mappings[hugo] = []
            all_mappings[hugo].append(mapping)
        return all_mappings

    def get_canonicals(self, row, all_mappings, chrom):
        # Which hugos are in MANE and which are not.
        hugos_in_mane = []
        other_hugos = []
        csq_hugos_in_mane = []
        csq_other_hugos = []
        csq_biotypes = row[self.colno_csq_biotype]
        csq_hugos = row[self.colno_csq_symbol]
        csq_ensts = row[self.colno_csq_ensts]
        if csq_ensts is None:
            csq_ensts = []
        else:
            csq_ensts = csq_ensts.split(';')
        if csq_hugos is None:
            csq_hugos = []
        else:
            csq_hugos = csq_hugos.split(';')
        for hugo in all_mappings:
            if hugo in self.mane_hugos and hugo not in hugos_in_mane:
                hugos_in_mane.append(hugo)
            elif hugo not in other_hugos:
                other_hugos.append(hugo)
        for i in range(len(csq_hugos)):
            hugo = csq_hugos[i]
            if csq_ensts[i].startswith('ENST') == False:
                continue
            if csq_biotypes[i] != 'protein_coding':
                continue
            if hugo in self.mane_hugos and hugo not in csq_hugos_in_mane:
                csq_hugos_in_mane.append(hugo)
            elif hugo not in csq_other_hugos:
                csq_other_hugos.append(hugo)
        # ENSG and canonical ENST
        self.ensgs = {}
        canonical_ensts = {}
        canonical_enstnvs = {}
        # MANE transcript as canonical
        for hugo in hugos_in_mane:
            self.ensgs[hugo] = self.mane_hugo_to_ensg[hugo]
            enst = self.mane_hugo_to_canonical_enst[hugo]
            canonical_ensts[hugo] = enst
            canonical_enstnvs[hugo] = self.remove_version(enst)
        for hugo in csq_hugos_in_mane:
            if hugo in self.ensgs:
                continue
            self.ensgs[hugo] = self.mane_hugo_to_ensg[hugo]
            enst = self.mane_hugo_to_canonical_enst[hugo]
            canonical_ensts[hugo] = enst
            canonical_enstnvs[hugo] = self.remove_version(enst)
        for hugo in other_hugos:
            if hugo in self.hugo_to_ensg and chrom in self.hugo_to_chrom[hugo]:
                self.ensgs[hugo] = self.hugo_to_ensg[hugo]
            elif hugo in csq_hugos:
                self.ensgs[hugo] = csq_genes[csq_hugos.index(hugo)]
            else:
                print(f'ENSG ID for {hugo} was not found. Using {hugo} as group_id')
                self.ensgs[hugo] = hugo
        for hugo in csq_other_hugos:
            if hugo in self.ensgs:
                continue
            if hugo in self.hugo_to_ensg and chrom in self.hugo_to_chrom[hugo]:
                self.ensgs[hugo] = self.hugo_to_ensg[hugo]
            elif hugo in csq_hugos:
                self.ensgs[hugo] = csq_genes[csq_hugos.index(hugo)]
            else:
                print(f'ENSG ID for {hugo} was not found. Using {hugo} as group_id')
                self.ensgs[hugo] = hugo
        # Longest transcript as canonical
        for hugo in other_hugos:
            mappings = all_mappings[hugo]
            enst = mappings[0][0]
            enstnv = self.remove_version(enst)
            canonical_ensts[hugo] = enst
            canonical_enstnvs[hugo] = enstnv
            if enstnv in self.enstnv_to_alens:
                canonical_alen = self.enstnv_to_alens[enstnv]
            else:
                canonical_alen = -1
            for mapping in mappings[1:]:
                enst, sos = self.parse_mapping(mapping)
                enstnv = self.remove_version(enst)
                if enstnv in self.enstnv_to_alens:
                    alen = self.enstnv_to_alens[enstnv]
                else:
                    alen = -1
                if alen > canonical_alen:
                    canonical_alen = alen
                    canonical_ensts[hugo] = enst
                    canonical_enstnvs[hugo] = enstnv
        for hugo in csq_other_hugos:
            enst = csq_ensts[0]
            enstnv = self.remove_version(enst)
            if enst.startswith('ENST'):
                canonical_ensts[hugo] = enst
                canonical_enstnvs[hugo] = enstnv
            else:
                canonical_ensts[hugo] = None
                canonical_enstnvs[hugo] = None
            if enstnv in self.enstnv_to_alens:
                canonical_alen = self.enstnv_to_alens[enstnv]
            else:
                canonical_alen = -1
            for i in range(1, len(csq_ensts)):
                enst = csq_ensts[i]
                enstnv = self.remove_version(enst)
                if enst.startswith('ENST') == False:
                    continue
                if enstnv in self.enstnv_to_alens:
                    alen = self.enstnv_to_alens[enstnv]
                else:
                    alen = -1
                if canonical_ensts[hugo] is None or alen > canonical_alen:
                    canonical_alen = alen
                    canonical_ensts[hugo] = enst
                    canonical_enstnvs[hugo] = enstnv
        # SO for canonical transcripts
        canonical_sos = {}
        for hugo in list(set(hugos_in_mane) | set(other_hugos)):
            canonical_mapping = self.find_canonical_mapping(hugo, all_mappings, canonical_enstnvs[hugo])
            if canonical_mapping is not None:
                enst, sos = self.parse_mapping(canonical_mapping)
                canonical_sos[hugo] = sos
        csq_consequences = row[self.colno_csq_consequence]
        for hugo in list(set(csq_hugos_in_mane) | set(csq_other_hugos)):
            canonical_enstnv = canonical_enstnvs[hugo]
            for i in range(len(csq_ensts)):
                if csq_ensts[i].split('.')[0] == canonical_enstnv:
                    sos = self.convert_csq_consequence(csq_consequences[i])
                    if hugo not in canonical_sos:
                        canonical_sos[hugo] = sos
                    else:
                        canonical_sos[hugo] += ',' + sos
                    break
        return canonical_enstnvs, canonical_sos

    def get_lof_of_enstnv(self, enstnv, csq_lofs, csq_enstnvs):
        if len(csq_lofs) == 0:
            return None
        if enstnv in csq_enstnvs:
            return csq_lofs[csq_enstnvs.index(enstnv)]
        else:
            return None

    def get_all_mappings(self, row):
        all_mappings_t = row[self.colno_all_mappings]
        if all_mappings_t != '':
            all_mappings = self.parse_all_mappings_str(all_mappings_t)
        else:
            all_mappings = {}

    def get_so_of_enstnv(self, row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos):
        if hugo in all_mappings:
            for mapping in all_mappings[hugo]:
                if enstnv == mapping[3].split('.')[0]:
                    return mapping[2]
        if enstnv in csq_enstnvs:
            return self.convert_csq_consequence(csq_sos[csq_enstnvs.index(enstnv)])
        else:
            return None

    def get_csq_lofs(self, row):
        csq_lofs = row[self.colno_csq_lofs]
        if csq_lofs is None:
            return []
        else:
            return csq_lofs.split(';')

    def get_csq_enstnvs(self, row):
        csq_ensts = row[self.colno_csq_ensts]
        if csq_ensts is None:
            return []
        else:
            return [v.split('.')[0] for v in csq_ensts.split(';')]

    def run_coding1_filter(self, row, all_mappings, canonical_enstnvs, canonical_sos, csq_sos):
        csq_lofs = self.get_csq_lofs(row)
        csq_enstnvs = self.get_csq_enstnvs(row)
        metasvm_score = row[self.colno_metasvm_score]
        fathmm_xf_score = row[self.colno_fathmm_xf_score]
        group_ids = set()
        for hugo in canonical_enstnvs:
            enstnv = canonical_enstnvs[hugo]
            lof = self.get_lof_of_enstnv(enstnv, csq_lofs, csq_enstnvs)
            so = self.get_so_of_enstnv(
                row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos) # oc over vep
            if  lof == 'HC'\
                or\
                (so == 'missense_variant' and metasvm_score is not None and metasvm_score > 0)\
                or\
                (fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    so in ['complex_substitution',
                    'exon_loss_variant',
                    'frameshift_variant',
                    'frameshift_elongation',
                    'frameshift_truncation',
                    'inframe_insertion',
                    'inframe_deletion'
                    'missense_variant',
                    'splice_site_variant',
                    'splice_acceptor_variant',
                    'splice_donor_variant',
                    'start_lost',
                    'stop_gained',
                    'stop_lost',
                    'transcript_ablation'])\
                or\
                (so == 'synonymous_variant' and\
                    fathmm_xf_score is not None and fathmm_xf_score > 0.5):
                group_ids.add(self.ensgs[hugo])
        return group_ids

    def run_coding2_filter(self, row, all_mappings, canonical_enstnvs, canonical_sos, csq_sos):
        csq_lofs = self.get_csq_lofs(row)
        csq_enstnvs = self.get_csq_enstnvs(row)
        fathmm_xf_score = row[self.colno_fathmm_xf_score]
        sift_prediction = row[self.colno_sift_prediction]
        lrt_pred = row[self.colno_lrt_lrt_pred]
        polyphen2_hdiv_pred = row[self.colno_polyphen2_hdiv_pred]
        polyphen2_hvar_pred = row[self.colno_polyphen2_hvar_pred]
        group_ids = set()
        for hugo in canonical_enstnvs:
            enstnv = canonical_enstnvs[hugo]
            lof = self.get_lof_of_enstnv(enstnv, csq_lofs, csq_enstnvs)
            so = self.get_so_of_enstnv(row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos)
            if  (\
                    so == 'missense_variant' and\
                    sift_prediction == 'Damaging' and\
                    lrt_pred == 'Deleterious' and\
                    polyphen2_hdiv_pred is not None and 'P' in polyphen2_hdiv_pred and\
                    polyphen2_hvar_pred is not None and 'P' in polyphen2_hvar_pred
                ) or\
                (fathmm_xf_score is not None and fathmm_xf_score > 0.5 and so in [
                    'complex_substitution',
                    'exon_loss_variant',
                    'frameshift_variant',
                    'frameshift_elongation',
                    'frameshift_truncation',
                    'inframe_insertion',
                    'inframe_deletion'
                    'missense_variant',
                    'splice_site_variant',
                    'splice_acceptor_variant',
                    'splice_donor_variant',
                    'start_lost',
                    'stop_gained',
                    'stop_lost',
                    'transcript_ablation']\
                ) or\
                (\
                    so == 'synonymous_variant' and\
                    fathmm_xf_score is not None and fathmm_xf_score > 0.5\
                ) or\
                (lof == 'HC'):
                group_ids.add(self.ensgs[hugo])
        return group_ids

    def run_coding3_filter(self, row, all_mappings, canonical_enstnvs, canonical_sos, csq_sos):
        csq_lofs = self.get_csq_lofs(row)
        csq_enstnvs = self.get_csq_enstnvs(row)
        fathmm_xf_score = row[self.colno_fathmm_xf_score]
        sift_prediction = row[self.colno_sift_prediction]
        lrt_pred = row[self.colno_lrt_lrt_pred]
        polyphen2_hdiv_pred = row[self.colno_polyphen2_hdiv_pred]
        polyphen2_hvar_pred = row[self.colno_polyphen2_hvar_pred]
        group_ids = set()
        for hugo in canonical_enstnvs:
            enstnv = canonical_enstnvs[hugo]
            lof = self.get_lof_of_enstnv(enstnv, csq_lofs, csq_enstnvs)
            so = self.get_so_of_enstnv(row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos)
            if  (\
                    so == 'missense_variant' and\
                    (\
                        sift_prediction == 'Damaging' or\
                        lrt_pred == 'Deleterious' or\
                        (polyphen2_hdiv_pred is not None and 'P' in polyphen2_hdiv_pred) or\
                        (polyphen2_hvar_pred is not None and 'P' in polyphen2_hvar_pred)\
                    )\
                )\
                or\
                (fathmm_xf_score is not None and fathmm_xf_score > 0.5 and so in [
                    'complex_substitution',
                    'exon_loss_variant',
                    'frameshift_variant',
                    'frameshift_elongation',
                    'frameshift_truncation',
                    'inframe_insertion',
                    'inframe_deletion'
                    'missense_variant',
                    'splice_site_variant',
                    'splice_acceptor_variant',
                    'splice_donor_variant',
                    'start_lost',
                    'stop_gained',
                    'stop_lost',
                    'transcript_ablation']) or\
                (so == 'synonymous_variant' and\
                    fathmm_xf_score is not None and fathmm_xf_score > 0.5)\
                or\
                (lof == 'HC'):
                group_ids.add(self.ensgs[hugo])
        return group_ids

    def run_coding_noncoding_filter_1(
            self, row, all_mappings, canonical_enstnvs, canonical_sos, csq_sos):
        csq_lofs = self.get_csq_lofs(row)
        csq_enstnvs = self.get_csq_enstnvs(row)
        fathmm_xf_score = row[self.colno_fathmm_xf_score]
        metasvm_score = row[self.colno_metasvm_score]
        sift_prediction = row[self.colno_sift_prediction]
        lrt_pred = row[self.colno_lrt_lrt_pred]
        genehancer_feature_name = row[self.colno_genehancer_feature_name]
        ensembl_regulatory_build_region = row[self.colno_ensembl_regulatory_build_region]
        group_ids = set()
        for hugo in canonical_enstnvs:
            enstnv = canonical_enstnvs[hugo]
            lof = self.get_lof_of_enstnv(enstnv, csq_lofs, csq_enstnvs)
            so = self.get_so_of_enstnv(row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos)
            if  lof == 'HC'\
                or\
                (so == 'missense_variant' and metasvm_score is not None and metasvm_score > 0)\
                or\
                (fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    so in ['complex_substitution',
                    'exon_loss_variant',
                    'frameshift_variant',
                    'frameshift_elongation',
                    'frameshift_truncation',
                    'inframe_insertion',
                    'inframe_deletion'
                    'missense_variant',
                    'splice_site_variant',
                    'splice_acceptor_variant',
                    'splice_donor_variant',
                    'start_lost',
                    'stop_gained',
                    'stop_lost',
                    'transcript_ablation'])\
                or\
                (so == 'synonymous_variant' and\
                    fathmm_xf_score is not None and fathmm_xf_score > 0.5):
                group_ids.add(self.ensgs[hugo])
            elif genehancer_feature_name == 'Enhancer' and\
                (\
                    (fathmm_xf_score is not None and fathmm_xf_score > 0.5)\
                    or\
                    (ensembl_regulatory_build_region in [\
                        'CTCF_binding_site', 'TF_binding_site'\
                    ])\
                ):
                genehancer_target_genes = [v.split(':')[0]\
                    for v in row[self.colno_genehancertargetgenes].split(',')]
                for target in genehancer_target_genes:
                    if target.startswith('ENSG'):
                        group_ids.add(target)
            elif genehancer_feature_name == 'Promoter' and\
                (\
                    (fathmm_xf_score is not None and fathmm_xf_score > 0.5)\
                    or\
                    (ensembl_regulatory_build_region in [
                        'CTCF_binding_site', 'TF_binding_site'
                    ])\
                ):
                genehancer_target_genes = [v.split(':')[0]\
                    for v in row[self.colno_genehancertargetgenes].split(',')]
                for target in genehancer_target_genes:
                    if target.startswith('ENSG'):
                        group_ids.add(target)
            elif so is not None and 'upstream_gene_variant' in so and\
                (\
                    (fathmm_xf_score is not None and fathmm_xf_score > 0.5)\
                    or\
                    (ensembl_regulatory_build_region in [\
                        'CTCF_binding_site', 'TF_binding_site'\
                    ])\
                ):
                group_ids.add(self.ensgs[hugo])
        return group_ids

    def run_coding_noncoding_filter_2(
            self, row, all_mappings, canonical_enstnvs, canonical_sos, csq_sos):
        csq_lofs = self.get_csq_lofs(row)
        csq_enstnvs = self.get_csq_enstnvs(row)
        fathmm_xf_score = row[self.colno_fathmm_xf_score]
        metasvm_score = row[self.colno_metasvm_score]
        sift_prediction = row[self.colno_sift_prediction]
        lrt_pred = row[self.colno_lrt_lrt_pred]
        genehancer_feature_name = row[self.colno_genehancer_feature_name]
        ensembl_regulatory_build_region = row[self.colno_ensembl_regulatory_build_region]
        group_ids = set()
        for hugo in canonical_enstnvs:
            enstnv = canonical_enstnvs[hugo]
            lof = self.get_lof_of_enstnv(enstnv, csq_lofs, csq_enstnvs)
            so = self.get_so_of_enstnv(row, hugo, enstnv, csq_enstnvs, all_mappings, csq_sos)
            if  lof == 'HC'\
                or\
                (so == 'missense_variant' and metasvm_score is not None and metasvm_score > 0)\
                or\
                (fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    so in ['complex_substitution',
                    'exon_loss_variant',
                    'frameshift_variant',
                    'frameshift_elongation',
                    'frameshift_truncation',
                    'inframe_insertion',
                    'inframe_deletion'
                    'missense_variant',
                    'splice_site_variant',
                    'splice_acceptor_variant',
                    'splice_donor_variant',
                    'start_lost',
                    'stop_gained',
                    'stop_lost',
                    'transcript_ablation'])\
                or\
                (so == 'synonymous_variant' and\
                    fathmm_xf_score is not None and fathmm_xf_score > 0.5):
                group_ids.add(self.ensgs[hugo])
            elif genehancer_feature_name == 'Enhancer' and\
                fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    ensembl_regulatory_build_region in [
                    'CTCF_binding_site',
                    'TF_binding_site',
                    'enhancer',
                    'open_chromatin_region',
                    'promoter',
                    'promoter_flanking_region'
                ]:
                genehancer_target_genes = [v.split(':')[0]\
                    for v in row[self.colno_genehancertargetgenes].split(',')]
                for target in genehancer_target_genes:
                    if target.startswith('ENSG'):
                        group_ids.add(target)
            elif genehancer_feature_name == 'Promoter' and\
                fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    ensembl_regulatory_build_region in [
                    'CTCF_binding_site',
                    'TF_binding_site'
                    'enhancer',
                    'open_chromatin_region',
                    'promoter',
                    'promoter_flanking_region'
                ]:
                genehancer_target_genes = [v.split(':')[0]\
                    for v in row[self.colno_genehancertargetgenes].split(',')]
                for target in genehancer_target_genes:
                    if target.startswith('ENSG'):
                        group_ids.add(target)
            elif so is not None and 'upstream_gene_variant' in so and\
                fathmm_xf_score is not None and fathmm_xf_score > 0.5 and\
                    ensembl_regulatory_build_region in [
                    'CTCF_binding_site',
                    'TF_binding_site'
                    'enhancer',
                    'open_chromatin_region',
                    'promoter',
                    'promoter_flanking_region'
                ]:
                group_ids.add(self.ensgs[hugo])
        return group_ids

    def write_table_row (self, row):
        if self.should_write_level(self.level) == False:
            return
        try:
            if len(self.colnos_to_display[self.level]) > 0:
                filtered_row = [row[colno] for colno in self.colnos_to_display[self.level]]
            else:
                filtered_row = row
            chrom = filtered_row[self.colno_to_display_chrom]
            pos = int(filtered_row[self.colno_to_display_pos])
            ref = filtered_row[self.colno_to_display_ref]
            alt = filtered_row[self.colno_to_display_alt]
            if ref == '-' or alt == '-': # deletion or insertion
                chrom = filtered_row[self.colno_to_display_chrom]
                pos = pos - 1
                prev_base = self.wgs_reader.get_bases(chrom, pos).upper()
                if ref != '-' and alt == '-': # deletion
                    ref = prev_base + ref
                    alt = prev_base
                elif ref == '-' and alt != '-': # insertion
                    ref = prev_base
                    alt = prev_base + alt
                filtered_row[self.colno_to_display_pos] = pos
                filtered_row[self.colno_to_display_ref] = ref
                filtered_row[self.colno_to_display_alt] = alt
            all_mappings_t = row[self.colno_all_mappings]
            if all_mappings_t != '':
                all_mappings = self.parse_all_mappings_str(all_mappings_t)
            else:
                all_mappings = {}
            csq_consequences = row[self.colno_csq_consequence]
            if csq_consequences is None:
                csq_consequences = []
            else:
                csq_consequences = csq_consequences.split(';')
            #coding = row[self.colno_coding]
            #genehancertargetgenes = row[self.colno_genehancertargetgenes]
            # VEP annotations
            #csq = row[self.colno_csq]
            #csq_hugos = row[self.colno_csq_symbol]
            #csq_genes = row[self.colno_csq_gene]
            #csq_lofs = row[self.colno_csq_lofs]
            #csq_biotypes = row[self.colno_csq_biotype]
            #csq_ensts = row[self.colno_csq_ensts]
            #if csq_hugos is None:
            #    csq_hugos = []
            #else:
                #csq_hugos = [self.hugo_synonyms[v] if v in self.hugo_synonyms else v\
                    #for v in csq_hugos.split(';')]
            #    csq_hugos = csq_hugos.split(';')
            #if csq_ensts is None:
            #    csq_ensts = []
            #else:
            #    csq_ensts = csq_ensts.split(';')
            #if csq_genes is not None:
            #    csq_genes = [m for v in csq_genes.split(',') for m in v.split(';')]
            #else:
            #    csq_toks = csq.split('|')
            #    for tok in csq_toks:
            #        if 'ENSG' in tok:
            #            csq_genes = [m for v in tok.split(',') for m in v.split(';')]
            #            break
            #if csq_genes is None:
            #    csq_genes = []
            #if csq_lofs is not None:
            #    csq_lofs = csq_lofs.split(';')
            #else:
            #    csq_toks = csq.split('|')
            #    for tok in csq_toks:
            #        if 'HC' in tok:
            #            csq_lofs = tok.split(';')
            #            break
            #if csq_lofs is None:
            #    csq_lofs = []
            #if csq_biotypes is None:
            #    csq_biotypes = []
            #else:
            #    csq_biotypes = csq_biotypes.split(';')
            canonical_enstnvs, canonical_sos = self.get_canonicals(row, all_mappings, chrom)
            # Filters
            if self.filter_name.startswith('coding1.json'):
                group_ids = self.run_coding1_filter(
                    row,
                    all_mappings,
                    canonical_enstnvs,
                    canonical_sos,
                    csq_consequences
                )
            elif self.filter_name.startswith('coding2.json'):
                group_ids = self.run_coding2_filter(
                    row,
                    all_mappings,
                    canonical_enstnvs,
                    canonical_sos,
                    csq_consequences
                )
            elif self.filter_name.startswith('coding3.json'):
                group_ids = self.run_coding3_filter(
                    row,
                    all_mappings,
                    canonical_enstnvs,
                    canonical_sos,
                    csq_consequences
                )
            elif self.filter_name.startswith('coding_noncoding_1.json'):
                group_ids = self.run_coding_noncoding_filter_1(
                    row,
                    all_mappings,
                    canonical_enstnvs,
                    canonical_sos,
                    csq_consequences
                )
            elif self.filter_name.startswith('coding_noncoding_2.json'):
                group_ids = self.run_coding_noncoding_filter_2(
                    row,
                    all_mappings,
                    canonical_enstnvs,
                    canonical_sos,
                    csq_consequences
                )
            '''
            # GeneHancer targets
            if genehancertargetgenes is not None:
                genehancertargetgenes = [v.split(':')[0].strip() for v in genehancertargetgenes.split(',')]
                for target in genehancertargetgenes:
                    if target.startswith('ENSG') and target not in group_ids:
                        group_ids.add(target)
                        genehancer_target_exists = True
                    elif target in self.hugo_to_ensg and target in self.hugo_to_chrom and chrom in self.hugo_to_chrom[target]:
                        ensg = self.hugo_to_ensg[target]
                        if ensg not in group_ids:
                            group_ids.add(ensg)
                            genehancer_target_exists = True
            '''
            wrong_chrom_ensgs = []
            for ensg in group_ids:
                if ensg in self.ensg_to_chrom and self.ensg_to_chrom[ensg] != chrom:
                    wrong_chrom_ensgs.append(ensg)
            if len(wrong_chrom_ensgs) > 0:
                print(f'@@@ wrong_chrom_ensgs={wrong_chrom_ensgs}')
            for ensg in wrong_chrom_ensgs:
                    group_ids.remove(ensg)
            '''
            so_ignores = [
                'intron_variant',
                'synonymous_variant',
                '3_prime_UTR_variant',
                '5_prime_UTR_variant',
                'downstream_gene_variant',
                'intergenic_variant',
                'non_coding_transcript_exon_variant',
                'splice_region_variant',
                'start_retained_variant',
                'stop_retained_variant',
                'mature_miRNA_variant',
                'NMD_transcript_variant',
                'non_coding_transcript_variant',
                'TFBS_ablation',
                'TFBS_amplification',
                'TF_binding_site_variant',
                'regulatory_region_ablation',
                'regulatory_region_amplification',
                'feature_elongation',
                'regulatory_region_variant',
                'feature_truncation',
                'incomplete_terminal_codon_variant',
            ]
            # Collects group_id.
            group_ids = set()
            ## coding and splice site variant
            for hugo in canonical_enstsnv:
                if hugo == '': # For example, ENSTR.
                    continue
                sos = None
                if hugo not in canonical_sos:
                    canonical_enstnv = canonical_enstnvs[hugo]
                    for i in range(len(csq_ensts)):
                        enstnv = self.remove_version(csq_ensts[i])
                        if enstnv == canonical_enstnv:
                            csq_consq = csq_consequences[i]
                            if ('intron' in csq_consq and not ('splice_donor' in csq_consq or 'splice_acceptor' in csq_consq)) or 'downstream' in csq_consq or 'non_coding' in csq_consq or 'upstream' in csq_consq:
                                break
                            elif enstnv not in self.enstnv_to_alens:
                                print(f'{enstnv} not in oc aalen')
                                break
                            elif (self.filter_name == 'coding1' or self.filter_name == 'coding2' or self.filter_name == 'coding3'):
                                if csq_biotypes[i] != 'protein_coding':
                                    break
                                elif self.has_coding_so(
                            if csq_hugos[i] in canonical_sos:
                                sos = canonical_sos[csq_hugos[i]]
                                break
                            for cano_hugo, cano_enstnv in canonical_enstnvs.items():
                                if cano_enstnv == canonical_enstnv and cano_hugo in canonical_sos:
                                    sos = canonical_sos[cano_hugo]
                                    break
                            if sos is None:
                                print(f'##################\nAn exception occurred. Please contact the OpenCRAVAT team with the following information:')
                                print(f'#exception: sos is None\n#row={row}\ncanonical_enstnvs={canonical_enstnvs}\ncanonical_sos={canonical_sos}\nin mane? {hugo in self.mane_hugos}\nall_mappings={all_mappings}\ncsq_ensts={csq_ensts}\ncsq_hugos={csq_hugos}\ncsq_consequenced={csq_consequences}\ncsq={csq}\nhugo={hugo}')
                                return
                        if sos is not None:
                            break
                    if sos is None:
                        continue
                else:
                    sos = canonical_sos[hugo]
                if self.has_coding_so(sos):
                    ensg = self.ensgs[hugo]
                    group_ids.add(ensg)
            ## HC Lof from VEP
            if len(csq_ensts) == len(csq_lofs):
                for hugo in canonical_enstsnv:
                    canonical_enstnv = canonical_enstsnv[hugo]
                    if canonical_enstnv is None:
                        continue
                    ensg = self.ensgs[hugo]
                    if ensg in group_ids:
                        continue
                    for i in range(len(csq_lofs)):
                        enst = csq_ensts[i]
                        enstnv = self.remove_version(enst)
                        lof = csq_lofs[i]
                        biotype = csq_biotypes[i]
                        ### LoF HC and BIOTYPE relationship from chr22.sqlite:
                        # frameshift_variant	protein_coding
                        # frameshift_variant&splice_region_variant	protein_coding
                        # frameshift_variant&start_lost	protein_coding
                        # frameshift_variant&stop_lost	protein_coding
                        # frameshift_variant&stop_retained_variant	protein_coding
                        # splice_acceptor_variant	protein_coding
                        # splice_acceptor_variant&coding_sequence_variant	protein_coding
                        # splice_acceptor_variant&coding_sequence_variant&intron_variant	protein_coding
                        # splice_acceptor_variant&intron_variant	protein_coding
                        # splice_donor_variant	protein_coding
                        # splice_donor_variant&coding_sequence_variant	protein_coding
                        # splice_donor_variant&coding_sequence_variant&intron_variant	protein_coding
                        # splice_donor_variant&intron_variant	protein_coding
                        # stop_gained	protein_coding
                        # stop_gained&frameshift_variant	protein_coding
                        # stop_gained&inframe_insertion	protein_coding
                        # stop_gained&inframe_insertion&splice_region_variant	protein_coding
                        # stop_gained&splice_region_variant	protein_coding
                        ### thus, no need for checking BIOTYPE "protein_coding".
                        if enstnv == canonical_enstnv and lof == 'HC':
                            group_ids.add(ensg)
                            break
            ## GeneHancer
            genehancer_target_exists = False
            if genehancertargetgenes is not None:
                genehancertargetgenes = [v.split(':')[0].strip() for v in genehancertargetgenes.split(',')]
                for target in genehancertargetgenes:
                    if target.startswith('ENSG') and target not in group_ids:
                        group_ids.add(target)
                        genehancer_target_exists = True
                    elif target in self.hugo_to_ensg and target in self.hugo_to_chrom and chrom in self.hugo_to_chrom[target]:
                        ensg = self.hugo_to_ensg[target]
                        if ensg not in group_ids:
                            group_ids.add(ensg)
                            genehancer_target_exists = True
            ## 5k upstream
            upstream_but_no_canonical = False
            if len(csq_consequences) > 0:
                for hugo in canonical_enstsnv:
                    ensg = self.ensgs[hugo]
                    if ensg in group_ids:
                        continue
                    canonical_enstnv = canonical_enstnvs[hugo]
                    for i in range(len(csq_genes)):
                        hugo = csq_hugos[i]
                        #if hugo in self.hugo_synonyms:
                        #    hugo = self.hugo_synonyms[hugo]
                        ensg = csq_genes[i]
                        enst = csq_ensts[i]
                        enstnv = self.remove_version(enst)
                        consequence = csq_consequences[i]
                        if hugo == '': # ENSR for example
                            continue
                        if 'upstream_gene_variant' in consequence:
                            if enstnv == canonical_enstnv:
                                group_ids.add(csq_genes[i])
                                upstream_but_no_canonical = False
                                break
                            else:
                                upstream_but_no_canonical = True
            if len(group_ids) == 0:
                errmsgs = set()
                correct_so = False
                for hugo in canonical_sos:
                    sos = canonical_sos[hugo].split(',')
                    if self.has_coding_so(sos):
                        correct_so = True
                        break
                if correct_so == False:
                    errmsgs.add(f'no valid so in canonical transcript')
                if genehancertargetgenes is not None \
                        and len(genehancertargetgenes) > 0 \
                        and genehancer_target_exists == False:
                    errmsgs.add(f'GeneHancer targets are not ENSG')
                if upstream_but_no_canonical:
                    errmsgs.add('5k upstream on non-canonical transcript')
                if len(csq_ensts) == 0:
                    errmsgs.add('no transcript detected')
                if 'HC' in csq_lofs:
                    correct_lof_canonical_so = False
                    for lof_i in range(len(csq_lofs)):
                        lof = csq_lofs[lof_i]
                        enst = csq_ensts[lof_i]
                        consequence = csq_consequences[lof_i]
                        hugo = csq_hugos[lof_i]
                        if hugo in canonical_enstnvs:
                            canonical = canonical_enstnvs[hugo]
                        else:
                            canonical = ''
                        if lof == 'HC' and enst.split('.')[0] == canonical\
                                and consequence not in so_ignores:
                            correct_lof_canonical_so = True
                            break
                    if correct_lof_canonical_so == False:
                        errmsgs.add('no HC lof for canonical transcript with valid so')
                no_canonical_enst = True
                for hugo in canonical_enstnvs:
                    if hugo in all_mappings:
                        mappings = all_mappings[hugo]
                        for mapping in mappings:
                            enstnv = mapping[0].split('.')[0]
                            if enstnv == canonical_enstnvs[hugo]:
                                no_canonical_enst = False
                                break
                if len(csq_hugos) > 0:
                    for enst_i in range(len(csq_ensts)):
                        enstnv = csq_ensts[enst_i].split('.')[0]
                        if enstnv.startswith('ENST') == False:
                            continue
                        hugo = csq_hugos[enst_i]
                        #if hugo in self.hugo_synonyms:
                        #    hugo = self.hugo_synonyms[hugo]
                        try:
                            if enstnv == canonical_enstnvs[hugo]:
                                no_canonical_enst = False
                                break
                        except:
                            print(f'hugo={hugo} canonical_enstnvs={canonical_enstnvs}')
                            print(f'csq_hugos={csq_hugos}')
                            print(f'row={row}')
                            raise
                if no_canonical_enst:
                    errmsgs.add('no canonical transcript')
                if len(errmsgs) == 0:
                    print(f'#################\nAn exception occurred. Please contact the OpenCRAVAT team with the following information:')
                    print(f'#exception: No gene name for {chrom} {pos} {ref} {alt}\n#row={row}\n# csq={csq}\n# row={row}\n# csq_genes={csq_genes}\n# canonical_sos={canonical_sos}\n# coding={coding}\n# csq_lofs={csq_lofs}\n# genehancertargetgenes={genehancertargetgenes}\n# csq_ensts={csq_ensts}\n# csq_consequence={csq_consequences}\n# group_ids={group_ids}\n# canonical_ensts={canonical_ensts}\n# all_mappings={all_mappings}\n# genehancer_target_exists={genehancer_target_exists}\n# errmsgs={errmsgs}')
            else:
                if chrom.startswith('chr'):
                    chrom = chrom[3:]
                    filtered_row[self.colno_to_display_chrom] = chrom
                group_ids = list(group_ids)
                group_ids.sort()
                group_ids = [v for v in group_ids if v != '']
                for group_id in group_ids:
                    filtered_row[self.colno_to_display_hugo] = group_id
                    self.data[self.level].append([v for v in list(filtered_row)])
            '''
            if chrom.startswith('chr'):
                chrom = chrom[3:]
                filtered_row[self.colno_to_display_chrom] = chrom
            group_ids = list(group_ids)
            group_ids.sort()
            group_ids = [v for v in group_ids if v != '']
            for group_id in group_ids:
                filtered_row[self.colno_to_display_hugo] = group_id
                self.data[self.level].append([v for v in list(filtered_row)])
        except Exception as e:
            print(f'#################\nAn exception occurred. Please contact the OpenCRAVAT team with the following information:')
            print(f'#exception: {e}')
            import traceback
            traceback.print_exc(file=sys.stdout)
            print(f'#row={row}')

    def end (self):
        self.dfs = {}
        for level in self.headers.keys():
            level_data = pd.DataFrame(self.data[level], columns=self.colnames_to_display[level])
            level_data = level_data.drop_duplicates()
            self.filename = f'{self.filename_prefix}.{level}.{self.filename_postfix}'
            self.filenames.append(self.filename)
            if len(level_data) > 0:
                pyreadr.write_rdata(self.filename, level_data, df_name=f'{self.filename_prefix}_{level}')
            else:
                wf = open(self.filename, 'w')
                wf.close()
        return self.filenames

def main ():
    reporter = Reporter(sys.argv)
    reporter.run()

if __name__ == '__main__':
    main()


"""
	https://dmoj.ca/problem/ccc03s3
"""
import sys; input = sys.stdin.readline
wood = int(input())
floor = []
row = int(input())
col = int(input())
for _ in range(row):
    floor.append(list(input()[:-1]))

def fix(a, b):
    counter, add = 0, 0
    while len(floor[a])-b != add and floor[a][b+add] == ".":
        floor[a][b+add] = "X"
        add += 1
        counter += 1
    for x in range(add):
        if a+1 < len(floor) and floor[a+1][b+x] == ".":
            change = 0
            while floor[a+1][b+x-change] == "." and b+x-change > -1:
                change += 1
            counter += fix(a+1, b+x-change+1)
        if a-1 > -1 and floor[a-1][b+x] == ".":
            change = 0
            while floor[a-1][b+x-change] == "." and b+x-change > -1:
                change += 1
            counter += fix(a-1, b+x-change+1)
    return counter

areas = []
for i in range(0, row):
    for j in range(0, col):
        if floor[i][j] == ".":
            areas.append(fix(i, j))
areas = sorted(areas, reverse=True)
total = 0
amount = 0
for num in areas:
    if total+num <= wood:
        total += num
        amount += 1
    else:
        break
print(amount, "rooms," if amount != 1 else "room,", wood-total, "square metre(s) left over")


#!/usr/bin/env python3.2

import os
import sys
import time
import math

# Example for Unix or Mac OS X
shared_dir = os.environ['HOME'] + '/Dropbox/dropbox-buf'

# Example for Windows
#shared_dir = r'C:\Documents and Settings\userid\My Documents\My Dropbox\dropbox-buf'

chunk_size = 200 * 1024 * 1024   # 200 MB

cur_file = shared_dir + os.sep + '.cur'

def wait_disappear(fname):
    while os.path.exists(fname):
        time.sleep(1)

def split_file(fname, n_chunk):
    for n in range(n_chunk):
        part_file = shared_dir + os.sep + '.part_%03d' % n
        with open(fname, 'rb') as f_in, open(part_file, 'wb') as f_out:
            f_in.seek(n * chunk_size)
            f_out.write(f_in.read(chunk_size))
        wait_disappear(part_file)

# just being lazy and assume '/' works universally
def univ_path(path):
    if os.sep == '/':
        return path
    return '/'.join(path.split(os.sep))

def send_dir(dst_dir):
    print(time.strftime('%m/%d %H:%M:%S', time.localtime()), end=' ')
    print('d:', os.path.normpath(dst_dir))
    with open(cur_file, 'w', encoding='utf-8') as f:
        f.write('d\n')   # directory
        f.write(univ_path(dst_dir) + '\n')
    wait_disappear(cur_file)

def send_file(src_path, dst_dir, fname):
    print(time.strftime('%m/%d %H:%M:%S', time.localtime()), end=' ')
    print('f:', os.path.normpath(src_path))
    fsize = os.path.getsize(src_path)
    n_chunk = max(1, int(math.ceil(fsize / chunk_size)))

    with open(cur_file, 'w', encoding='utf-8') as f:
        f.write('f\n')   # file
        f.write(univ_path(dst_dir) + '\n')
        f.write(fname + '\n')
        f.write('%d\n' % n_chunk)

    split_file(src_path, n_chunk)
    wait_disappear(cur_file)

def main():
    for arg in sys.argv[1:]:
        if os.path.isfile(arg):
            send_file(arg, '', os.path.basename(arg))
        elif os.path.isdir(arg):
            rootdir = arg
            skip = len(rootdir)
            last_dir = ''
            dirs = [d for d in rootdir.split(os.sep) if d not in ['.', '']]
            if dirs and dirs[-1] != '..':
                last_dir = dirs[-1]
                send_dir(last_dir)

            for dirpath, dnames, fnames in os.walk(rootdir, followlinks=True):
                dirpath = dirpath.rstrip(os.sep)
                dst_dir = last_dir + os.sep + dirpath[skip:].lstrip(os.sep)
                dst_dir = dst_dir.rstrip(os.sep)   # for dirpath[skip:] == ''

                for dname in dnames:
                    send_dir(dst_dir + os.sep + dname)

                for fname in fnames:
                    send_file(dirpath + os.sep + fname, dst_dir, fname)

    print(time.strftime('%m/%d %H:%M:%S', time.localtime()))
    with open(cur_file, 'w', encoding='utf-8') as f:
        f.write('q\n')   # quit

if __name__ == '__main__':
    main()


#!/usr/bin/env python3
from __future__ import absolute_import, division, print_function, unicode_literals

from tensorflow.keras import backend as K
from tensorflow.keras import Sequential
from tensorflow.keras.layers import Flatten, Dense,Dropout, Conv1D,BatchNormalization,Input,LeakyReLU
import tensorflow as tf
import numpy as np

def basic_window(window_size):
    m = Sequential()
    m.add(Input(shape=(window_size*2+1,4)))
    m.add(Flatten())
    m.add(Dense(128, activation='relu'))
    m.add(Dropout(0.5))
    m.add(Dense(32, activation='relu'))
    m.add(Dense(1, activation = 'sigmoid'))
    return m

def basic_window_leakyrelu(window_size):
    m = Sequential()
    m.add(Input(shape=(window_size*2+1,4)))
    m.add(Flatten())
    m.add(Dense(128))
    m.add(LeakyReLU(alpha=0.05))
    m.add(Dropout(0.5))
    m.add(Dense(32))
    m.add(LeakyReLU(alpha=0.05))
    m.add(Dense(1, activation = 'sigmoid'))
    return m

def conv_window(window_size):
    m = Sequential()
    m.add(Input(shape=(window_size*2+1,4)))
    m.add(Conv1D(128, 5, padding='same', activation='relu'))
    m.add(BatchNormalization())
    m.add(Dropout(0.5))
    m.add(Conv1D(128, 3, padding='same', activation='relu'))
    m.add(BatchNormalization())
    m.add(Dropout(0.5))
    m.add(Conv1D(64, 3, padding='same', activation='relu'))
    m.add(BatchNormalization())
    m.add(Dropout(0.5))
    m.add(Flatten())
    m.add(Dense(128, activation='relu'))
    m.add(Dropout(0.5))
    m.add(Dense(32, activation='relu'))
    m.add(Dense(1, activation = 'sigmoid'))
    return m


#!/usr/bin/python

import os
import sys
import subprocess
import datetime
import math

try:
    # Change the next line if your config folder is not $HOME/.config
    config_directory = f"{os.environ['HOME']}/.config"

    # If $HOME isn't set, os.environ['HOME'] will cause an error

except KeyError:
    print("The environment variable $HOME is not set.")
    print("You need to change the config_directory variable.")
    print("See README.md on github (https://github.com/michaelskyba/kvrg-avg) for more information.")

    sys.exit(1)

# If config_directory doesn't exist, print an error an exit
if not os.path.isdir(config_directory):
    print(f"The config directory that is set ({config_directory}) does not exist.")
    print("You need to change the config_directory variable.")
    print("See README.md on github (https://github.com/michaelskyba/kvrg-avg) for more information.")

    sys.exit(1)

# If config_director/avg/trackers does not exist, create it
# mkdir without -p will raise an error if config_directory/avg doesn't exist first
if not os.path.isdir("f{config_directory}/avg/trackers"):
    subprocess.run(["mkdir", "-p", f"{config_directory}/avg/trackers"])

# config for either average (default) or ETA for date trackers in avg list

date_list_ETA_set = False

if os.path.isfile(f"{config_directory}/avg/config"):
    with open(f"{config_directory}/avg/config", "r") as config_file:
        if "ETA\n" in config_file.readlines():
            date_list_ETA_set = True

# Starts checking for command-line arguments

# You ran "avg" without any extra arguments, or you ran "avg list"
# running something like "avg list foo bar" is the same
if len(sys.argv) == 1 or sys.argv[1] == "list":

    # Get the tracker names by looking in config/avg/trackers
    tracker_names = os.listdir(f"{config_directory}/avg/trackers")

    # Alert the user if they have no trackers
    if not tracker_names:
        print("You have no trackers.")
        print("Use 'avg create \"<name>\" [\"<description>\"]' to create one.")
        sys.exit(1)

    # Print the tracker names and their average values, if the user has a tracker
    else:
        for tracker in tracker_names:
            with open(f"{config_directory}/avg/trackers/{tracker}", "r") as tracker_file:
                tracker_lines = tracker_file.readlines()

                if len(tracker_lines) > 2 and tracker_lines[2].strip() == "date":
                    if not date_list_ETA_set:
                        # convert to human-readable
                        seconds = int(tracker_lines[1].strip())

                        if seconds == 0:
                            output = seconds
                            print(f"{tracker} - {output}")
                            continue

                        minutes = math.floor(seconds / 60)

                        if minutes == 0:
                            output = seconds
                            print(f"{tracker} - {output}")
                            continue

                        hours = math.floor(minutes / 60)

                        if hours == 0:
                            output = f"{minutes} minutes and {seconds - minutes * 60} seconds"
                            print(f"{tracker} - {output}")
                            continue

                        days = math.floor(hours / 24)

                        if days == 0:
                            output = f"{hours} hours and {minutes - hours * 60} minutes"
                            print(f"{tracker} - {output}")
                            continue

                        months = math.floor(days / 30)

                        if months == 0:
                            output = f"{days} days and {hours - days * 24} hours"
                            print(f"{tracker} - {output}")
                            continue

                        years = math.floor(months / 12)

                        if years == 0:
                            output = f"{months} months and {days - months * 30} days"
                            print(f"{tracker} - {output}")
                            continue

                        output = f"{years} years and {months - years * 12} months"
                        print(f"{tracker} - {output}")

                    elif len(tracker_lines) > 4:
                        # we want the ETA
                        argument = tracker_lines[len(tracker_lines) - 1].strip()

                        date = []
                        date.append(argument[0:4])
                        date.append(argument[5:7])
                        date.append(argument[8:10])
                        date.append(argument[11:13])
                        date.append(argument[14:16])

                        # Make sure everything is an integer
                        int_date = []
                        for part in date:
                            int_date.append(int(part))
                        date = []
                        for part in int_date:
                            date.append(part)

                        latest_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])
                        average = tracker_lines[1].strip()
                        average = int(average)
                        average = datetime.timedelta(seconds=average)

                        print(f"{tracker} - {latest_date + average}")

                    else:
                        # not enough intervals for an ETA
                        print(f"{tracker} - 0")

                else:
                    print(f"{tracker} - {tracker_lines[1].strip()}")
        sys.exit(0)

# You ran "avg create ..."
if sys.argv[1] == "create":
    # If user runs "avg create"
    if len(sys.argv) == 2:
        print("You need a <name> argument.")
        sys.exit(1)

    # Check if config/avg/trackers contains a tracker called <name>
    if sys.argv[2] in os.listdir(f"{config_directory}/avg/trackers"):
        print(f"Tracker with name '{sys.argv[2]}' already exists.")
        sys.exit(1)

    # Create a file with name <name> in config/avg/trackers
    with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "w") as tracker_file:

        # Saves the description if the user provided one

        # the description is the fourth argument, so the length has to be > 3 (>=4)
        # and sys.argv[3] will get the fourth argument (3rd when not including "avg")

        if len(sys.argv) > 3 and sys.argv[3] != "date":
            description = sys.argv[3]

        # Date tracker with description

        elif len(sys.argv) > 4:
            description = sys.argv[4]

        # No description

        else:
            description = "This tracker does not have a description."

        # avg create ... date

        if len(sys.argv) > 3 and sys.argv[3] == "date":
            tracker_file.write(f"{description}\n0\n{sys.argv[3]}\n")

        else:
            tracker_file.write(f"{description}\n0\n")

    sys.exit(0)

# You ran "avg delete ..."
if sys.argv[1] == "delete":
    # If user runs "avg delete"
    if len(sys.argv) == 2:
        print("You need a <name> argument.")
        sys.exit(1)

    # Removes the tracker file
    try:
        os.remove(f"{config_directory}/avg/trackers/{sys.argv[2]}")

    # Tracker does not exist
    except FileNotFoundError:
        print(f"There is no such tracker '{sys.argv[2]}'.")
        sys.exit(1)

    sys.exit(0)

# You ran "avg push ..."
if sys.argv[1] == "push":
    # If user runs "avg push"
    if len(sys.argv) == 2:
        print("You need a <name> and a <one or more values> argument.")
        sys.exit(1)

    # Check if config/avg/trackers contains a tracker called <name>
    if sys.argv[2] not in os.listdir(f"{config_directory}/avg/trackers"):
        print(f"Tracker with name '{sys.argv[2]}' does not exist.")
        sys.exit(1)

    # If user runs "avg push <name>"
    if len(sys.argv) == 3:
        print("You need a <one or more values> argument.")
        sys.exit(1)

    # Check type of tracker
    with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "r") as tracker_file:
        tracker_lines = tracker_file.readlines()

        if len(tracker_lines) > 2 and tracker_lines[2].strip() == "date":
            tracker_type = "date"
        else:
            tracker_type = "normal"

    # Makes sure all values are numbers if it's a normal tracker
    if tracker_type == "normal":
        for index, argument in enumerate(sys.argv):
            if index > 2:
                try:
                    float_argument = float(argument)
                except ValueError:
                    print(f"Value '{argument}' is not a number.")
                    sys.exit(1)

    # Makes sure all values are dates (or "now") if it's a date tracker
    else:
        for index, argument in enumerate(sys.argv):
            if index > 2:
                # Skip it if they type "now"
                if argument == "now":
                    continue

                # Make sure the date is the right length
                if len(argument) != 16:
                    print(f"Value '{argument}' is invalid.")
                    sys.exit(1)

                # Test if they put slashes in the right places
                for slash in [4, 7, 10, 13]:
                    if argument[slash] != "/":
                        print(f"Value '{argument}' is invalid.")
                        sys.exit(1)

                date = []

                date.append(argument[0:4])
                date.append(argument[5:7])
                date.append(argument[8:10])
                date.append(argument[11:13])
                date.append(argument[14:16])

                # Make sure they put integers as the date values (month, day, etc.)
                for date_index, value in enumerate(date):
                    try:
                        date[date_index] = int(value)

                    except ValueError:
                        print(f"Value '{value}' is not a number.")
                        sys.exit(1)

                # Test if user's date is a real date
                try:
                    final_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])

                except ValueError:
                    print(f"Value '{argument}' is invalid.")
                    sys.exit(1)

    # Appends values to tracker file
    # A separate loop is used to avoid appending a few of the arguments before
    #   finding out one of them is invalid
    for index, argument in enumerate(sys.argv):
        if index > 2:
            with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "a") as tracker_file:
                if argument == "now":
                    # cdate -- current date
                    cdate = datetime.datetime.now()
                    # zfill puts in zeros accordingly - '14'.zfill(3) = '014'
                    passed_argument = f"{cdate.year}/{str(cdate.month).zfill(2)}/{str(cdate.day).zfill(2)}/{str(cdate.hour).zfill(2)}/{str(cdate.minute).zfill(2)}"

                else:
                    passed_argument = argument

                tracker_file.write(f"{passed_argument}\n")

    # Update average

    # Calculate the correct average
    with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "r") as tracker_file:

        new_tracker_file_lines = tracker_file.readlines()

        # Get the number of lines
        tracker_file_num_of_lines = len(new_tracker_file_lines)

        # Normal tracker
        if tracker_type == "normal":
            # Add the values
            value_sum = 0

            for index, value in enumerate(new_tracker_file_lines):
                if index > 1:
                    value_sum += float(value)

            # Actual computation
            average = value_sum * 100 / (tracker_file_num_of_lines - 2)
            average = round(average)
            average = average / 100
            # it needs to be tracker_file_num_of_lines - 2 because the
            #   description (first line) and average (second line) aren't entries

            new_tracker_file_lines[1] = f"{average}\n"

        # Date tracker that has at least two entries
        # You can't calculate an average interval with only one entry, because intervals = entries - 1
        # Date trackers have a description (first line), an average (second line), and a date identifier (third line)
        # That's three lines
        # Finally, the entries are listed. With one entry, you have 3 + 1 = 4 lines
        # So, to have at least two entries, you need to have more than one entry, or more than 4 lines:
        elif tracker_file_num_of_lines > 4:
            # Add the intervals between dates
            # stored as seconds
            intervals = []

            for index, value in enumerate(new_tracker_file_lines):
                # Entries start on the fourth line, so index has to be at least 3
                # lines - 1 is used to avoid later_date being out of range
                if index > 2 and index < (tracker_file_num_of_lines - 1):
                    # print(f"find the distance between {value} and {new_tracker_file_lines[index + 1]}")

                    # Get the earlier date in the right format (index)

                    argument = value

                    date = []
                    date.append(argument[0:4])
                    date.append(argument[5:7])
                    date.append(argument[8:10])
                    date.append(argument[11:13])
                    date.append(argument[14:16])

                    # Make sure everything is an integer
                    int_date = []
                    for part in date:
                        int_date.append(int(part))
                    date = []
                    for part in int_date:
                        date.append(part)

                    earlier_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])

                    # Get the later date in the right format (index + 1)

                    argument = new_tracker_file_lines[index + 1]

                    date = []
                    date.append(argument[0:4])
                    date.append(argument[5:7])
                    date.append(argument[8:10])
                    date.append(argument[11:13])
                    date.append(argument[14:16])

                    # Make sure everything is an integer
                    int_date = []
                    for part in date:
                        int_date.append(int(part))
                    date = []
                    for part in int_date:
                        date.append(part)

                    later_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])

                    # Add the interval to the intervals list
                    intervals.append((later_date - earlier_date).total_seconds())

            # calculate the average of the second intervals
            interval_sum = 0

            for interval in intervals:
                interval_sum += interval

            average = interval_sum / (len(intervals))
            average = round(average)

            # write to tracker file
            new_tracker_file_lines[1] = f"{average}\n"

    # Update the average in the file
    with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "w") as tracker_file:
        tracker_file.writelines(new_tracker_file_lines)

    sys.exit(0)

# You ran "avg get ..."
if sys.argv[1] == "get":
    # If user runs "avg get"
    if len(sys.argv) == 2:
        print("You need an <attribute> argument and a <name> argument.")
        sys.exit(1)

    # Check if user gave a valid attribute
    if sys.argv[2] not in ["description", "average", "type", "ETA"]:
        print(f"No such attribute, '{sys.argv[2]}'.")
        sys.exit(1)

    # If user runs "avg get <attribute>"
    if len(sys.argv) == 3:
        print("You need a <name> argument.")
        sys.exit(1)

    # Checks if user gave a valid tracker name
    if sys.argv[3] not in os.listdir(f"{config_directory}/avg/trackers"):
        print(f"Tracker with name '{sys.argv[3]}' does not exist.")
        sys.exit(1)

    # Use has a valid tracker name
    with open(f"{config_directory}/avg/trackers/{sys.argv[3]}", "r") as tracker_file:
        tracker_lines = tracker_file.readlines()

        # User ran "avg get description <name>"
        if sys.argv[2] == "description":
            print(tracker_lines[0].strip())

        # User ran "avg get average <name>"
        if sys.argv[2] == "average":
            print(tracker_lines[1].strip())

        # User ran "avg get type <name>"
        if sys.argv[2] == "type":
            if len(tracker_lines) > 2 and tracker_lines[2].strip() == "date":
                print("date")
            else:
                print("normal")

        if sys.argv[2] == "ETA":
            if len(tracker_lines) > 4:
                argument = tracker_lines[len(tracker_lines) - 1].strip()

                date = []
                date.append(argument[0:4])
                date.append(argument[5:7])
                date.append(argument[8:10])
                date.append(argument[11:13])
                date.append(argument[14:16])

                # Make sure everything is an integer
                int_date = []
                for part in date:
                    int_date.append(int(part))
                date = []
                for part in int_date:
                    date.append(part)

                latest_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])
                average = tracker_lines[1].strip()
                average = int(average)
                average = datetime.timedelta(seconds=average)

                print(latest_date + average)

            else:
                # No intervals
                print("0")

    sys.exit(0)

# You ran "avg info ..."
if sys.argv[1] == "info":
    # If user runs "avg info"
    if len(sys.argv) == 2:
        print("You need a <name> argument.")
        sys.exit(1)

    # Checks if user gave a valid tracker name
    if sys.argv[2] not in os.listdir(f"{config_directory}/avg/trackers"):
        print(f"Tracker with name '{sys.argv[2]}' does not exist.")
        sys.exit(1)

    # Lists attributes
    with open(f"{config_directory}/avg/trackers/{sys.argv[2]}", "r") as tracker_file:
        tracker_lines = tracker_file.readlines()

        print(f"Name: {sys.argv[2]}")
        print(f"Description: {tracker_lines[0].strip()}")

        if len(tracker_lines) > 2 and tracker_lines[2].strip() == "date":
            if len(tracker_lines) > 4:
                # average

                # convert to human-readable
                print_average = True

                seconds = int(tracker_lines[1].strip())

                if seconds == 0 and print_average:
                    output = seconds
                    print(f"Average: {output}")
                    print_average = False

                minutes = math.floor(seconds / 60)

                if minutes == 0 and print_average:
                    output = seconds
                    print(f"Average: {output}")
                    print_average = False

                hours = math.floor(minutes / 60)

                if hours == 0 and print_average:
                    output = f"{minutes} minutes and {seconds - minutes * 60} seconds"
                    print(f"Average: {output}")
                    print_average = False

                days = math.floor(hours / 24)

                if days == 0 and print_average:
                    output = f"{hours} hours and {minutes - hours * 60} minutes"
                    print(f"Average: {output}")
                    print_average = False

                months = math.floor(days / 30)

                if months == 0 and print_average:
                    output = f"{days} days and {hours - days * 24} hours"
                    print(f"Average: {output}")
                    print_average = False

                years = math.floor(months / 12)

                if years == 0 and print_average:
                    output = f"{months} months and {days - months * 30} days"
                    print(f"Average: {output}")
                    print_average = False

                output = f"{years} years and {months - years * 12} months"
                if print_average:
                    print(f"Average: {output}")

                # ETA
                argument = tracker_lines[len(tracker_lines) - 1].strip()

                date = []
                date.append(argument[0:4])
                date.append(argument[5:7])
                date.append(argument[8:10])
                date.append(argument[11:13])
                date.append(argument[14:16])

                # Make sure everything is an integer
                int_date = []
                for part in date:
                    int_date.append(int(part))
                date = []
                for part in int_date:
                    date.append(part)

                latest_date = datetime.datetime(date[0], date[1], date[2], date[3], date[4])
                average = tracker_lines[1].strip()
                average = int(average)
                average = datetime.timedelta(seconds=average)

                print(f"ETA: {latest_date + average}")

            else:
                # No intervals
                print("Average: 0")
                print("ETA: 0")

            # type
            print("This tracker is a date tracker.")
        else:
            print(f"Average: {tracker_lines[1].strip()}")
            print("This is a normal tracker.")

    sys.exit(0)

# Invalid command
print(f"'{sys.argv[1]}' is not a kvrg-avg command. See the README for a list of valid commands.")
sys.exit(1)


#!/usr/bin/env python
# This file is part of Responder
# Original work by Laurent Gaffie - Trustwave Holdings
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from UserDict import DictMixin

class OrderedDict(dict, DictMixin):

    def __init__(self, *args, **kwds):
        if len(args) > 1:
            raise TypeError('expected at most 1 arguments, got %d' % len(args))
        try:
            self.__end
        except AttributeError:
            self.clear()
        self.update(*args, **kwds)

    def clear(self):
        self.__end = end = []
        end += [None, end, end]
        self.__map = {}
        dict.clear(self)

    def __setitem__(self, key, value):
        if key not in self:
            end = self.__end
            curr = end[1]
            curr[2] = end[1] = self.__map[key] = [key, curr, end]
        dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        dict.__delitem__(self, key)
        key, prev, next = self.__map.pop(key)
        prev[2] = next
        next[1] = prev

    def __iter__(self):
        end = self.__end
        curr = end[2]
        while curr is not end:
            yield curr[0]
            curr = curr[2]

    def __reversed__(self):
        end = self.__end
        curr = end[1]
        while curr is not end:
            yield curr[0]
            curr = curr[1]

    def popitem(self, last=True):
        if not self:
            raise KeyError('dictionary is empty')
        if last:
            key = reversed(self).next()
        else:
            key = iter(self).next()
        value = self.pop(key)
        return key, value

    def __reduce__(self):
        items = [[k, self[k]] for k in self]
        tmp = self.__map, self.__end
        del self.__map, self.__end
        inst_dict = vars(self).copy()
        self.__map, self.__end = tmp
        if inst_dict:
            return self.__class__, (items,), inst_dict
        return self.__class__, (items,)

    def keys(self):
        return list(self)

    setdefault = DictMixin.setdefault
    update = DictMixin.update
    pop = DictMixin.pop
    values = DictMixin.values
    items = DictMixin.items
    iterkeys = DictMixin.iterkeys
    itervalues = DictMixin.itervalues
    iteritems = DictMixin.iteritems

    def __repr__(self):
        if not self:
            return '%s()' % (self.__class__.__name__,)
        return '%s(%r)' % (self.__class__.__name__, self.items())

    def copy(self):
        return self.__class__(self)

    @classmethod
    def fromkeys(cls, iterable, value=None):
        d = cls()
        for key in iterable:
            d[key] = value
        return d

    def __eq__(self, other):
        if isinstance(other, OrderedDict):
            return len(self)==len(other) and \
                   min(p==q for p, q in  zip(self.items(), other.items()))
        return dict.__eq__(self, other)

    def __ne__(self, other):
        return not self == other


# Solution with Backtracking

import unittest

class Solution:
    def subsetsWithDup(self, nums):
        res = []
        nums.sort()
        self.dfs(nums, 0, [], res)
        return res

    def dfs(self, nums, idx, path, res):
        res.append(path)
        for i in range(idx, len(nums)):
            if i > idx and nums[i] == nums[i-1]:
                continue
            self.dfs(nums, i+1, path+[nums[i]], res)

class Test(unittest.TestCase):

    def test_subsetsWithDup(self):
        ans = Solution()
        self.assertCountEqual(ans.subsetsWithDup([1,2,3]), [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]])
        self.assertCountEqual(ans.subsetsWithDup([]), [[]])
        self.assertCountEqual(ans.subsetsWithDup([3,1,2]), [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]])
        self.assertCountEqual(ans.subsetsWithDup([3,6,1,7]), [[],[1],[1,3],[1,3,6],[1,3,6,7],[1,3,7],[1,6],[1,6,7],[1,7],[3],[3,6],[3,6,7],[3,7],[6],[6,7],[7]])
        self.assertCountEqual(ans.subsetsWithDup([1]), [[],[1]])
        self.assertCountEqual(ans.subsetsWithDup([1,2,2]), [[],[1],[1,2],[1,2,2],[2],[2,2]])
        self.assertCountEqual(ans.subsetsWithDup([1,2,2,2]), [[],[1],[1,2],[1,2,2],[1,2,2,2],[2],[2,2],[2,2,2]])

if __name__ == "__main__":

    unittest.main()


#!/usr/bin/python3

import pprint
import code
from googleapiclient.discovery import build

def main():
	key=importkey("googlecustomsearch")
	if (key is None):
		print("no key")
		sys.exit()
	print(key)
	service = build("customsearch", "v1", developerKey=key)
	#res = service.cse().list(q='saunders', fileType="pdf", siteSearch="saunders.rit.edu", cx='011869109180613770264:mfpcqwsg38w', start=99).execute()
	res = service.cse().list(q='site:saunders.rit.edu filetype:pdf', cx='011869109180613770264:mfpcqwsg38w', filter='0', start=1).execute()
	for result in res['items']:
		print(result['link'])

def importkey(keyid):
	key=None
	myfile = open("api.keys", "r")
	for line in myfile:
		if (len(line) > len(keyid)):
			if (line[0:len(keyid)-1]==keyid):
				key = line[len(keyid)+1:]
	return key

if __name__ == '__main__':
	main()


# Описание класса
class MyClass:
    # Конструктор
    def __init__(self, n="Белый"):
        self.name = n
        print("Создан объект:", self.name)
    # Деструктор
    def __del__(self):
        print("Удалён объект:", self.name)
# Функция

def create(n):
    obj = MyClass(n)

# Создание объектов
A = MyClass()
B = MyClass("Красный")
C = MyClass("Синий")
# Вызов функции
create("Жёлтый")
# Полю присваивается новое значение
C.name = "Зелёный"
# Удаление объектов
del A
del B
del C
create("Жопка")


from functools import partial
from typing import Callable

import numpy as np
import rospy
from stable_baselines3.common.vec_env import VecNormalize
from supersuit.vector import ConcatVecEnv, MarkovVectorEnv
from supersuit.vector.sb3_vector_wrapper import SB3VecEnvWrapper

class MarkovVectorEnv_patched(MarkovVectorEnv):
    """Patched environment wrapper which creates the correct API for vector environments. Dones for dead agents are returned as True instead as False."""

    def step(self, actions):
        agent_set = set(self.par_env.agents)
        act_dict = {
            agent: actions[i]
            for i, agent in enumerate(self.par_env.possible_agents)
            if agent in agent_set
        }
        observations, rewards, dones, infos = self.par_env.step(act_dict)

        # adds last observation to info where user can get it
        if all(dones.values()):
            for agent, obs in observations.items():
                infos[agent]["terminal_observation"] = obs

        rews = np.array(
            [rewards.get(agent, 0) for agent in self.par_env.possible_agents],
            dtype=np.float32,
        )
        # we changed the default value to true instead of false
        dns = np.array(
            [dones.get(agent, True) for agent in self.par_env.possible_agents],
            dtype=np.uint8,
        )
        infs = [infos.get(agent, {}) for agent in self.par_env.possible_agents]

        if all(dones.values()):
            observations = self.reset()
        else:
            observations = self.concat_obs(observations)
        assert (
            self.black_death
            or self.par_env.agents == self.par_env.possible_agents
        ), "MarkovVectorEnv does not support environments with varying numbers of active agents unless black_death is set to True"
        return observations, rews, dns, infs

def vec_env_create(
    env_fn: Callable,
    agent_list_fn: Callable,
    num_robots: int,
    num_cpus: int,
    num_vec_envs: int,
    PATHS: dict,
) -> SB3VecEnvWrapper:
    """Function which vectorizes a given environment function in multiple parallel environments.

    Args:
        env_fn (Callable): Function that initializes an environment with wrappers
        agent_list_fn (Callable): Object containing the program arguments
        num_robots (int): Number of robots in the environment
        num_cpus (int): Maximal number of CPUs to use (Currently only process is used anyhow)
        num_vec_envs (int): Number of parallel environments to spawn
        PATHS (dict): Dictionary which holds hyperparameters for the experiment

    Returns:
        SB3VecEnvWrapper: Vectorized environments following the SB3 VecEnv API. Each each robot in an environment \
            poses as an environment in the vector.
    """
    env_list_fns = [
        partial(
            env_fn,
            ns=f"sim_{i}",
            num_agents=num_robots,
            agent_list_fn=agent_list_fn,
            PATHS=PATHS,
        )
        for i in range(1, num_vec_envs + 1)
    ]
    env = env_list_fns[0]()
    action_space = env.observation_space
    observation_space = env.observation_space
    metadata = env.metadata

    num_cpus = min(num_cpus, num_vec_envs)
    rospy.init_node("train_env", disable_signals=False, anonymous=True)
    vec_env = ConcatVecEnv(env_list_fns, observation_space, action_space)
    return SB3VecEnvWrapper(vec_env)


#!/usr/local/bin/ python3
# -*- coding:utf-8 -*-
# __author__ = "zenmeder"

class Solution(object):
	def singleNumber(self, nums):
		"""
		:type nums: List[int]
		:rtype: int
		"""
		length = len(nums)
		d = dict()
		for i in range(length):
			if nums[i] in d:
				del d[nums[i]]
			else:
				d[nums[i]] = 1
		return d.popitem()[0]
	def singleNumber2(self,nums):
		# 两个相同的数异或的结果是0，所有的数两两异或，最后结果必然是不同的那个数
		# 空间复杂度减小，但是用时较长
		result = 0
		for num in nums:
			result ^= num
		return result
solution = Solution()
print(solution.singleNumber2([1,2,2,2]))


import time
import pandas as pd
import cflib.crtp
from cflib.crazyflie import Crazyflie
from cflib.crazyflie.log import LogConfig
from cflib.crazyflie.syncCrazyflie import SyncCrazyflie
from cflib.crazyflie.syncLogger import SyncLogger
from cflib.utils import uri_helper
from cflib.positioning.position_hl_commander import PositionHlCommander
import numpy as np
from paths import path_pars
# URI to the Crazyflie to connect to
uri = uri_helper.uri_from_env(default='radio://0/80/2M/E7E7E7E701')

path_np = np.array(np.genfromtxt('/home/rajpal/CF programs/crazypaths/paths/circle_waypoints.csv',delimiter=',', skip_header=1, dtype=float).tolist())
# sequence = path_np[:,1:]

t_end = 20
hieght = 0.4
r_init = np.array([0,0,hieght])
t_lift = 2
# Change the sequence according to your setup
#             x    y    z  YAW
# sequence = [
#     (0.0, 0.0, 0.4, 0),
#     (0.0, 0.0, 0.5, 0),
#     (0.1, -0.1, 0.5, 0),
#     (0.1, 0.1, 0.5, 0),
#     (-0.1, 0.1, 0.5, 0),
#     (-0.1, -0.1, 0.5, 0),
#     (0.0, 0.0, 0.5, 0),
#     (0.0, 0.0, 0.4, 0),
#     (0.0, 0.0, 0.0, 0.0)
# ]

def wait_for_position_estimator(scf):
    print('Waiting for estimator to find position...')

    log_config = LogConfig(name = 'Kalman Variance', period_in_ms = 10)
    log_config.add_variable('kalman.varPX', 'float')
    log_config.add_variable('kalman.varPY', 'float')
    log_config.add_variable('kalman.varPZ', 'float')

    var_y_history = [1000] * 10
    var_x_history = [1000] * 10
    var_z_history = [1000] * 10

    threshold = 0.001

    with SyncLogger(scf, log_config) as logger:
        for log_entry in logger:
            data = log_entry[1]

            var_x_history.append(data['kalman.varPX'])
            var_x_history.pop(0)
            var_y_history.append(data['kalman.varPY'])
            var_y_history.pop(0)
            var_z_history.append(data['kalman.varPZ'])
            var_z_history.pop(0)

            min_x = min(var_x_history)
            max_x = max(var_x_history)
            min_y = min(var_y_history)
            max_y = max(var_y_history)
            min_z = min(var_z_history)
            max_z = max(var_z_history)

            # print("{} {} {}".
            #       format(max_x - min_x, max_y - min_y, max_z - min_z))

            if (max_x - min_x) < threshold and (
                    max_y - min_y) < threshold and (
                    max_z - min_z) < threshold:
                break

def reset_estimator(scf):
    cf = scf.cf
    cf.param.set_value('kalman.resetEstimation', '1')
    time.sleep(0.1)
    cf.param.set_value('kalman.resetEstimation', '0')

    wait_for_position_estimator(cf)

def position_callback(timestamp, data, logconf):
    x = data['kalman.stateX']
    y = data['kalman.stateY']
    z = data['kalman.stateZ']
    print('pos: ({}, {}, {})'.format(x, y, z))

def start_position_printing(scf):
    log_conf = LogConfig(name='Position', period_in_ms=10)
    log_conf.add_variable('kalman.stateX', 'float')
    log_conf.add_variable('kalman.stateY', 'float')
    log_conf.add_variable('kalman.stateZ', 'float')

    scf.cf.log.add_config(log_conf)
    log_conf.data_received_cb.add_callback(position_callback)
    log_conf.start()

def run_sequence(scf):
    cf = scf.cf
    while True:
        t_now = time.time()
        t = t_now-t_in
        print(t)

        rd,_,_ = path_pars(t-t_lift,t_end,c = 0.2,tilt=0,rd_init = r_init,shape = 'lissajous')
        print('Setting position {},time {}'.format(rd,t))

        if t < t_lift:
            for i in range(10):
                cf.commander.send_position_setpoint(r_init[0],
                                                    r_init[1],
                                                    r_init[2],
                                                    0)
                time.sleep(0.001)
        elif t < 2*t_end + t_lift:
            for i in range(10):
                cf.commander.send_position_setpoint(rd[0],
                                                rd[1],
                                                hieght,
                                                0)
                time.sleep(0.001)
        elif t < 2*t_end + t_lift + 1:
            for i in range(10):
                cf.commander.send_position_setpoint(r_init[0],
                                                    r_init[1],
                                                    0.05,
                                                    0)
                time.sleep(0.001)
        elif t >= 2*t_end + t_lift + 1:
            break

    cf.commander.send_stop_setpoint()
    # Make sure that the last packet leaves before the link is closed
    # since the message queue is not flushed before closing
    time.sleep(0.1)

if __name__ == '__main__':
    cflib.crtp.init_drivers()
    t_in = time.time()
    with SyncCrazyflie(uri, cf=Crazyflie(rw_cache='./cache')) as scf:
        reset_estimator(scf)
        # start_position_printing(scf)
        run_sequence(scf)