import pandas as pd
from termcolor import colored
import sys
import os
import logging
import json
import math
import numpy as np
from typing import Tuple

from ampyutils import amutils
from glab import glab_constants as glc
from GNSS import wgs84

__author__ = 'amuls'

def statistics_glab_outfile(df_outp: pd.DataFrame, logger: logging.Logger) -> Tuple[dict, dict]:
    """
    splitStatusFile splits the statistics file into the POS, SAT, CLK & VELACC parts
    """
    cFuncName = colored(os.path.basename(__file__), 'yellow') + ' - ' + colored(sys._getframe().f_code.co_name, 'green')

    logger.info('{func:s}: calculating statistics of OUTPUT messages'.format(func=cFuncName))

    # dictionary containing the statistics
    dStats = {}
    dStats['dop_bin'] = statistics_dopbin(df_dop_enu=df_outp[glc.dgLab['OUTPUT']['XDOP'] + glc.dgLab['OUTPUT']['dENU'] + glc.dgLab['OUTPUT']['sdENU']], logger=logger)
    dStats['crd'] = statistics_coordinates(df_crd=df_outp[glc.dgLab['OUTPUT']['llh'] + glc.dgLab['OUTPUT']['dENU'] + glc.dgLab['OUTPUT']['sdENU'] + glc.dgLab['OUTPUT']['UTM']], logger=logger)

    # print(dStats.keys())
    # print(dStats['crd'].keys())
    # print(dStats['crd']['dN0'].keys())
    # create the dNEU information to store in the glabng output database
    dDB_crd = {}
    for crd in glc.dgLab['OUTPUT']['dENU']:
        dDB_crd[crd] = '{crd:s},'.format(crd=crd)
        dDB_crd[crd] += '{mean:+.3f},'.format(mean=dStats['crd'][crd]['mean'])
        dDB_crd[crd] += '{std:+.3f},'.format(std=dStats['crd'][crd]['std'])
        dDB_crd[crd] += '{max:+.3f},'.format(max=dStats['crd'][crd]['max'])
        dDB_crd[crd] += '{min:+.3f},'.format(min=dStats['crd'][crd]['min'])
        # print('crd from glc = {crd:s} - {info:s}'.format(crd=crd, info=dDB_crd[crd]))

        for i, (dop_min, dop_max) in enumerate(zip(glc.dop_bins[:-1], glc.dop_bins[1:])):
            bin_interval = 'bin{:d}-{:.0f}'.format(dop_min, dop_max)
            # print(bin_interval)

            dDB_crd[crd] += '{mean:+.3f},'.format(mean=dStats['dop_bin'][bin_interval][crd]['mean'])
            dDB_crd[crd] += '{std:+.3f},'.format(std=dStats['dop_bin'][bin_interval][crd]['std'])
            dDB_crd[crd] += '{max:+.3f},'.format(max=dStats['dop_bin'][bin_interval][crd]['max'])
            dDB_crd[crd] += '{min:+.3f}'.format(min=dStats['dop_bin'][bin_interval][crd]['min'])

            if i < len(glc.dop_bins[:-1]) - 1:
                dDB_crd[crd] += ','

    # create the llh information to store in the glabng output database
    for i, crd in enumerate(glc.dgLab['OUTPUT']['llh']):
        dDB_crd[crd] = '{crd:s},'.format(crd=crd)
        if crd == 'ellh':
            dDB_crd[crd] += '{mean:+.3f},'.format(mean=dStats['crd'][crd]['mean'])
            dDB_crd[crd] += '{std:+.3f},'.format(std=dStats['crd'][crd]['std'])
            dDB_crd[crd] += '{max:+.3f},'.format(max=dStats['crd'][crd]['max'])
            dDB_crd[crd] += '{min:+.3f}'.format(min=dStats['crd'][crd]['min'])
        else:
            dDB_crd[crd] += '{mean:+.9f},'.format(mean=dStats['crd'][crd]['mean'])
            dDB_crd[crd] += '{std:+.9f},'.format(std=dStats['crd'][crd]['std'])
            dDB_crd[crd] += '{max:+.9f},'.format(max=dStats['crd'][crd]['max'])
            dDB_crd[crd] += '{min:+.9f}'.format(min=dStats['crd'][crd]['min'])

    # create the llh information to store in the glabng output database
    for i, crd in enumerate(glc.dgLab['OUTPUT']['UTM']):
        dDB_crd[crd] = '{crd:s},'.format(crd=crd)
        dDB_crd[crd] += '{mean:+.3f},'.format(mean=dStats['crd'][crd]['mean'])
        dDB_crd[crd] += '{std:+.3f},'.format(std=dStats['crd'][crd]['std'])
        dDB_crd[crd] += '{max:+.3f},'.format(max=dStats['crd'][crd]['max'])
        dDB_crd[crd] += '{min:+.3f}'.format(min=dStats['crd'][crd]['min'])

    # print('crd from glc = {crd:s} - {info:s}'.format(crd=crd, info=dDB_crd[crd]))

    return dStats, dDB_crd

def statistics_dopbin(df_dop_enu: pd.DataFrame, logger: logging.Logger) -> dict:
    """
    statistics_dopbin calculates the xDOP statistics for each xDOP bin
    """
    cFuncName = colored(os.path.basename(__file__), 'yellow') + ' - ' + colored(sys._getframe().f_code.co_name, 'green')

    logger.info('{func:s}: calculating statistics of xDOP'.format(func=cFuncName))

    dStats_dop = {}

    amutils.printHeadTailDataFrame(df=df_dop_enu, name='df_dop_enu')

    # go over all PDOP bins and plot according to the markersBin defined
    for i in range(len(glc.dop_bins) - 1):
        bin_PDOP = 'bin{:d}-{:.0f}'.format(glc.dop_bins[i], glc.dop_bins[i + 1])
        logger.debug('{func:s}: bin_PDOP = {bin!s}'.format(bin=bin_PDOP, func=cFuncName))

        # create the dict for this PDOP interval
        dStats_dop[bin_PDOP] = {}

        # find the indices within this bin
        index4Bin = (df_dop_enu['PDOP'] > glc.dop_bins[i]) & (df_dop_enu['PDOP'] <= glc.dop_bins[i + 1])

        dStats_dop[bin_PDOP]['perc'] = index4Bin.mean()
        dStats_dop[bin_PDOP]['count'] = int(index4Bin.count() * index4Bin.mean())

        for dENU, sdENU in zip(glc.dgLab['OUTPUT']['dENU'], glc.dgLab['OUTPUT']['sdENU']):
            dENU_stats = {}

            dENU_stats['wavg'] = amutils.wavg(df_dop_enu.loc[index4Bin], dENU, sdENU)
            dENU_stats['sdwavg'] = amutils.stddev(df_dop_enu.loc[index4Bin, dENU], dENU_stats['wavg'])
            dENU_stats['mean'] = df_dop_enu.loc[index4Bin, dENU].mean()
            dENU_stats['median'] = df_dop_enu.loc[index4Bin, dENU].median()
            dENU_stats['std'] = df_dop_enu.loc[index4Bin, dENU].std()
            dENU_stats['min'] = df_dop_enu.loc[index4Bin, dENU].min()
            dENU_stats['max'] = df_dop_enu.loc[index4Bin, dENU].max()

            # add for this crd dENU
            dStats_dop[bin_PDOP][dENU] = dENU_stats

            logger.debug('{func:s}: in {bin:s} statistics for {crd:s} are {stat!s}'.format(func=cFuncName, bin=bin_PDOP, crd=dENU, stat=dENU_stats))

    # report to the user
    logger.info('{func:s}: dStats_dop =\n{json!s}'.format(func=cFuncName, json=json.dumps(dStats_dop, sort_keys=False, indent=4, default=amutils.DT_convertor)))

    return dStats_dop

def statistics_coordinates(df_crd: pd.DataFrame, logger: logging.Logger) -> dict:
    """
    statistics_coordinates calculates the coordinate statistics
    """
    cFuncName = colored(os.path.basename(__file__), 'yellow') + ' - ' + colored(sys._getframe().f_code.co_name, 'green')

    logger.info('{func:s}: calculating coordinate statistics'.format(func=cFuncName))

    # init class WGS84
    wgs_84 = wgs84.WGS84()

    amutils.printHeadTailDataFrame(df=df_crd, name='df_crd', index=False)
    dStat = {}
    for crd in (glc.dgLab['OUTPUT']['llh'] + glc.dgLab['OUTPUT']['dENU'] + glc.dgLab['OUTPUT']['UTM']):
        dStat[crd] = {}

    # make sure to have the wavg for latitude since it is used for converting the stddev of geodetic coordinates into meter
    dStat['lat']['wavg'] = amutils.wavg(df_crd, 'lat', 'dN0')
    for llh, sdENU in zip(glc.dgLab['OUTPUT']['llh'], glc.dgLab['OUTPUT']['sdENU']):
        dStat[llh]['wavg'] = amutils.wavg(df_crd, llh, sdENU)
        if llh == 'lat':
            dStat[llh]['sdwavg'] = math.radians(amutils.stddev(df_crd[llh], dStat['lat']['wavg'])) * wgs_84.a
        elif llh == 'lon':
            dStat[llh]['sdwavg'] = math.radians(amutils.stddev(df_crd[llh], dStat['lat']['wavg'])) * wgs_84.a * math.cos(math.radians(dStat['lat']['wavg']))
        else:
            dStat[llh]['sdwavg'] = amutils.stddev(df_crd[llh], dStat[llh]['wavg'])

    for dENU, sdENU in zip(glc.dgLab['OUTPUT']['dENU'], glc.dgLab['OUTPUT']['sdENU']):
        dStat[dENU]['wavg'] = amutils.wavg(df_crd, dENU, sdENU)
        dStat[dENU]['sdwavg'] = amutils.stddev(df_crd[dENU], dStat[dENU]['wavg'])

    for dUTM, sdENU in zip(glc.dgLab['OUTPUT']['UTM'], glc.dgLab['OUTPUT']['sdENU'][:2]):
        dStat[dUTM]['wavg'] = amutils.wavg(df_crd, dUTM, sdENU)
        dStat[dUTM]['sdwavg'] = amutils.stddev(df_crd[dUTM], dStat[dENU]['wavg'])

    # calculate statistics for the nuùeric values
    for crd in (glc.dgLab['OUTPUT']['llh'] + glc.dgLab['OUTPUT']['dENU'] + glc.dgLab['OUTPUT']['UTM']):
        dStat[crd]['mean'] = df_crd[crd].mean()
        dStat[crd]['median'] = df_crd[crd].median()
        dStat[crd]['std'] = df_crd[crd].std()
        dStat[crd]['max'] = df_crd[crd].max()
        dStat[crd]['min'] = df_crd[crd].min()

        # results of gLAB kalman filter
        dStat[crd]['kf'] = df_crd[crd].iloc[-1]
        try:
            dStat[crd]['sdkf'] = df_crd['s{:s}'.format(crd[:2])].iloc[-1]
        except KeyError:
            dStat[crd]['sdkf'] = np.nan

    logger.info('{func:s}: OUTPUT statistics information =\n{json!s}'.format(func=cFuncName, json=json.dumps(dStat, sort_keys=False, indent=4, default=amutils.DT_convertor)))

    return dStat


"""
@author: bonniema

This code helps you get relevant information from each job card, and then iterate through all job cards on one page.
Then go to the next page
"""

from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException
from selenium import webdriver
import time
import pandas as pd

options = webdriver.ChromeOptions()

#Uncomment the line below if you'd like to scrape without a new Chrome window every time.
#options.add_argument('headless')

#Change the path to where chromedriver is in your home folder.
#driver = webdriver.Chrome(executable_path="D:\job_applications-2020\DEC_JAN_2021\SharpestMinds\ds_salary_proj/chromedriver", options=options)
driver = webdriver.Chrome(executable_path=path, options=options)
driver.set_window_size(1120, 1000)
#let the driver wait 3 seconds to locate the element before exiting out
driver.implicitly_wait(3)

titles=[]
companies=[]
locations=[]
links =[]
reviews=[]
salaries = []
descriptions=[]

for i in range(0,20):

    job_card = driver.find_elements_by_xpath('//div[contains(@class,"clickcard")]')

    for job in job_card:

    #.  not all companies have review
        try:
            review = job.find_element_by_xpath('.//span[@class="ratingsContent"]').text
        except:
            review = "None"
        reviews.append(review)
   #.   not all positions have salary
        try:
            salary = job.find_element_by_xpath('.//span[@class="salaryText"]').text
        except:
            salary = "None"
    #.  tells only to look at the element
        salaries.append(salary)

        try:
            location = job.find_element_by_xpath('.//span[contains(@class,"location")]').text
        except:
            location = "None"
    #.  tells only to look at the element
        locations.append(location)

        try:
            title  = job.find_element_by_xpath('.//h2[@class="title"]//a').text
        except:
            title = job.find_element_by_xpath('.//h2[@class="title"]//a').get_attribute(name="title")
        titles.append(title)
        links.append(job.find_element_by_xpath('.//h2[@class="title"]//a').get_attribute(name="href"))
        companies.append(job.find_element_by_xpath('.//span[@class="company"]').text)

    try:
        next_page = driver.find_element_by_xpath('//a[@aria-label={}]//span[@class="pn"]'.format(i+2))
        next_page.click()

    except:
        next_page = driver.find_element_by_xpath('//a[@aria-label="Next"]//span[@class="np"]')
        next_page.click()
    #except:
        #next_page = driver.find_element_by_xpath('//a[.//span[contains(text(),"Next")]]')
        #next_page.click()

    print("Page: {}".format(str(i+2)))


import os
import argparse
import spack.main
import spack.environment as ev
import snapshot_creator

manager = spack.main.SpackCommand('manager')
concretize = spack.main.SpackCommand('concretize')
config = spack.main.SpackCommand('config')
install = spack.main.SpackCommand('install')

def command(command, *args):
    print('spack', command.command_name, *args)
    print(command(*args, fail_on_error=False))

parser = argparse.ArgumentParser(
    'Run a test of the developer snapshot workflow')
parser.add_argument('--view_name', required=False,
                    help='view to use for the developer')
parser.add_argument('--snap_name', required=False,
                    help='directory name for the snapshot')
parser.add_argument('--dev_name', required=False,
                    help='directory name for the developer env')
parser.add_argument('--num_threads', required=False,
                    help='DAG paralleization')
parser.set_defaults(view_name='default', snap_name='test',
                    dev_name='test_external', num_threads=1)
args = parser.parse_args()

# set up the snapshot
print('Create Snapshot')
args = snapshot_creator.parse(
    ['--use_develop', '--modules', '--name', args.snap_name,
     '--num_threads', args.num_threads])
snapshot_path = snapshot_creator.create_snapshots(args)
print('Snapshot created at', snapshot_path)

# set up the user environment
ev.deactivate()
env_path = os.path.join(os.environ['SPACK_MANAGER'],
                        'environments', args.dev_name)
command(manager, 'create-env', '--directory', env_path, '--spec', 'nalu-wind')
ev.activate(ev.Environment(env_path))
command(manager, 'external', snapshot_path, '-v', args.view_name,
        '--blacklist', 'nalu-wind')
command(manager, 'develop', 'nalu-wind@master')
command(concretize)
command(install)


from collections import namedtuple
from importlib.resources import Resource

from rdflib import BNode, Graph, OWL, URIRef, Literal, RDF, RDFS

from compare.compare_code.owl_helpers.restriction_classifier import get_restricting_property, \
    get_restricting_type_and_class, \
    get_restriction_modality_and_cardinality_value

RestrictionTuple = namedtuple('OntologicalType', 'restricting_property restriction_type restricted_class restriction_modality restricted_cardinality')

def identify_restriction(restriction: BNode, ontology: Graph) -> tuple:
    restricting_property = get_restricting_property(restriction=restriction, ontology=ontology)
    restricting_type_and_class = get_restricting_type_and_class(restriction=restriction, ontology=ontology)
    restriction_modality_and_cardinality_value = get_restriction_modality_and_cardinality_value(restriction=restriction, ontology=ontology)

    restriction_tuple = \
        RestrictionTuple(
            restricting_property=restricting_property,
            restriction_type=restricting_type_and_class[0],
            restricted_class=restricting_type_and_class[1],
            restriction_modality=restriction_modality_and_cardinality_value[0],
            restricted_cardinality=restriction_modality_and_cardinality_value[1])

    return restriction_tuple

def identify_datatype_restriction(datatype_restriction: BNode, ontology: Graph) -> tuple:
    restricted_datatypes = list(ontology.objects(subject=datatype_restriction, predicate=OWL.onDatatype))
    for restricted_datatype in restricted_datatypes:
        restricting_restriction = list(ontology.objects(subject=datatype_restriction, predicate=OWL.withRestrictions))
        identified_restricting_restriction = identify_resource(resource=restricting_restriction[0], ontology=ontology)
        return tuple([restricted_datatype, identified_restricting_restriction])
    resource_unions_of = list(ontology.objects(subject=datatype_restriction, predicate=OWL.unionOf))
    if len(resource_unions_of) == 1:
        identified_resource = get_listed_resources(rdf_list_object=resource_unions_of[0], ontology=ontology, rdf_list=list())
        return tuple(identified_resource)
    else:
        identified_resource = identify_collection(resource_unions_of, ontology, OWL.unionOf)
        return identified_resource

def identify_collection(collections: list, ontology: Graph, collection_type) -> tuple:
    identified_collections = list()
    for collection in collections:
        if not isinstance(collection, list):
            items = ontology.items(collection)
        identified_items = list()
        for item in items:
            identified_item = identify_resource(item, ontology)
            identified_items.append(identified_item)
        identified_collection = tuple([collection_type] + identified_items)
        identified_collections.append(identified_collection)
    return tuple(identified_collections)

def identify_resource(resource: Resource, ontology: Graph) -> object:
    identified_resource = None

    if isinstance(resource, URIRef) or isinstance(resource, Literal):
        return resource

    resource_types = set(ontology.objects(subject=resource, predicate=RDF.type))

    if OWL.Restriction in resource_types:
        identified_resource = identify_restriction(restriction=resource, ontology=ontology)
        return identified_resource

    if RDFS.Datatype in resource_types:
        identified_resource = identify_datatype_restriction(datatype_restriction=resource, ontology=ontology)
        return identified_resource

    resource_ones_of = set(ontology.objects(subject=resource, predicate=OWL.oneOf))
    if len(resource_ones_of) > 0:
        identified_resource = identify_collection(resource_ones_of, ontology, OWL.oneOf)
        return identified_resource

    resource_unions_of = set(ontology.objects(subject=resource, predicate=OWL.unionOf))
    if len(resource_unions_of) > 0:
        identified_resource = identify_collection(resource_unions_of, ontology, OWL.unionOf)
        return identified_resource

    resource_intersections_of = set(ontology.objects(subject=resource, predicate=OWL.intersectionOf))
    if len(resource_intersections_of) > 0:
        identified_resource = identify_collection(resource_intersections_of, ontology, OWL.intersectionOf)
        return identified_resource

    resource_complements_of = set(ontology.objects(subject=resource, predicate=OWL.complementOf))
    if len(resource_complements_of) > 0:
        identified_resource = identify_collection(resource_complements_of, ontology, OWL.complementOf)
        return identified_resource

    resource_subject_predicates = list(ontology.subject_predicates(object=resource))
    if len(resource_subject_predicates) > 0:
        for resource_subject_predicate in resource_subject_predicates:
            if resource_subject_predicate[1] == OWL.propertyChainAxiom:
                chained_properties = get_listed_resources(rdf_list_object=resource, ontology=ontology, rdf_list=list())
                return chained_properties
            if resource_subject_predicate[1] == OWL.withRestrictions:
                datatype_restrictions = get_listed_resources(rdf_list_object=resource, ontology=ontology, rdf_list=list())
                identified_resource = identify_collection(datatype_restrictions, ontology, OWL.withRestrictions)
                return identified_resource

    return identified_resource

def get_listed_resources(rdf_list_object: Resource, ontology: Graph, rdf_list: list) -> list:
    first_items_in_rdf_list = list(ontology.objects(subject=rdf_list_object, predicate=RDF.first))
    if len(first_items_in_rdf_list) == 0:
        return rdf_list
    rdf_list.append(first_items_in_rdf_list[0])
    rest_items_in_rdf_list = list(ontology.objects(subject=rdf_list_object, predicate=RDF.rest))
    rdf_list = get_listed_resources(rdf_list_object=rest_items_in_rdf_list[0], ontology=ontology, rdf_list=rdf_list)
    return rdf_list


#!/usr/bin/env python

'''
Commands:
# ON
# OFF
ALL ON
ALL OFF
TEMP
T
PIC
SLEEP START #
SLEEP END #
SLEEP
SET ## (bottom temp)
RANGE # (top-bottom temp)
ON (thermostat mode on)
OFF (thermostat mode off)
STATUS
'''

import sys
import time
import os
import datetime
import matplotlib
matplotlib.use('Agg')
import matplotlib.dates
import matplotlib.pyplot
from twython import Twython
import numpy
from subprocess import Popen, PIPE
import twitter_credentials as tc
import iphone_data as iphone
import remote_commands as rc
import temperature_log_reader as tlr
import postgresql_commands as psql
import camera_commands as cc
import traceback

# your twitter consumer and access information goes here
CONSUMER_KEY = tc.CONSUMER_KEY
CONSUMER_SECRET = tc.CONSUMER_SECRET
ACCESS_TOKEN = tc.ACCESS_TOKEN
ACCESS_SECRET = tc.ACCESS_SECRET
# acceptable username
#ok_user_name = tc.ok_user_name
ok_user_name = tc.ok_user_id

# set iphone info
iPhoneIP = iphone.iPhoneIP
iPhoneMAC = iphone.iPhoneMAC
# set the variables for iPhone check
iPhoneStatusOld = 0  # default to 0 (not here)
iPhoneStatusNew = 0  # default to 0 (not here)

# sign on to twitter
twitter = Twython(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_SECRET)

# set if we are using an ac or heater
isheater = 1

# set the sleep_mode variable
sleep_status = False   # False = sleep mode waiting to start, True = sleep mode has started, waiting to end
sleep_mode = True  # we will want to start and stop sleep mode
# set the sleep end and start times
sleep_start = datetime.datetime.now()
sleep_end = datetime.datetime.now()
sleep_start_hour_default = 0
sleep_start_minute_default = 0
sleep_end_hour_default = 7
sleep_end_minute_default = 30

# set the thermostat mode variable (0 off, 1 on)
thermostat_always_off = False
thermostat_mode = 0
thermostat_temp = 71
thermostat_range = 1
# active_heaters (all, 1, 2, 3, 4)
active_heaters = [True, False, False, False, False]
# activate_heaters (-1 = off, 0 = no action, 1 = on)
activate_heaters = 0

# set the delta times for each process (in sec)
# TweetCheck: check twitter
# IPhoneCheck: check if iphone is connected
# ThermTweetCheck: send a dm tweet if thermostat sends a signal
# CameraCheck: check the camera image
deltaTweetCheck = 60
deltaIPhoneCheck = 120
deltaThermTweetCheck = 600
deltaCameraCheck = 60
deltaPsqlCheck = 5
deltaActivateHeaters = 0
tweetCheckTime = 0
iPhoneCheckTime = 0
thermTweetCheckTime = 0
cameraCheckTime = 0
psqlCheckTime = 0
activateHeatersCheckTime = 0
currentTime = time.time()

commands = {'1 ON': rc.on1,
            '1 OFF': rc.off1,
            '2 ON': rc.on2,
            '2 OFF': rc.off2,
            '3 ON': rc.on3,
            '3 OFF': rc.off3,
            '4 ON': rc.on4,
            '4 OFF': rc.off4,
            'ALL ON': rc.onAll,
            'ALL OFF': rc.offAll,
            'TEMP': tlr.temp,
            'T': tlr.tempLog,
            'PIC': tlr.take_image}

do_midnight_plot = True

while True:
    # get the current time
    currentTime = time.time()
    # get the current datetime
    currentDatetime = datetime.datetime.now()
    # see if we want to post the date history
    if do_midnight_plot:
        if currentDatetime.hour == 0:
            # put the last 24 hours on twitter
            tlr.tempPlot(twitter, 24)
            # make sure we do not repeat the mignight plot
            do_midnight_plot = False
    elif currentDatetime.hour > 0:
        # make sure we will do a midnight plot when the time is correct
        do_midnight_plot = True
    # see if we should check the postgresql database
    if psqlCheckTime < currentTime:
        # update the psql check time
        psqlCheckTime = currentTime + deltaPsqlCheck
        # try to read the psql database
        try:
            psqlResult = psql.sendReceiveSQL("SELECT min FROM therm;")
            thermostat_temp = float(psqlResult[0][0])
            psqlResult = psql.sendReceiveSQL("SELECT range FROM therm;")
            thermostat_range = float(psqlResult[0][0])
            #psqlResult = psql.sendReceiveSQL("SELECT * FROM activeHeaters;")
            #active_heaters = list(psqlResult[0])
            psqlResult = psql.sendReceiveSQL("SELECT sleep FROM status")
            sleep_mode = psqlResult[0][0]
            # see if we should change the sleep status
            if sleep_mode:
                psqlResult = psql.sendReceiveSQL("SELECT endHour FROM sleep")
                sleep_end.hour = int(psqlResult[0][0])
                sleep_end.minute = int(psqlResult[0][0]-sleep_end.hour)
                psqlResult = psql.sendReceiveSQL("SELECT startHour FROM sleep")
                sleep_start.hour = int(psqlResult[0][0])
                sleep_start.minute = int(psqlResult[0][0]-sleep_end.hour)
                # see if we should start the sleep status
                if sleep_status and currentDatetime.hour == sleep_start.hour and \
                   currentDatetime.minute == sleep_start.minute:
                    # GOING TO SLEEP!
                    sleep_status = True
                    psql.sendSQL("UPDATE sleep SET active = true;")
                # see if we should end the sleep status
                if sleep_status and currentDatetime.hour == sleep_end.hour and \
                   currentDatetime.minute == sleep_end.minute:
                    # WAKING UP!
                    sleep_status = False
                    psql.sendSQL("UPDATE sleep SET active = false;")
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            print("Postgresql error!")

        # check the temperatures
        try:
            mainDir = '/sys/bus/w1/devices'
            allDeviceDirs = os.listdir(mainDir)
            # find index that has masater directory
            deviceNames = []
            for i in range(len(allDeviceDirs)):
                if allDeviceDirs[i] == 'w1_bus_master1':
                    masterDirIndex = i
                else:
                    deviceNames.append(allDeviceDirs[i])
            numDevices = len(deviceNames)
            temps = [[] for i in range(numDevices)]
            for i in range(numDevices):
                tfile = open(mainDir + '/' + allDeviceDirs[masterDirIndex] + '/' + deviceNames[i] + '/w1_slave')
                # read the text
                text = tfile.read()
                # close the file
                tfile.close()
                # split the text with new lines and select the second to last line
                secondline = text.split("\n")[-2]
                # split the line into words, referring to the spaces and select the 10th word
                tempWord = secondline.split(" ")[9]
                # The first two characaters are t=, so get rid of them
                temp = float(tempWord[2:])
                # convert to correct temps
                tempC = temp / 1000
                temps[i] = tempC * 9 / 5 + 32
            # end of reading in all the sensor data
            # get the median
            tempF = numpy.median(temps)

            #tempF = tlr.get_temp()
            tempFstr = "%.2f" % tempF
            psql.sendSQL("DELETE FROM temp WHERE now > 0 OR now <= 0;")
            psql.sendSQL("INSERT INTO temp (now) VALUES (" + tempFstr + ");")
            #psql.sendSQL("UPDATE temp SET now = " + tempFstr + ";")
            # activate heaters if neccessary
            if tempF < thermostat_temp and not thermostat_always_off:
                if isheater > 0:
                    # turn on heat
                    rc.onAll(twitter, sendMessage=False)
                else:
                    # turn off ac
                    rc.offAll(twitter, sendMessage=False)
            if tempF > thermostat_temp + thermostat_range and not thermostat_always_off:
                if isheater > 0:
                    # turn off heat
                    rc.offAll(twitter, sendMessage=False)
                else:
                    # turn on ac
                    rc.onAll(twitter, sendMessage=False)
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            print("Error reading temperatures")

    # see if we should check twitter
    if tweetCheckTime < currentTime:
        # update the tweet check time
        tweetCheckTime = currentTime + deltaTweetCheck
        # read direct messages
        # see if we are in thermostat mode
        if thermostat_mode != 0:
            # see what the temperature is
            # read the temperature of the sensors and tweet it
            try:
                mainDir = '/sys/bus/w1/devices'
                allDeviceDirs = os.listdir(mainDir)
                #deviceNames = allDeviceDirs[0:-1]
                deviceNames = []
                masterDir = ''
                for deviceName in allDeviceDirs:
                    if 'master' not in deviceName:
                        deviceNames.append(deviceName)
                    else:
                        masterDir = deviceName

                numDevices = len(deviceNames)
                temps = [[] for i in range(numDevices)]
                for i in range(numDevices):
                    tfile = open(mainDir + '/' + masterDir + '/' + deviceNames[i] + '/w1_slave')
                    # read the text
                    text = tfile.read()
                    # close the file
                    tfile.close()
                    # split the text with new lines and select the second to last line
                    secondline = text.split("\n")[-2]
                    # split the line into words, referring to the spaces and select the 10th word
                    tempWord = secondline.split(" ")[9]
                    # The first two characaters are t=, so get rid of them
                    temp = float(tempWord[2:])
                    # convert to correct temps
                    tempC = temp / 1000
                    temps[i] = tempC * 9 / 5 + 32
                # end of reading in all the sensor data
                # get the median
                tempF = numpy.median(temps)

                tempFstr = "%.2f" % tempF
                psql.sendSQL("DELETE FROM temp WHERE now > 0 OR now <= 0;")
                psql.sendSQL("INSERT INTO temp (now) VALUES (" + tempFstr + ");")
                #psql.sendSQL("UPDATE temp SET now = " + tempFstr + ";")

                # compare to thermostat threshold and send message if needed
                if thermTweetCheckTime < currentTime:
                    sendTweetUpdate = True
                    thermTweetCheckTime = currentTime + deltaThermTweetCheck
                else:
                    sendTweetUpdate = False

                if tempF < thermostat_temp and not thermostat_always_off:
                    if isheater > 0:
                        # turn on heat
                        rc.onAll(twitter, sendMessage=sendTweetUpdate)
                    else:
                        # turn off ac
                        rc.offAll(twitter, sendMessage=sendTweetUpdate)
                if tempF > thermostat_temp + thermostat_range and not thermostat_always_off:
                    if isheater > 0:
                        # turn off heat
                        rc.offAll(twitter, sendMessage=sendTweetUpdate)
                    else:
                        # turn on ac
                        rc.onAll(twitter, sendMessage=sendTweetUpdate)
            except Exception as e:
                exc_type, exc_obj, exc_tb = sys.exc_info()
                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                print(exc_type, fname, exc_tb.tb_lineno)
                twitter.send_direct_message(text='Error reading temperatures', screen_name=tc.ok_user_id)

        # see if we are in sleep mode
        if sleep_mode != 0:
            now = datetime.datetime.now()
            # see if we are at the sleep hour and minute
            if (not sleep_status) and now.hour == sleep_start.hour and now.minute == sleep_start.minute:
                # we are at the sleep start time, so turn off the heat
                rc.offAll(twitter)
                # keep sleep mode on
                sleep_status = True
                psql.sendSQL("UPDATE sleep SET active = true;")
                print('sleep mode start activated')
            if sleep_status and now.hour == sleep_end.hour and now.minute == sleep_end.minute:
                # we are at the sleep end time, so turn on the heat
                rc.onAll(twitter)
                # turn off sleep mode
                sleep_status = 0
                psql.sendSQL("UPDATE sleep SET active = false;")
                twitter.send_direct_message(text='Sleep Mode Off', screen_name=tc.ok_user_id)
                print('sleep mode end activated')

        # read direct messages
        try:
            direct_messages = twitter.get_direct_messages()
            direct_messages = direct_messages['events']
        except:
            print('twitter GET rate limit error')
            direct_messages = []

        num_messages = len(direct_messages)
        # see if there is a new direct message
        # print('found ' + str(num_messages) + ' messages')
        for i in range(num_messages):
            #sender_name = direct_messages[num_messages - i - 1]['sender_screen_name']
            sender_name = direct_messages[num_messages - i -1]['message_create']['sender_id']
            message_id = direct_messages[num_messages - i - 1]['id']
            if sender_name == ok_user_name:
                #message = direct_messages[num_messages - i - 1]['text'].upper()
                message = direct_messages[num_messages - i - 1]['message_create']['message_data']['text'].upper()
                # see what command was sent
                print(message)
                try:
                    commands[message](twitter)
                except:
                    try:
                        message_split = message.split(' ')
                        if message[0] == 'T':
                            try:
                                hoursToPlot = int(message[1:])
                                tlr.tempPlot(twitter, hoursToPlot)
                            except Exception as err:
                                print('could not plot the temp')
                                traceback.print_tb(err.__traceback__)
                        elif message_split[0] == 'SLEEP':
                            # turn on sleep mode
                            sleep_mode = 1
                            try:
                                if message_split[1] == 'START':
                                    sleep_start = datetime.datetime.now()
                                    if sleep_start.hour > int(message_split[2]):
                                        sleep_start += datetime.timedelta(days=1)
                                    sleep_start = sleep_start.replace(hour=int(message_split[2]))
                                    sleep_start = sleep_start.replace(minute=int(message_split[3]))
                                    print('start sleep mode tweet received')
                                    twitter.send_direct_message(
                                        text='Sleep mode will start on ' + sleep_start.strftime('%H:%M'),
                                        screen_name=tc.ok_user_id)

                                elif message_split[1] == 'END':
                                    sleep_end = datetime.datetime.now()
                                    if sleep_end.hour > int(message_split[2]):
                                        sleep_end += datetime.timedelta(days=1)
                                    sleep_end = sleep_end.replace(hour=int(message_split[2]))
                                    sleep_end = sleep_end.replace(minute=int(message_split[3]))
                                    print('end sleep mode tweet received')
                                    twitter.send_direct_message(
                                        text='Sleep mode will end on ' + sleep_end.strftime('%H:%M'),
                                        screen_name=tc.ok_user_id)
                                else:
                                    # use the default sleep settings
                                    sleep_start = datetime.datetime.now()
                                    if sleep_start.hour > sleep_start_hour_default:
                                        sleep_start += datetime.timedelta(days=1)
                                    sleep_start = sleep_start.replace(hour=sleep_start_hour_default)
                                    sleep_start = sleep_start.replace(minute=sleep_start_minute_default)
                                    sleep_end = datetime.datetime.now()
                                    if sleep_end.hour > sleep_end_hour_default:
                                        sleep_end += datetime.timedelta(days=1)
                                    sleep_end = sleep_end.replace(hour=sleep_end_hour_default)
                                    sleep_end = sleep_end.replace(minute=sleep_end_minute_default)
                                    print('default sleep mode')
                                    twitter.send_direct_message(
                                        text='Default Sleep mode enabled. Sleep mode will start on ' + sleep_start.strftime(
                                            '%H:%M'), screen_name=tc.ok_user_id)
                                    twitter.send_direct_message(
                                        text='Sleep mode will end on ' + sleep_end.strftime('%H:%M'),
                                        screen_name=tc.ok_user_id)

                            except:
                                print('issues with sleep mode tweet read')
                        elif message_split[0] == 'SET':
                            # set the new thermostat temp
                            thermostat_temp = int(message_split[1])
                            # put the  the psql database
                            try:
                                psql.sendSQL("DELETE FROM therm WHERE min > 0 OR min <= 0;")
                                psql.sendSQL("INSERT INTO therm VALUES (" + str(thermostat_temp) + ", " +
                                             str(thermostat_range) + ");")
                                #psql.sendSQL("UPDATE therm SET now = " + tempFstr + ";")
                            except Exception as e:
                                exc_type, exc_obj, exc_tb = sys.exc_info()
                                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                                print(exc_type, fname, exc_tb.tb_lineno)
                                print("Postgresql error!")
                            twitter.send_direct_message(text='Thermostat temp set to ' + message_split[1],
                                                        screen_name=tc.ok_user_id)
                        elif message_split[0] == 'RANGE':
                            # set the new thermostat range
                            thermostat_range = int(message_split[1])
                            twitter.send_direct_message(
                                text='Thermostat max temp set to ' + str(int(message_split[1]) + thermostat_temp),
                                screen_name=tc.ok_user_id)
                            try:
                                psql.sendSQL("DELETE FROM therm WHERE min > 0 OR min <= 0;")
                                psql.sendSQL("INSERT INTO therm VALUES (" + str(thermostat_temp) + ", " +
                                             str(thermostat_range) + ");")
                            except Exception as e:
                                exc_type, exc_obj, exc_tb = sys.exc_info()
                                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                                print(exc_type, fname, exc_tb.tb_lineno)
                                print("Postgresql error!")

                        elif message_split[0] == 'ON':
                            # turn on thermostat mode
                            thermostat_mode = 1
                            psql.sendSQL("UPDATE status SET thermostat = true;")
                            twitter.send_direct_message(text='Thermostat mode turned on', screen_name=tc.ok_user_id)
                        elif message_split[0] == 'OFF':
                            # turn off thermostat mode
                            thermostat_mode = 0
                            psql.sendSQL("UPDATE status SET thermostat = false;")
                            twitter.send_direct_message(text='Thermostat mode turned off', screen_name=tc.ok_user_id)
                        elif message_split[0] == 'STATUS':
                            # send the status of the thermostat
                            message_to_send = 'Temp: %1.1f, Thermostat Mode: %d,  T Range: %d-%d,  Home: %d' % \
                                              (tlr.get_temp(), thermostat_mode, thermostat_temp,
                                               thermostat_temp+thermostat_range, iPhoneStatusOld)
                            twitter.send_direct_message(text=message_to_send, screen_name=tc.ok_user_id)
                        else:
                            print('unknown twitter command!')
                            twitter.send_direct_message(text='Did not understand command', screen_name=tc.ok_user_id)
                    except Exception as e:
                        exc_type, exc_obj, exc_tb = sys.exc_info()
                        fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                        print(exc_type, fname, exc_tb.tb_lineno)
                        print('twitter sending error')
            try:
                twitter.destroy_direct_message(id=message_id)
                print('direct message destroyed')
            except Exception as e:
                exc_type, exc_obj, exc_tb = sys.exc_info()
                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
                print(exc_type, fname, exc_tb.tb_lineno)
                print('error deleting direct message!')
        # end of the tweet check

    # see if we should check if the iphone is here
    if iPhoneCheckTime < currentTime and not thermostat_always_off:
        # update the iPhone check time
        iPhoneCheckTime = currentTime + deltaIPhoneCheck
        # ping the iphone
        try:
            ping_response = Popen(["ping", iPhoneIP, '-c 1'], stdout=PIPE).stdout.read()
            pid = Popen(["arp", "-a", iPhoneIP], stdout=PIPE)
            arp_output = pid.communicate()[0].split()
            # see if iphone is connected
            if iPhoneMAC in arp_output:
                iPhoneStatusNew = 1
                if iPhoneStatusNew != iPhoneStatusOld:
                    thermostat_mode = 1
                    psql.sendSQL("UPDATE status SET thermostat = true;")
                    twitter.send_direct_message(text='You came home! Thermostat mode turned on',
                                                screen_name=tc.ok_user_id)
                iPhoneStatusOld = 1
            else:
                # iphone is disconnected!
                iPhoneStatusNew = 0
                if iPhoneStatusNew != iPhoneStatusOld:
                    # we have a switch (I just left home)
                    # turn off the thermostat mode and all outlets
                    thermostat_mode = 0
                    psql.sendSQL("UPDATE status SET thermostat = false;")
                    twitter.send_direct_message(text='You left the house!  Thermostat mode and all outlets turned off.',
                                                screen_name=tc.ok_user_id)
                    rc.offAll(twitter)
                iPhoneStatusOld = 0
        except Exception as e:
            exc_type, exc_obj, exc_tb = sys.exc_info()
            fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
            print(exc_type, fname, exc_tb.tb_lineno)
            print('Error with iphone pinging')

    # see if we should check if the camera sees something
    if cameraCheckTime < currentTime:
        cameraCheckTime = currentTime+deltaCameraCheck
        # check the camera
        # camera.capture(image_filename)

    # see if we should activate heaters
    if activateHeatersCheckTime < currentTime and not thermostat_always_off:
        # update the activate heaters checktime
        activateHeatersCheckTime = currentTime + deltaActivateHeaters
        if activate_heaters > 0:
            if active_heaters[0]:
                rc.onAll(twitter, sendMessage=False)
            if active_heaters[1]:
                rc.on1(twitter, sendMessage=False)
            if active_heaters[2]:
                rc.on2(twitter, sendMessage=False)
            if active_heaters[3]:
                rc.on3(twitter, sendMessage=False)
            if active_heaters[4]:
                rc.on4(twitter, sendMessage=False)
        elif activate_heaters < 0:
            # turn off heat
            if active_heaters[0]:
                rc.offAll(twitter, sendMessage=False)
            if active_heaters[1]:
                rc.off1(twitter, sendMessage=False)
            if active_heaters[2]:
                rc.off2(twitter, sendMessage=False)
            if active_heaters[3]:
                rc.off3(twitter, sendMessage=False)
            if active_heaters[4]:
                rc.off4(twitter, sendMessage=False)


#binary tree to Doubly linkedlist
class Node:
	head = None
	prev = None
	def __init__(self,val):
		self.val=val
		self.left=None
		self.right=None

def convertTreeToDLL(root):
	if root==None:
		return
	#if Node.head:
	#	print("ads "+str(Node.head.val))
	convertTreeToDLL(root.left)
	#print(root.val)
	if not Node.head:
		Node.head = root
		#print("adsjg "+str(Node.head.val))
	else:
		#print(root.val,Node.prev.val)
		root.left = Node.prev
		Node.prev.right = root
	Node.prev = root
	convertTreeToDLL(root.right)

root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.right.right = Node(5)
convertTreeToDLL(root)
while Node.head:
	print(Node.head.val , end = " ")
	Node.head = Node.head.right


import json
import os
import re
from config_processors.silver_config_validator import SilverConfigValidator

class SilverConfig:
    def __init__(self, config_repo_path, source_system, env, territory):
        self.config_repo_path = config_repo_path
        self.source_system = source_system
        self.env = env
        self.territory = territory

    def remove_table_entries(self, table_names):
        print("Removing tables: ", table_names)

        config_dir = os.path.join(
            self.config_repo_path,
            "configs",
            self.source_system,
            self.territory,
            self.env,
        )

        app_vars_file = os.path.join(config_dir, "app_vars.json")

        if not os.path.isfile(app_vars_file):
            print(f"No such file: {app_vars_file}")
            return

        with open(app_vars_file, "r") as f:
            app_vars = json.load(f)

        table_names = set(table_names)  # convert list to set for faster operations

        # Initialize a dictionary to store removed tables
        removed_matches = {}

        for job, values in app_vars.items():
            if len(values) > 1 and isinstance(values[1], str):
                for table_name in table_names:
                    table_entry_str = (
                        f'{{ "database_name": "[^"]*", "table_name":"{table_name}" }}'
                        + ","
                    )
                    matches = re.findall(table_entry_str, values[1])
                    if matches:
                        print("\nEnrichment job::", job)
                        if job not in removed_matches:
                            removed_matches[job] = []
                        for match in matches:
                            print(f"Removing Matching record: {match}")
                        values[1] = re.sub(table_entry_str, "", values[1])

        with open(app_vars_file, "w") as f:
            json.dump(app_vars, f, indent=4)
            f.write("\n")

        self.validate_config_file(app_vars_file)

    def unpause_tables(self, table_names):
        print("Unpausing tables: ", table_names)

        metadata_file_path = os.path.join(
            "metadata", f"{self.territory}-{self.env}-app-vars.json"
        )
        app_vars_file_path = os.path.join(
            self.config_repo_path,
            "configs",
            "excite",
            self.territory,
            self.env,
            "app_vars.json",
        )
        with open(metadata_file_path, "r") as f:
            metadata = json.load(f)

        with open(app_vars_file_path, "r") as f:
            app_vars = json.load(f)

        for table_name in table_names:
            if table_name in metadata:
                job_name = metadata[table_name]["job_name"]
                value_string = metadata[table_name]["value"]

                if job_name in app_vars:
                    if table_name in app_vars[job_name][1]:
                        raise ValueError(
                            f"Error Processing::Table {table_name} already exists in {job_name}"
                        )
                    app_vars[job_name][1] = re.sub(
                        r"\]$", f", {value_string}]", app_vars[job_name][1]
                    )
                    print("\nEnrichment job::", job_name)
                    print(f"Adding table {value_string}")
                else:
                    raise ValueError(f"Job {job_name} not found in app_vars.json")
            else:
                raise ValueError(f"Table {table_name} not found in metadata")

        with open(app_vars_file_path, "w") as f:
            json.dump(app_vars, f, indent=4)
            f.write("\n")

        self.validate_config_file(app_vars_file_path)

    def validate_config_file(self, app_vars_file_path):
        validator = SilverConfigValidator()
        if not validator.validate_config_file(app_vars_file_path):
            raise ValueError(f"Invalid Config file: {app_vars_file_path}")

    def build_metadata(self):
        config_dir = os.path.join(
            self.config_repo_path,
            "configs",
            self.source_system,
            self.territory,
            self.env,
        )
        source_file_path = os.path.join(config_dir, "app_vars.json")

        if not os.path.isfile(source_file_path):
            raise FileNotFoundError(f"No such file: {source_file_path}")

        with open(source_file_path, "r") as source_file:
            data = json.load(source_file)

        metadata = {}

        # Traverse the jobs in the data
        for job_name, values in data.items():
            if len(values) > 1 and isinstance(values[1], str):
                try:
                    tables = json.loads(values[1])  # parse json string that has tables
                except json.JSONDecodeError:
                    raise ValueError(
                        f"Invalid JSON string in the value array for {job_name}"
                    )

                for table in tables:
                    table_name = table["table_name"]
                    if table_name in metadata:  # Check if table_name already exists
                        raise ValueError(f"Duplicate table name detected: {table_name}")

                    metadata[table_name] = {
                        "value": json.dumps(table, separators=(",", ":")),
                        "job_name": job_name,
                    }

        # save the metadata file
        output_file_path = os.path.join(
            "metadata", f"{self.territory}-{self.env}-app-vars.json"
        )
        with open(output_file_path, "w") as output_file:
            json.dump(metadata, output_file, indent=4)

        print(f"Metadata saved to: {output_file_path}")


# coding=utf-8
"""Shared utility functions"""

import collections
import os
import re
import subprocess
import sys
import threading
import unicodedata
from typing import Any, BinaryIO, Iterable, List, Optional, TextIO, Union

from wcwidth import wcswidth

from . import constants

def strip_ansi(text: str) -> str:
    """Strip ANSI escape codes from a string.

    :param text: string which may contain ANSI escape codes
    :return: the same string with any ANSI escape codes removed
    """
    return constants.ANSI_ESCAPE_RE.sub('', text)

def ansi_safe_wcswidth(text: str) -> int:
    """
    Wraps wcswidth to make it compatible with colored strings

    :param text: the string being measured
    """
    # Strip ANSI escape codes since they cause wcswidth to return -1
    return wcswidth(strip_ansi(text))

def is_quoted(arg: str) -> bool:
    """
    Checks if a string is quoted
    :param arg: the string being checked for quotes
    :return: True if a string is quoted
    """
    return len(arg) > 1 and arg[0] == arg[-1] and arg[0] in constants.QUOTES

def quote_string_if_needed(arg: str) -> str:
    """ Quotes a string if it contains spaces and isn't already quoted """
    if is_quoted(arg) or ' ' not in arg:
        return arg

    if '"' in arg:
        quote = "'"
    else:
        quote = '"'

    return quote + arg + quote

def strip_quotes(arg: str) -> str:
    """ Strip outer quotes from a string.

     Applies to both single and double quotes.

    :param arg:  string to strip outer quotes from
    :return: same string with potentially outer quotes stripped
    """
    if is_quoted(arg):
        arg = arg[1:-1]
    return arg

def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]],
                             default_values: collections.Iterable = ()):
    """
    Convenience function for defining a namedtuple with default values

    From: https://stackoverflow.com/questions/11351032/namedtuple-and-default-values-for-optional-keyword-arguments

    Examples:
        >>> Node = namedtuple_with_defaults('Node', 'val left right')
        >>> Node()
        Node(val=None, left=None, right=None)
        >>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
        >>> Node()
        Node(val=1, left=2, right=3)
        >>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
        >>> Node()
        Node(val=None, left=None, right=7)
        >>> Node(4)
        Node(val=4, left=None, right=7)
    """
    T = collections.namedtuple(typename, field_names)
    # noinspection PyProtectedMember,PyUnresolvedReferences
    T.__new__.__defaults__ = (None,) * len(T._fields)
    if isinstance(default_values, collections.Mapping):
        prototype = T(**default_values)
    else:
        prototype = T(*default_values)
    T.__new__.__defaults__ = tuple(prototype)
    return T

def cast(current: Any, new: str) -> Any:
    """Tries to force a new value into the same type as the current when trying to set the value for a parameter.

    :param current: current value for the parameter, type varies
    :param new: new value
    :return: new value with same type as current, or the current value if there was an error casting
    """
    typ = type(current)
    orig_new = new

    if typ == bool:
        try:
            return bool(int(new))
        except (ValueError, TypeError):
            pass
        try:
            new = new.lower()
            if (new == 'on') or (new[0] in ('y', 't')):
                return True
            if (new == 'off') or (new[0] in ('n', 'f')):
                return False
        except AttributeError:
            pass
    else:
        try:
            return typ(new)
        except (ValueError, TypeError):
            pass
    print("Problem setting parameter (now {}) to {}; incorrect type?".format(current, orig_new))
    return current

def which(editor: str) -> Optional[str]:
    """Find the full path of a given editor.

    Return the full path of the given editor, or None if the editor can
    not be found.

    :param editor: filename of the editor to check, ie 'notepad.exe' or 'vi'
    :return: a full path or None
    """
    try:
        editor_path = subprocess.check_output(['which', editor], stderr=subprocess.STDOUT).strip()
        editor_path = editor_path.decode()
    except subprocess.CalledProcessError:
        editor_path = None
    return editor_path

def is_text_file(file_path: str) -> bool:
    """Returns if a file contains only ASCII or UTF-8 encoded text.

    :param file_path: path to the file being checked
    :return: True if the file is a text file, False if it is binary.
    """
    import codecs

    expanded_path = os.path.abspath(os.path.expanduser(file_path.strip()))
    valid_text_file = False

    # Check if the file is ASCII
    try:
        with codecs.open(expanded_path, encoding='ascii', errors='strict') as f:
            # Make sure the file has at least one line of text
            # noinspection PyUnusedLocal
            if sum(1 for line in f) > 0:
                valid_text_file = True
    except OSError:  # pragma: no cover
        pass
    except UnicodeDecodeError:
        # The file is not ASCII. Check if it is UTF-8.
        try:
            with codecs.open(expanded_path, encoding='utf-8', errors='strict') as f:
                # Make sure the file has at least one line of text
                # noinspection PyUnusedLocal
                if sum(1 for line in f) > 0:
                    valid_text_file = True
        except OSError:  # pragma: no cover
            pass
        except UnicodeDecodeError:
            # Not UTF-8
            pass

    return valid_text_file

def remove_duplicates(list_to_prune: List) -> List:
    """Removes duplicates from a list while preserving order of the items.

    :param list_to_prune: the list being pruned of duplicates
    :return: The pruned list
    """
    temp_dict = collections.OrderedDict()
    for item in list_to_prune:
        temp_dict[item] = None

    return list(temp_dict.keys())

def norm_fold(astr: str) -> str:
    """Normalize and casefold Unicode strings for saner comparisons.

    :param astr: input unicode string
    :return: a normalized and case-folded version of the input string
    """
    return unicodedata.normalize('NFC', astr).casefold()

def alphabetical_sort(list_to_sort: Iterable[str]) -> List[str]:
    """Sorts a list of strings alphabetically.

    For example: ['a1', 'A11', 'A2', 'a22', 'a3']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=norm_fold)

    :param list_to_sort: the list being sorted
    :return: the sorted list
    """
    return sorted(list_to_sort, key=norm_fold)

def try_int_or_force_to_lower_case(input_str: str) -> Union[int, str]:
    """
    Tries to convert the passed-in string to an integer. If that fails, it converts it to lower case using norm_fold.
    :param input_str: string to convert
    :return: the string as an integer or a lower case version of the string
    """
    try:
        return int(input_str)
    except ValueError:
        return norm_fold(input_str)

def natural_keys(input_str: str) -> List[Union[int, str]]:
    """
    Converts a string into a list of integers and strings to support natural sorting (see natural_sort).

    For example: natural_keys('abc123def') -> ['abc', '123', 'def']
    :param input_str: string to convert
    :return: list of strings and integers
    """
    return [try_int_or_force_to_lower_case(substr) for substr in re.split(r'(\d+)', input_str)]

def natural_sort(list_to_sort: Iterable[str]) -> List[str]:
    """
    Sorts a list of strings case insensitively as well as numerically.

    For example: ['a1', 'A2', 'a3', 'A11', 'a22']

    To sort a list in place, don't call this method, which makes a copy. Instead, do this:

    my_list.sort(key=natural_keys)

    :param list_to_sort: the list being sorted
    :return: the list sorted naturally
    """
    return sorted(list_to_sort, key=natural_keys)

def unquote_redirection_tokens(args: List[str]) -> None:
    """
    Unquote redirection tokens in a list of command-line arguments
    This is used when redirection tokens have to be passed to another command
    :param args: the command line args
    """
    for i, arg in enumerate(args):
        unquoted_arg = strip_quotes(arg)
        if unquoted_arg in constants.REDIRECTION_TOKENS:
            args[i] = unquoted_arg

def find_editor() -> str:
    """Find a reasonable editor to use by default for the system that the cmd2 application is running on."""
    editor = os.environ.get('EDITOR')
    if not editor:
        if sys.platform[:3] == 'win':
            editor = 'notepad'
        else:
            # Favor command-line editors first so we don't leave the terminal to edit
            for editor in ['vim', 'vi', 'emacs', 'nano', 'pico', 'gedit', 'kate', 'subl', 'geany', 'atom']:
                if which(editor):
                    break
    return editor

class StdSim(object):
    """
    Class to simulate behavior of sys.stdout or sys.stderr.
    Stores contents in internal buffer and optionally echos to the inner stream it is simulating.
    """
    def __init__(self, inner_stream, echo: bool = False,
                 encoding: str = 'utf-8', errors: str = 'replace') -> None:
        """
        Initializer
        :param inner_stream: the emulated stream
        :param echo: if True, then all input will be echoed to inner_stream
        :param encoding: codec for encoding/decoding strings (defaults to utf-8)
        :param errors: how to handle encoding/decoding errors (defaults to replace)
        """
        self.inner_stream = inner_stream
        self.echo = echo
        self.encoding = encoding
        self.errors = errors
        self.pause_storage = False
        self.buffer = ByteBuf(self)

    def write(self, s: str) -> None:
        """Add str to internal bytes buffer and if echo is True, echo contents to inner stream"""
        if not isinstance(s, str):
            raise TypeError('write() argument must be str, not {}'.format(type(s)))

        if not self.pause_storage:
            self.buffer.byte_buf += s.encode(encoding=self.encoding, errors=self.errors)
        if self.echo:
            self.inner_stream.write(s)

    def getvalue(self) -> str:
        """Get the internal contents as a str"""
        return self.buffer.byte_buf.decode(encoding=self.encoding, errors=self.errors)

    def getbytes(self) -> bytes:
        """Get the internal contents as bytes"""
        return bytes(self.buffer.byte_buf)

    def read(self) -> str:
        """Read from the internal contents as a str and then clear them out"""
        result = self.getvalue()
        self.clear()
        return result

    def readbytes(self) -> bytes:
        """Read from the internal contents as bytes and then clear them out"""
        result = self.getbytes()
        self.clear()
        return result

    def clear(self) -> None:
        """Clear the internal contents"""
        self.buffer.byte_buf.clear()

    def isatty(self) -> bool:
        """StdSim only considered an interactive stream if `echo` is True and `inner_stream` is a tty."""
        if self.echo:
            return self.inner_stream.isatty()
        else:
            return False

    def __getattr__(self, item: str):
        if item in self.__dict__:
            return self.__dict__[item]
        else:
            return getattr(self.inner_stream, item)

class ByteBuf(object):
    """
    Used by StdSim to write binary data and stores the actual bytes written
    """
    def __init__(self, std_sim_instance: StdSim) -> None:
        self.byte_buf = bytearray()
        self.std_sim_instance = std_sim_instance

    def write(self, b: bytes) -> None:
        """Add bytes to internal bytes buffer and if echo is True, echo contents to inner stream."""
        if not isinstance(b, bytes):
            raise TypeError('a bytes-like object is required, not {}'.format(type(b)))
        if not self.std_sim_instance.pause_storage:
            self.byte_buf += b
        if self.std_sim_instance.echo:
            self.std_sim_instance.inner_stream.buffer.write(b)

class ProcReader(object):
    """
    Used to captured stdout and stderr from a Popen process if any of those were set to subprocess.PIPE.
    If neither are pipes, then the process will run normally and no output will be captured.
    """
    def __init__(self, proc: subprocess.Popen, stdout: Union[StdSim, BinaryIO, TextIO],
                 stderr: Union[StdSim, BinaryIO, TextIO]) -> None:
        """
        ProcReader initializer
        :param proc: the Popen process being read from
        :param stdout: the stream to write captured stdout
        :param stderr: the stream to write captured stderr
        """
        self._proc = proc
        self._stdout = stdout
        self._stderr = stderr

        self._out_thread = threading.Thread(name='out_thread', target=self._reader_thread_func,
                                            kwargs={'read_stdout': True})

        self._err_thread = threading.Thread(name='out_thread', target=self._reader_thread_func,
                                            kwargs={'read_stdout': False})

        # Start the reader threads for pipes only
        if self._proc.stdout is not None:
            self._out_thread.start()
        if self._proc.stderr is not None:
            self._err_thread.start()

    def send_sigint(self) -> None:
        """Send a SIGINT to the process similar to if <Ctrl>+C were pressed."""
        import signal
        if sys.platform.startswith('win'):
            signal_to_send = signal.CTRL_C_EVENT
        else:
            signal_to_send = signal.SIGINT
        self._proc.send_signal(signal_to_send)

    def terminate(self) -> None:
        """Terminate the process"""
        self._proc.terminate()

    def wait(self) -> None:
        """Wait for the process to finish"""
        if self._out_thread.is_alive():
            self._out_thread.join()
        if self._err_thread.is_alive():
            self._err_thread.join()

        # Handle case where the process ended before the last read could be done.
        # This will return None for the streams that weren't pipes.
        out, err = self._proc.communicate()

        if out:
            self._write_bytes(self._stdout, out)
        if err:
            self._write_bytes(self._stderr, err)

    def _reader_thread_func(self, read_stdout: bool) -> None:
        """
        Thread function that reads a stream from the process
        :param read_stdout: if True, then this thread deals with stdout. Otherwise it deals with stderr.
        """
        if read_stdout:
            read_stream = self._proc.stdout
            write_stream = self._stdout
        else:
            read_stream = self._proc.stderr
            write_stream = self._stderr

        # The thread should have been started only if this stream was a pipe
        assert read_stream is not None

        # Run until process completes
        while self._proc.poll() is None:
            # noinspection PyUnresolvedReferences
            available = read_stream.peek()
            if available:
                read_stream.read(len(available))
                self._write_bytes(write_stream, available)

    @staticmethod
    def _write_bytes(stream: Union[StdSim, BinaryIO, TextIO], to_write: bytes) -> None:
        """
        Write bytes to a stream
        :param stream: the stream being written to
        :param to_write: the bytes being written
        """
        try:
            if hasattr(stream, 'buffer'):
                stream.buffer.write(to_write)
            else:
                stream.write(to_write)
        except BrokenPipeError:
            # This occurs if output is being piped to a process that closed
            pass

class ContextFlag(object):
    """A context manager which is also used as a boolean flag value within the default sigint handler.

    Its main use is as a flag to prevent the SIGINT handler in cmd2 from raising a KeyboardInterrupt
    while a critical code section has set the flag to True. Because signal handling is always done on the
    main thread, this class is not thread-safe since there is no need.
    """
    def __init__(self) -> None:
        # When this flag has a positive value, it is considered set.
        # When it is 0, it is not set. It should never go below 0.
        self.__count = 0

    def __bool__(self) -> bool:
        return self.__count > 0

    def __enter__(self) -> None:
        self.__count += 1

    def __exit__(self, *args) -> None:
        self.__count -= 1
        if self.__count < 0:
            raise ValueError("count has gone below 0")

class RedirectionSavedState(object):
    """Created by each command to store information about their redirection."""

    def __init__(self, self_stdout: Union[StdSim, BinaryIO, TextIO], sys_stdout: Union[StdSim, BinaryIO, TextIO],
                 pipe_proc_reader: Optional[ProcReader]) -> None:
        # Used to restore values after the command ends
        self.saved_self_stdout = self_stdout
        self.saved_sys_stdout = sys_stdout
        self.saved_pipe_proc_reader = pipe_proc_reader

        # Tells if the command is redirecting
        self.redirecting = False

        # If the command created a process to pipe to, then then is its reader
        self.pipe_proc_reader = None


from flask import Flask, request
import ssl

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello, world!'

if __name__ == '__main__':
    # Load server certificate and private key
    server_cert = r'C:\Users\Lenovo\Desktop\MTLS Connection\MTLS\certificate.pem'
    server_key = r'C:\Users\Lenovo\Desktop\MTLS Connection\MTLS\key.pem'

    # Create SSL context with self-signed certificate and private key
    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_cert_chain(certfile=server_cert, keyfile=server_key)

    # Run the app with SSL/TLS configured
    app.run(ssl_context=context, host='0.0.0.0', port=443)


import logging
import numpy as np

import torch

from pathlib import Path
from typing import Dict, Union, List, Callable

from dc_framework.data_preparation import Dataset

logger = logging.getLogger("__name__")

def init(model: torch.nn.Module, criterion: torch.nn.Module, metrics: Dict[str, Callable] = None):
    return DCFramework(model, criterion, metrics)

class DCFramework:
    def __init__(self, model: torch.nn.Module, criterion: torch.nn.Module, metrics: Dict[str, Callable] = None, lr=1e-3):
        self.model = model
        self.optimizer = torch.optim.SGD(model.parameters(), lr=lr)
        self.criterion = criterion
        if metrics:
            self.metrics = torch.nn.ModuleDict({name:func for name, func in metrics})
        else:
            self.metrics = torch.nn.ModuleDict({"mae":torch.nn.L1Loss()})

        #check if model is already on cuda
        self.on_cuda = False
        if next(model.parameters()).is_cuda:
            self.on_cuda = True

    def forward(self, feature, target):
        try:
            if self.on_cuda:
                feature = feature.cuda()
            output = self.model(feature)
        except:
            logger.warning(f"feature: {feature}")
            raise
        try:
            if self.on_cuda:
                output = output.cuda()
                target = target.cuda()

            loss = self.criterion(output, target)
            results = {}
            for name, metric in self.metrics.items():
                results[name] = metric(output, target)
            results['output'] = output
            results['loss']   = loss
        except:
            logger.warning(f"output: {output}")
            logger.warning(f"target: {target}")
            raise
        return results

    def train(self, train_data: Dict[str, np.array], val_data: Union[Dict[str, np.array], None] = None, batch_size: int = 1, epochs: int = 2):

        train_dataloader = self._prepare_loader(train_data, batch_size=batch_size)
        if val_data:
            val_dataloader = self._prepare_loader(val_data, batch_size=batch_size)

        for i in range(epochs):

            self._train_loop(train_dataloader)

            if val_data:
                self._val_loop(val_dataloader) #torch.no_grad is incorporated into the function

    @torch.no_grad()
    def val(self, val_data: Dict[str, np.array], batch_size: int = 1):
        val_dataloader = self._prepare_loader(val_data, batch_size=batch_size)
        self._val_loop(val_dataloader)

    def _train_loop(self, loader):
        for batch in loader:
            output = self.forward(*batch)
            loss = output["loss"]
            loss.backward()
            self.optimizer.step()

    @torch.no_grad()
    def _val_loop(self, loader):
        for batch in loader:
            output = self.forward(*batch)
            loss = output["loss"]

    def _prepare_loader(self, data: Dict[str, np.array], batch_size: int = 1):
        data = Dataset(data)
        loader = data.get_data_loader(batch_size=batch_size)
        return loader

    def to(self, device: Union[torch.device, str]):
        device = torch.device(device) #cpu or gpu
        self.model.to(device)
        self.metrics.to(device)
        self.criterion.to(device)

    def cpu(self):
        self.to("cpu")
        self.on_cuda = False

    def cuda(self):
        self.to("cuda")
        self.on_cuda = True

    def save(self, path: Path):
        state = {
            "model": self.model.state_dict(),
            "optimizer": self.optimizer.state_dict(),
        }
        torch.save(state, path)

    def load(self, path):
        checkpoint = torch.load(path)
        self.model.load_state_dict(checkpoint["model"])
        self.optimizer.load_state_dict(checkpoint["optimizer"])
        if self.on_cuda:
            self.to("cuda")


'''
-Hard-
*Priority Queue*

You are given two integers, m and k, and a stream of integers. You are tasked to implement a data structure that calculates the MKAverage for the stream.

The MKAverage can be calculated using these steps:

If the number of the elements in the stream is less than m you should consider the MKAverage to be -1. Otherwise, copy the last m elements of the stream to a separate container.
Remove the smallest k elements and the largest k elements from the container.
Calculate the average value for the rest of the elements rounded down to the nearest integer.
Implement the MKAverage class:

MKAverage(int m, int k) Initializes the MKAverage object with an empty stream and the two integers m and k.
void addElement(int num) Inserts a new element num into the stream.
int calculateMKAverage() Calculates and returns the MKAverage for the current stream rounded down to the nearest integer.

Example 1:

Input
["MKAverage", "addElement", "addElement", "calculateMKAverage", "addElement", "calculateMKAverage", "addElement", "addElement", "addElement", "calculateMKAverage"]
[[3, 1], [3], [1], [], [10], [], [5], [5], [5], []]
Output
[null, null, null, -1, null, 3, null, null, null, 5]

Explanation
MKAverage obj = new MKAverage(3, 1);
obj.addElement(3);        // current elements are [3]
obj.addElement(1);        // current elements are [3,1]
obj.calculateMKAverage(); // return -1, because m = 3 and only 2 elements exist.
obj.addElement(10);       // current elements are [3,1,10]
obj.calculateMKAverage(); // The last 3 elements are [3,1,10].
                          // After removing smallest and largest 1 element the container will be [3].
                          // The average of [3] equals 3/1 = 3, return 3
obj.addElement(5);        // current elements are [3,1,10,5]
obj.addElement(5);        // current elements are [3,1,10,5,5]
obj.addElement(5);        // current elements are [3,1,10,5,5,5]
obj.calculateMKAverage(); // The last 3 elements are [5,5,5].
                          // After removing smallest and largest 1 element the container will be [5].
                          // The average of [5] equals 5/1 = 5, return 5

Constraints:

3 <= m <= 105
1 <= k*2 < m
1 <= num <= 105
At most 105 calls will be made to addElement and calculateMKAverage.

'''

import heapq
from heapq import heappop, heappush
import math

from sortedcontainers import SortedList
import collections

class MKAverage:

    def __init__(self, m: int, k: int):
        self.m = m
        self.k = k
        self.minh = []
        self.maxh = []
        self.minhd = []
        self.maxhd = []
        self.nums = []
        self.cnt = 0
        self.t = m - 2*k
        self.sums = 0
        self.sminh = 0
        self.smaxh = 0

    def addElement(self, num: int) -> None:
        # print('xxx', num, self.sums, self.sminh, self.smaxh)
        self.sums += num
        self.nums.append(num)
        i = -1
        if self.cnt >= self.m:
            i = self.cnt - self.m
            while self.minhd and self.minhd[0][1] < i:
                heapq.heappop(self.minhd)
            while self.minhd and self.maxhd[0][1] < i:
                heapq.heappop(self.maxhd)
            mark = 0
            if self.nums[i] > self.minhd[0][0]:
                self.sminh -= self.nums[i]
                self.sums -= self.nums[i]
                mark = 1
            elif self.nums[i] < -self.maxh[0][0]:
                self.smaxh -= self.nums[i]
                self.sums -= self.nums[i]
                mark = -1
            else:
                self.sums -= self.nums[i]
            if mark > 0:
                val, pos = heapq.heappop(self.maxhd)
                heapq.heappush(self.minh, (-val, pos))
            elif mark < 0:
                val, pos = heapq.heappop(self.minhd)
                heapq.heappush(self.maxh, (-val, pos))

        # while self.minh and self.minh[0][1] <= i:
        #     heapq.heappop(self.minh)
        # while self.maxh and self.maxh[0][1] <= i:
        #     heapq.heappop(self.maxh)
        # print('before add', num, self.sums, self.sminh, self.smaxh, self.minh, self.maxh)

        # if not self.minhd:
        #     heapq.heappush(self.minhd, (num, self.cnt))

        # if not self.maxhd:
        #     heapq.heappush(self.maxhd, (-num, self.cnt))

        self.sminh += num
        if len(self.minh) > self.k:
            while self.minh and self.minh[0][1] < i:
                heapq.heappop(self.minh)
            val, pos = heapq.heappop(self.minh)
            # heapq.heappush(self.minhd, (val, pos))
            heapq.heappush(self.maxhd, (-val, pos))
            self.sminh -= val
        self.smaxh += num
        heapq.heappush(self.maxh, (-num, self.cnt))
        if len(self.maxh) > self.k:
            while self.maxh and self.maxh[0][1] < i:
                heapq.heappop(self.maxh)
            val, pos = heapq.heappop(self.maxh)
            heapq.heappush(self.minhd, (-val, pos))
            # heapq.heappush(self.maxhd, (val, pos))
            self.smaxh -= -val
        self.cnt += 1
        print('afetr add', num, self.sums, self.sminh, self.smaxh, self.minh, self.maxh, self.minhd, self.maxhd)

    def calculateMKAverage(self) -> int:
        if self.cnt < self.m: return -1
        # print(self.minh, self.maxh)
        # print(self.sums,  self.sminh,  self.smaxh, self.t)
        return math.floor((self.sums - self.sminh - self.smaxh) / self.t)

class MKAverage2:

    def __init__(self, m: int, k: int):
        self.m, self.k = m, k
        self.deque = collections.deque()
        self.sl = SortedList()
        self.total = self.first_k = self.last_k = 0

    def addElement(self, num: int) -> None:
        self.total += num
        self.deque.append(num)
        index = self.sl.bisect_left(num)
        if index < self.k:
            self.first_k += num
            if len(self.sl) >= self.k:
                self.first_k -= self.sl[self.k - 1]
        if index >= len(self.sl) + 1 - self.k:
            self.last_k += num
            if len(self.sl) >= self.k:
                self.last_k -= self.sl[-self.k]
        self.sl.add(num)
        if len(self.deque) > self.m:
            num = self.deque.popleft()
            self.total -= num
            index = self.sl.index(num)
            if index < self.k:
                self.first_k -= num
                self.first_k += self.sl[self.k]
            elif index >= len(self.sl) - self.k:
                self.last_k -= num
                self.last_k += self.sl[-self.k - 1]
            self.sl.remove(num)

    def calculateMKAverage(self) -> int:
        if len(self.sl) < self.m:
            return -1
        return (self.total - self.first_k - self.last_k) // (self.m - 2 * self.k)

class MKAverage3:
    def __init__(self, m: int, k: int):
        self.m, self.k = m, k
        self.arr = [0]*m
        self.lh1, self.rh1 = self.heap_init(m, k)
        self.lh2, self.rh2 = self.heap_init(m, m - k)
        self.score = 0

    def heap_init(self, p1, p2):
        h1 = [(0, i) for i in range(p1-p2, p1)]
        h2 = [(0, i) for i in range(p1-p2)]
        heapq.heapify(h1)
        heapq.heapify(h2)
        return (h1, h2)

    def update(self, lh, rh, m, k, num):
        score, T = 0, len(self.arr)
        if num > rh[0][0]:
            heappush(rh, (num, T))
            if self.arr[T - m] <= rh[0][0]:
                if rh[0][1] >= T - m: score += rh[0][0]
                score -= self.arr[T - m]
                heappush(lh, (-rh[0][0], rh[0][1]))
                heappop(rh)
        else:
            heappush(lh, (-num, T))
            score += num
            if self.arr[T - m] >= rh[0][0]:
                heappush(rh, (-lh[0][0], lh[0][1]))
                q = heappop(lh)
                score += q[0]
            else:
                score -= self.arr[T - m]

        while lh and lh[0][1] <= T - m: heappop(lh)  # lazy-deletion
        while rh and rh[0][1] <= T - m: heappop(rh)  # lazy-deletion
        return score

    def addElement(self, num):
        t1 = self.update(self.lh1, self.rh1, self.m, self.k, num)
        t2 = self.update(self.lh2, self.rh2, self.m, self.m - self.k, num)
        self.arr.append(num)
        self.score += (t2 - t1)

    def calculateMKAverage(self):
        if len(self.arr) < 2*self.m: return -1
        return self.score//(self.m - 2*self.k)

def negateX(z):
    return ( -z[0],-z[1] )

class MKAverage4:

    def __init__(self, m: int, k: int):
        # use 4 heaps (2 min, 2 max)
        # lowQ and lowA consist of a pair, complementing each other
        # together they hold all the numbers in the window
        # the key step is to maintain lowQ having k smallest numbers
        # if not, exchange lowQ[0] with lowA[0] until satisfied
        # similarly for highA and highQ

        self.lowQ = []  # max-heap, smallest k numbers in the window
        self.highQ = [] # min-heap, largest k numbers in the window
        self.lowA = []  # min-heap, largest m-k numbers in the window
        self.highA = [] # max-heap, smallest m-k numbers in the window
        self.total = 0
        self.counter = 0
        self.m = m
        self.k = k
        self.divisor = m - k - k
        self.allNums = []

    def addElement(self, num: int) -> None:
        # print("addElement",num)
        self.total += num
        heappush(self.lowA, (num,self.counter) )
        heappush(self.highA, (-num,-self.counter) )

        if self.counter < self.m:
            if self.counter == self.m-1:
                # print(self.lowA)
                # print(self.highA)
                for _ in range(self.k):
                    heappush(self.lowQ, negateX(heappop(self.lowA)))
                    heappush(self.highQ, negateX(heappop(self.highA)))
                self.total -= -sum( n for n,_ in self.lowQ )
                self.total -= sum( n for n,_ in self.highQ )
        else:
            removedIndex = self.counter-self.m
            removed = (self.allNums[self.counter-self.m], removedIndex)
            # print("removedNum",removed)
            if removed <= negateX(self.lowQ[0]):
                # print("removedNum from lowQ")
                low = heappop(self.lowA)
                self.total -= low[0]
                heappush(self.lowQ, negateX(low))
            elif removed >= self.highQ[0]:
                # print("removedNum from highQ")
                high = heappop(self.highA)
                self.total -= -high[0]
                heappush(self.highQ, negateX(high))
            else:
                self.total -= removed[0]

            while -self.lowQ[0][1] <= removedIndex:
                heappop(self.lowQ)
            while self.lowA[0][1] <= removedIndex:
                heappop(self.lowA)
            while self.highQ[0][1] <= removedIndex:
                heappop(self.highQ)
            while -self.highA[0][1] <= removedIndex:
                heappop(self.highA)

            while self.highA[0] < negateX(self.highQ[0]):
                a,q = heappop(self.highA),heappop(self.highQ)
                self.total += a[0]
                self.total += q[0]
                heappush(self.highA, negateX(q))
                heappush(self.highQ, negateX(a))
                # print("swizzle high")
            while self.lowA[0] < negateX(self.lowQ[0]):
                a,q = heappop(self.lowA),heappop(self.lowQ)
                self.total -= a[0]
                self.total -= q[0]
                heappush(self.lowA, negateX(q))
                heappush(self.lowQ, negateX(a))
                # print("swizzle low")

        self.allNums.append(num)
        self.counter += 1
        # print(self.total, self.allNums, "|||", self.lowQ, self.highQ, "|||", self.lowA, self.highA)

    def calculateMKAverage(self) -> int:
        if self.counter < self.m:
            return -1
        return self.total // self.divisor

class BinaryIndexTree:
    def __init__(self, n: int):
        self.leng = n + 1
        """
        i is the number, nodes[i] = sum(nums[i - i & (-i), i - 1]),
        nums is the original array
        e.g.: https://pic.leetcode-cn.com/755dd21358e8cd6ac39c85bdbaa67188dcf67dad7bd8c32d4ad777c1f376ff08-binaryindexedtreeexpandedtreewithinfo.gif
        """
        self.nodes = [0] * self.leng

    def query(self, i: int) -> int:
        """
        i is a number
        return the sum of numbers that is less than or equal to,
        which is equivalent to the sum of logN ranges (等于i代表的logi个区间的总和。)
        """
        ans = 0
        while i:
            ans += self.nodes[i]
            i -= i & -i  # remove the lowbit of i
        return ans

    def update(self, i: int, delta: int) -> None:
        while i < self.leng:
            self.nodes[i] += delta
            # parent(i) = i + i&(-i), i&(-i) is thes lowbit of i,
            # e.g. i = 10, 10 = (1100)2, lowbit(1100_2) = 100_2
            i += i & -i

from collections import deque
class MKAverage5:
    def __init__(self, m: int, k: int):
        """
        Constraints:
            3 <= m <= 105
            1 <= k*2 < m
            1 <= num <= 105
            At most 105 calls will be made to addElement and calculateMKAverage.
        """
        self.max_num = 10 ** 5
        self.m = m
        self.k = k
        # self.queue_len_limit = m - 2 * k
        self.queue = deque([])
        """
        self.value_tree.query(num) = the sum of the number which <= num
        self.count_tree.query(num) = the count of the number which <=num
        """
        self.value_tree = BinaryIndexTree(self.max_num)
        self.count_tree = BinaryIndexTree(self.max_num)

        self.remaining_cnt = m - 2 * k
        self.MKAverage = None  # avoid repetitive computing for MKAverage

    def addElement(self, num: int) -> None:
        self.value_tree.update(num, num)
        self.count_tree.update(num, 1)
        self.queue.append(num)
        if len(self.queue) > self.m:
            removed_num = self.queue.popleft()
            self.value_tree.update(removed_num, -removed_num)
            self.count_tree.update(removed_num, -1)

        self.MKAverage = None  # avoid recomputing MKAverage if no new number added

    def search_low_boundary(self, count: int) -> int:
        """
        search the minimum num that makes self.count_tree.query(num) >= count
        即self.queue中小于等于num的数至少有count个
        e.g, input 1,
        self.value_tree.nodes = [0, 1, 3, 3, 10, 5, 11, 7, 36]
        self.count_tree.nodes = [0, 1, 2, 1, 4, 1, 2, 1, 8]
        self.max_num = 8
        i, low_boundary_num, prefix_sum
        3,   0,     0
        2,   0,     0
        1,   2,     2
        0,   2,     2

        low_boundary_num = 2, low_boundary_num + 1 = 3,
        so search_low_boundary(3) = 3 means the minimum num that makes self.count_tree.query(num) >= 3
        """
        # there are prefix_sum numbers that are less than count
        low_boundary_num, prefix_sum = 0, 0
        # self.max_num + 1 是self.count_tree.sums的长度
        for i in range(int(math.log2(self.max_num + 1)), -1, -1):
            low_boundary_num2 = low_boundary_num + (1 << i)
            # print(i, low_boundary_num, prefix_sum, low_boundary_num2)

            if low_boundary_num2 <= self.max_num and prefix_sum + self.count_tree.nodes[low_boundary_num2] < count:
                prefix_sum += self.count_tree.nodes[low_boundary_num2]
                low_boundary_num = low_boundary_num2

        # +1 because 'low_boundary_num' will have position of largest value less than 'count'
        return low_boundary_num + 1

    def calculateMKAverage(self) -> int:
        if len(self.queue) < self.m:
            return -1
        if self.MKAverage is not None:
            return self.MKAverage
        low_num, high_num = self.search_low_boundary(self.k), self.search_low_boundary(self.m - self.k)
        # num_sum = self.value_tree.query(high_num) - self.value_tree.query(low_num)
        high, low = self.value_tree.query(high_num),  self.value_tree.query(low_num)
        num_sum = high - low
        print(self.value_tree.nodes[:6])
        print(self.count_tree.nodes[:6])
        print(low_num, high_num)
        print(low, high, num_sum)
        print(self.count_tree.query(low_num), self.count_tree.query(high_num))
        """
        https://leetcode.com/problems/finding-mk-average/discuss/1152438/Python3-Fenwick-tree/903394
        Because probably there are more than k number that is less than or equal to low_num,
        and there are more than m-k number that is less than or equal to high_num, we have to make some adjustments.
        即有的数多减了, 有的数少减了。需要重新调整
        e.g. m = 6, k = 2, nums = [1,2,2,3,3,4]
        prefix_sum = [0,1,3,5,6] prefix_sum[i] 表示 <= i的数的个数
        k1 = k, k2 = m - k = 4
        -> prefix_sum[lo=2]=3>=k1=2
        -> prefix_sum[hi=3]=5>=k2=4
        -> values_tree.query(hi)-values_tree.query(lo) = sum([1,2,2,3,3]) - sum([1,2,2]) = sum([3,3])
        But the actual solution here is sum([2,3]), we need to remove a 3 and add back a 2.
        """
        num_sum += (self.count_tree.query(low_num) - self.k) * low_num
        num_sum -= (self.count_tree.query(high_num) - (self.m - self.k)) * high_num

        # rounded down to the nearest integer
        self.MKAverage = num_sum // self.remaining_cnt
        return self.MKAverage

if __name__ == "__main__":
    # obj = MKAverage(3, 1)
    # obj.addElement(3)#        // current elements are [3]
    # obj.addElement(1)#        // current elements are [3,1]
    # print(obj.calculateMKAverage())# // return -1, because m = 3 and only 2 elements exist.
    # obj.addElement(10)#       // current elements are [3,1,10]
    # print(obj.calculateMKAverage())# // The last 3 elements are [3,1,10].
    #                         # // After removing smallest and largest 1 element the container will be [3].
    #                         # // The average of [3] equals 3/1 = 3, return 3
    # obj.addElement(5)#        // current elements are [3,1,10,5]
    # obj.addElement(5)#        // current elements are [3,1,10,5,5]
    # obj.addElement(5)#        // current elements are [3,1,10,5,5,5]
    # print(obj.calculateMKAverage())# // The last 3 elements are [5,5,5].
    #                         # // After removing smallest and largest 1 element the container will be [5].
    #                         # // The average of [5] equals 5/1 = 5, return 5

    # obj = MKAverage(6, 1)
    # obj.addElement(3)#        // current elements are [3]
    # obj.addElement(1)#        // current elements are [3,1]

    # obj.addElement(12)#       // current elements are [3,1,10]

    # obj.addElement(5)#        // current elements are [3,1,10,5]
    # obj.addElement(3)#        // current elements are [3,1,10,5,5]
    # obj.addElement(4)#        // current elements are [3,1,10,5,5,5]
    # print(obj.calculateMKAverage())# // The last 3 elements are [5,5,5].
    #                         # // After removing smallest and largest 1 element the container will be [5].
    #                         # // The average of [5] equals 5/1 = 5, return 5

    # obj = MKAverage(3, 1)
    # obj.addElement(17612)#        // current elements are [3]
    # obj.addElement(74607)#        // current elements are [3,1]
    # print(obj.calculateMKAverage())# // return -1, because m = 3 and only 2 elements exist.
    # obj.addElement(8272)#       // current elements are [3,1,10]

    # obj.addElement(33433)#        // current elements are [3,1,10,5]
    # print(obj.calculateMKAverage())# // return -1, because m = 3 and only 2 elements exist.
    # obj.addElement(15456)#        // current elements are [3,1,10,5,5]
    # obj.addElement(64938)#        // current elements are [3,1,10,5,5,5]
    # print(obj.calculateMKAverage())# // The last 3 elements are [5,5,5].
    #                         # // After removing smallest and largest 1 element the container will be [5].
    #                         # // The average of [5] equals 5/1 = 5, return 5
    # obj.addElement(99741)#        // current elements are [3,1,10,5,5,5]

    obj = MKAverage5(6, 2)
    obj.addElement(1)#        // current elements are [3]
    obj.addElement(2)#        // current elements are [3,1]

    obj.addElement(2)#       // current elements are [3,1,10]

    obj.addElement(3)#        // current elements are [3,1,10,5]
    obj.addElement(3)#        // current elements are [3,1,10,5,5]
    obj.addElement(4)#        // current elements are [3,1,10,5,5,5]
    print(obj.calculateMKAverage())# // The last 3 elements are [5,5,5].
    #


def remainNum(n, k):
    if(n<1 or k<1):
        return False
    tmp = 0
    for i in range(1,n+1):
        tmp = (tmp+k) % i
    return tmp+1

if __name__ == '__main__':
    print(remainNum(5, 2))

62圆圈中剩下的数字.py

62圆圈中剩下的数字.py


# def find_avarage(*args):
#     sum=0
#     count=0
#     for i in args :
#         sum+=int(i)
#         count+=1
#     return sum/count
# print("avarage is :",find_avarage(11,10,12))

# def make_absolute(number):
#     if number >=0 : return number
#     else : return -number

# def max_number(num1,num2):
#     """Finds max number"""
#     if(num1>num2): return num1
#     elif (num1==num2) :
#         print("numbers r equal")
#         return num1
#     else : return num2
# print("max number is:",max_number(12,12))

###### task 4 squares

def square_rectangle(a,b):
    return a*b

def square_circle(radius):
    return 3.14*(radius**2)

def square_triangle(a,b,c):
    p=(a+b+c)/2
    return (p*(p-a)*(p-b)*(p-c))**0.5

choice=int(input("if u want square of triangle type 1 , for circle type 2, for rectangle type 3 :"))

if choice ==1 :
    print("now input 3 sides")
    side1=float(input("input side1:"))
    side2=float(input("input side2:"))
    side3=float(input("input side3:"))
    print("square of triangle is",square_triangle(side1,side2,side3))
elif choice == 2 :
    radius=float(input("input radius:"))
    print("square of circle is :",square_circle(radius))
elif choice == 3 :
    side1 = float(input("input side1:"))
    side2 = float(input("input side2:"))
    print("square of rectangle is:",square_rectangle(side1,side2))
else : print("u did something wrong , restart the program(")