"""
A transaction is possibly invalid if:
-    the amount exceeds $1000, or;
-    if it occurs within (and including) 60 minutes of another transaction with the same name in a different city.

You are given an array of strings transaction where transactions[i] consists of comma-separated values representing the
name, time (in minutes), amount, and city of the transaction.

Return a list of transactions that are possibly invalid. You may return the answer in any order.

Example 1:
Input: transactions = ["alice,20,800,mtv","alice,50,100,beijing"]
Output: ["alice,20,800,mtv","alice,50,100,beijing"]
Explanation: The first transaction is invalid because the second transaction occurs within a difference of 60 minutes,
have the same name and is in a different city. Similarly the second one is invalid too.

Example 2:
Input: transactions = ["alice,20,800,mtv","alice,50,1200,mtv"]
Output: ["alice,50,1200,mtv"]

Example 3:
Input: transactions = ["alice,20,800,mtv","bob,50,1200,mtv"]
Output: ["bob,50,1200,mtv"]
"""
from typing import List

class Solution:
    """
    Probably, most downvoted problem on LC due to its ambiguous/vague description and a number of corner cases.

    Runtime: 148 ms, faster than 44.76% of Python3
    Memory Usage: 14.7 MB, less than 91.39% of Python3

    Time complexity: O(n^2) as we need to compare at worst all possible pairs of transactions
    Space complexity: O(n) - in worst case all transactions are invalid
    """

    def invalidTransactions(self, transactions: List[str]) -> List[str]:
        invalid = list()
        for i, first in enumerate(transactions):
            name1, time1, amount1, city1 = first.split(",")
            time1 = int(time1)
            if int(amount1) > 1000:
                invalid.append(first)
                continue
            for j, second in enumerate(transactions):
                name2, time2, amount2, city2 = second.split(",")
                time2 = int(time2)
                if i != j and name1 == name2 and abs(time1 - time2) <= 60 and city1 != city2:
                    invalid.append(first)
                    break
        return invalid

if __name__ == '__main__':
    solutions = [Solution()]
    tc = (
        (["alice,20,1220,mtv", "alice,20,1220,mtv"], ["alice,20,1220,mtv", "alice,20,1220,mtv"]),
        (["alice,20,800,mtv", "alice,50,100,beijing"], ["alice,20,800,mtv", "alice,50,100,beijing"]),
        (["alice,20,800,mtv", "alice,50,1200,mtv"], ["alice,50,1200,mtv"]),
        (["alice,20,800,mtv", "bob,50,1200,mtv"], ["bob,50,1200,mtv"]),
        (["alice,20,800,mtv", "alice,50,100,mtv", "alice,51,100,frankfurt"],
         ["alice,20,800,mtv", "alice,50,100,mtv", "alice,51,100,frankfurt"]),
        (["bob,689,1910,barcelona", "bob,832,1726,barcelona", "bob,820,596,bangkok", "bob,175,221,amsterdam"],
         ["bob,689,1910,barcelona", "bob,832,1726,barcelona", "bob,820,596,bangkok"]),
    )
    for sol in solutions:
        for inp_tr, exp_res in tc:
            result = sol.invalidTransactions(inp_tr)
            assert sorted(result) == sorted(exp_res), f"actual {sorted(result)} != {sorted(exp_res)} expected"


# 1

my_favorite_artist = ["いきものがかり", "マイケルジャクソン", "bigbang", "kara"]

# 2

location = [(35.1814, 136.9063), (35.0116, 135.768), (35.6895, 139.6917)]

# 3

my_info = {
    "address": "Nagoya",
    "tel": "080-8753-9628",
    "favorite_color": "red",
}

# 4
string = input("キーを入力してください：")
if string in my_info:
    string = my_info[string]
    print(string)
else:
    print("正しくありません。")

# 5
my_favorite_artist_dic = {"kara": ["Go Go summer", "jumpin", "jetcoster love"]}

# 6
# set()
# セットを生成する際には重複する要素は取り除かれます。
# また、ディクショナリからセットを生成すると、キーがセットの要素となります。


import glob
import pandas as pd

import moviepy.editor as mpy
from matplotlib import pyplot as plt
from sklearn.tree import DecisionTreeClassifier

from adaboost.AdaBoost import AdaBoost

FILE = "chips"
ANIMATION_ROOT = "animation/"
ITERATIONS = 2800
ANIMATE = False

class Dataset:
    def __init__(self, dataset):
        self.features = dataset[:, :-1]
        self.targets = [1 if x == 'P' else -1 for x in dataset[:, 2]]
        self.amount = len(self.targets)

def draw_plot(quality):
    plt.close()
    steps = [x[0] for x in quality]
    accuracy = [x[1] for x in quality]
    plt.xlabel("Ada step")
    plt.ylabel("Accuracy")
    plt.xscale("log")
    plt.grid(True)
    plt.plot(steps, accuracy, 'r')
    plt.show()

def draw_animation():
    scatters_list = glob.glob(ANIMATION_ROOT + FILE + "/*")
    clip = mpy.ImageSequenceClip(scatters_list, fps=50, durations=500)
    clip.write_gif(ANIMATION_ROOT + FILE + '/' + FILE + ".gif")

dataset = Dataset(pd.read_csv(open(FILE + ".csv")).values)
ada = AdaBoost(ITERATIONS, DecisionTreeClassifier(max_depth=1))
draw_plot(ada.boost(dataset, ANIMATION_ROOT + FILE if ANIMATE else None))
if ANIMATE:
    draw_animation()


# Thanks to @AvinashReddy3108 for this plugin

import asyncio
import os
import time
from datetime import datetime
from html import unescape
from pathlib import Path

from googleapiclient.discovery import build
from telethon.errors.rpcerrorlist import YouBlockedUserError
from telethon.tl.types import DocumentAttributeAudio
from youtube_dl import YoutubeDL
from youtube_dl.utils import (
    ContentTooShortError,
    DownloadError,
    ExtractorError,
    GeoRestrictedError,
    MaxDownloadsReached,
    PostProcessingError,
    UnavailableVideoError,
    XAttrMetadataError,
)

from ..utils import admin_cmd, edit_or_reply
from . import CMD_HELP

@borg.on(admin_cmd(pattern="yt(a|v) (.*)", outgoing=True))
async def download_video(v_url):
    """ For .ytdl command, download media from YouTube and many other sites. """
    url = v_url.pattern_match.group(2)
    ytype = v_url.pattern_match.group(1).lower()
    v_url = await edit_or_reply(v_url, "`Preparing to download...`")
    if ytype == "a":
        opts = {
            "format": "bestaudio",
            "addmetadata": True,
            "key": "FFmpegMetadata",
            "writethumbnail": True,
            "prefer_ffmpeg": True,
            "geo_bypass": True,
            "nocheckcertificate": True,
            "postprocessors": [
                {
                    "key": "FFmpegExtractAudio",
                    "preferredcodec": "mp3",
                    "preferredquality": "320",
                }
            ],
            "outtmpl": "%(id)s.mp3",
            "quiet": True,
            "logtostderr": False,
        }
        video = False
        song = True
    elif ytype == "v":
        opts = {
            "format": "best",
            "addmetadata": True,
            "key": "FFmpegMetadata",
            "writethumbnail": True,
            "prefer_ffmpeg": True,
            "geo_bypass": True,
            "nocheckcertificate": True,
            "postprocessors": [
                {"key": "FFmpegVideoConvertor", "preferedformat": "mp4"}
            ],
            "outtmpl": "%(id)s.mp4",
            "logtostderr": False,
            "quiet": True,
        }
        song = False
        video = True
    try:
        await v_url.edit("`Fetching data, please wait..`")
        with YoutubeDL(opts) as ytdl:
            ytdl_data = ytdl.extract_info(url)
    except DownloadError as DE:
        await v_url.edit(f"`{str(DE)}`")
        return
    except ContentTooShortError:
        await v_url.edit("`The download content was too short.`")
        return
    except GeoRestrictedError:
        await v_url.edit(
            "`Video is not available from your geographic location due to geographic restrictions imposed by a website.`"
        )
        return
    except MaxDownloadsReached:
        await v_url.edit("`Max-downloads limit has been reached.`")
        return
    except PostProcessingError:
        await v_url.edit("`There was an error during post processing.`")
        return
    except UnavailableVideoError:
        await v_url.edit("`Media is not available in the requested format.`")
        return
    except XAttrMetadataError as XAME:
        await v_url.edit(f"`{XAME.code}: {XAME.msg}\n{XAME.reason}`")
        return
    except ExtractorError:
        await v_url.edit("`There was an error during info extraction.`")
        return
    except Exception as e:
        await v_url.edit(f"{str(type(e)): {str(e)}}")
        return
    c_time = time.time()
    catthumb = Path(f"{ytdl_data['id']}.jpg")
    if not os.path.exists(catthumb):
        catthumb = Path(f"{ytdl_data['id']}.webp")
    elif not os.path.exists(catthumb):
        catthumb = None
    if song:
        await v_url.edit(
            f"`Preparing to upload song:`\
        \n**{ytdl_data['title']}**\
        \nby *{ytdl_data['uploader']}*"
        )
        await v_url.client.send_file(
            v_url.chat_id,
            f"{ytdl_data['id']}.mp3",
            supports_streaming=True,
            thumb=catthumb,
            attributes=[
                DocumentAttributeAudio(
                    duration=int(ytdl_data["duration"]),
                    title=str(ytdl_data["title"]),
                    performer=str(ytdl_data["uploader"]),
                )
            ],
            progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
                progress(
                    d, t, v_url, c_time, "Uploading..", f"{ytdl_data['title']}.mp3"
                )
            ),
        )
        os.remove(f"{ytdl_data['id']}.mp3")
        if catthumb:
            os.remove(catthumb)
        await v_url.delete()
    elif video:
        await v_url.edit(
            f"`Preparing to upload video:`\
        \n**{ytdl_data['title']}**\
        \nby *{ytdl_data['uploader']}*"
        )
        await v_url.client.send_file(
            v_url.chat_id,
            f"{ytdl_data['id']}.mp4",
            supports_streaming=True,
            caption=ytdl_data["title"],
            progress_callback=lambda d, t: asyncio.get_event_loop().create_task(
                progress(
                    d, t, v_url, c_time, "Uploading..", f"{ytdl_data['title']}.mp4"
                )
            ),
        )
        os.remove(f"{ytdl_data['id']}.mp4")
        if catthumb:
            os.remove(catthumb)
        await v_url.delete()

@borg.on(admin_cmd(pattern="yts (.*)"))
async def yt_search(video_q):
    """ For .yts command, do a YouTube search from Telegram. """
    query = video_q.pattern_match.group(1)
    result = ""
    if not Config.YOUTUBE_API_KEY:
        await edit_or_reply(
            video_q,
            "`Error: YouTube API key missing! Add it to reveal config vars in heroku or userbot/uniborgConfig.py in github fork.`",
        )
        return
    video_q = await edit_or_reply(video_q, "```Processing...```")
    full_response = await youtube_search(query)
    videos_json = full_response[1]
    for video in videos_json:
        title = f"{unescape(video['snippet']['title'])}"
        link = f"https://youtu.be/{video['id']['videoId']}"
        result += f"{title}\n{link}\n\n"
    reply_text = f"**Search Query:**\n`{query}`\n\n**Results:**\n\n{result}"
    await video_q.edit(reply_text)

async def youtube_search(
    query, order="relevance", token=None, location=None, location_radius=None
):
    """ Do a YouTube search. """
    youtube = build(
        "youtube", "v3", developerKey=Config.YOUTUBE_API_KEY, cache_discovery=False
    )
    search_response = (
        youtube.search()
        .list(
            q=query,
            type="video",
            pageToken=token,
            order=order,
            part="id,snippet",
            maxResults=10,
            location=location,
            locationRadius=location_radius,
        )
        .execute()
    )
    videos = [
        search_result
        for search_result in search_response.get("items", [])
        if search_result["id"]["kind"] == "youtube#video"
    ]

    try:
        nexttok = search_response["nextPageToken"]
        return (nexttok, videos)
    except HttpError:
        nexttok = "last_page"
        return (nexttok, videos)
    except KeyError:
        nexttok = "KeyError, try again."
        return (nexttok, videos)

@borg.on(admin_cmd(pattern="insta (.*)"))
async def kakashi(event):
    if event.fwd_from:
        return
    chat = "@allsaverbot"
    link = event.pattern_match.group(1)
    if "www.instagram.com" not in link:
        await edit_or_reply(
            event, "` I need a Instagram link to download it's Video...`(*_*)"
        )
    else:
        start = datetime.now()
        catevent = await edit_or_reply(event, "**Downloading.....**")
    async with event.client.conversation(chat) as conv:
        try:
            msg_start = await conv.send_message("/start")
            response = await conv.get_response()
            msg = await conv.send_message(link)
            details = await conv.get_response()
            await conv.get_response()
            await conv.get_response()
            video = await conv.get_response()
            await event.client.send_read_acknowledge(conv.chat_id)
        except YouBlockedUserError:
            await catevent.edit("**Error:** `unblock` @allsaverbot `and retry!`")
            return
        await catevent.delete()
        cat = await event.client.send_file(
            event.chat_id,
            video,
        )
        end = datetime.now()
        ms = (end - start).seconds
        await cat.edit(
            f"➥ Video uploaded in {ms} seconds.\n➥ Uploaded by :- {hmention}",
            parse_mode="html",
        )
    await event.client.delete_messages(
        conv.chat_id, [msg_start.id, response.id, msg.id, details.id, video.id]
    )

CMD_HELP.update(
    {
        "ytdl": "**Plugin :** `ytdl`\
    \n\n**Syntax :** `.yta link`\
    \n**Usage : **downloads the audio from the given link(Suports the all sites which support youtube-dl)\
    \n\n**Syntax : **`.ytv link`\
    \n**Usage : **downloads the video from the given link(Suports the all sites which support youtube-dl)\
    \n\n**Syntax : ** `.yts query`\
    \n**Usage : **Fetches youtube results you need api token for this\
    \n\n**Syntax : ** `.insta` <link>\
    \n**Usage : **Downloads the video from the given instagram link\
    "
    }
)


import os, sys, time

# this rmOldSessionFiles.py script can be run manually or as a scheduled PA task

rmOldSessionFileAge = 86400 # this value must be greater than app.config['PERMANENT_SESSION_LIFETIME']

def rmOldSessionFiles():
  try:
    now = time.time()
    path = os.getcwd() + '/flask_session/'
    path = path.replace('mysite', '')  # pn PA the flask_session dir is not
    print('rmOldSessionFiles() path = ' + path)
    for f in os.listdir(path):
      filePath = os.path.join(path, f)
      if os.path.isfile(filePath):
        if os.stat(filePath).st_mtime < now - rmOldSessionFileAge:
          print('rmOldSessionFiles() - removing file: ' + filePath)
          os.remove(filePath)

  except (OSError, IOError) as e:
    print('Error: rmOldSessionFiles(): - Shutil.Error | OSError | IOError: during copy phase: ' + str(e))
  except:
    tupleExc = sys.exc_info()
    print('Error: rmOldSessionFiles(): - Unexpected Exception: ' + str(tupleExc[0]) + '  ' + str(tupleExc[1]))

rmOldSessionFiles()


import torch
from torch.autograd import Variable
import torch.nn as nn
import torch.nn.functional as F
# can use the below import should you choose to initialize the weights of your Net
import torch.nn.init as I

class Net(nn.Module):

    def __init__(self):
        super(Net, self).__init__()

        ## 1. This network takes in a square (224,224), grayscale image as input
        ## 2. It ends with a linear layer that represents the keypoints
        ## it's suggested that you make this last layer output 136 values, 2 for each of the 68 keypoint (x, y) pairs

        # 1 input image channel (grayscale), 32 output channels/feature maps, 5x5 square convolution kernel
        self.conv1 = nn.Conv2d(1, 32, 5) # (224-5)+1 = 221, 221/2 = 110 after pooling
        self.conv2 = nn.Conv2d(32, 64, 4) # (110-4)+1 = 107, 107/2 = 53 after pooling
        self.conv3 = nn.Conv2d(64, 128, 3) # (53-3)+1 = 51, 51/2 = 25 after pooling
        self.conv4 = nn.Conv2d(128, 256, 2) # (25-2)+1 = 24, 24/2 = 12 after pooling
        self.conv5 = nn.Conv2d(256, 512, 1) # (12-1)+1 = 12, 12/2 = 6 after pooling

        # Dropout layers
        self.dropout1 = nn.Dropout2d(p=0.1) # for conv2d
        self.dropout2 = nn.Dropout2d(p=0.2) # for conv2d
        self.dropout3 = nn.Dropout2d(p=0.3) # for conv2d
        self.dropout4 = nn.Dropout2d(p=0.4) # for conv2d
        self.dropout5 = nn.Dropout2d(p=0.5) # for conv2d

        self.dropout6 = nn.Dropout(p=0.5) # for linear1d
        self.dropout7 = nn.Dropout(p=0.6) # for linear1d

        # Maxpolling layer
        self.pool = nn.MaxPool2d(kernel_size=2,stride=2)

        # Dense layers, input_feature = 512*6*6 = 18432
        self.fc1 = nn.Linear(18432,1000)
        self.fc2 = nn.Linear(1000,512)
        self.out = nn.Linear(512,68*2)

        # Batchnorm layers

        self.BF1 = nn.BatchNorm2d(32) # for conv2d
        self.BF2 = nn.BatchNorm2d(64) # for conv2d
        self.BF3 = nn.BatchNorm2d(128) # for conv2d
        self.BF4 = nn.BatchNorm2d(256) # for conv2d

        self.BF5 = nn.BatchNorm1d(18432) # for linear1d
        self.BF6 = nn.BatchNorm1d(1000) # for linear1d
        self.BF7 = nn.BatchNorm1d(512) # for linear1d

    def forward(self, x):
        ## x is the input image and, as an example, here you may choose to include a pool/conv step:
        x = self.BF1(self.dropout1(self.pool(F.relu(self.conv1(x)))))
        x = self.BF2(self.dropout2(self.pool(F.relu(self.conv2(x)))))
        x = self.BF3(self.dropout3(self.pool(F.relu(self.conv3(x)))))
        x = self.BF4(self.dropout4(self.pool(F.relu(self.conv4(x)))))
        x = self.dropout5(self.pool(F.relu(self.conv5(x))))
        # flatten
        x = self.BF5(x.view(x.size(0),-1))
        x = self.BF6(self.dropout6(F.relu(self.fc1(x))))
        x = self.BF7(self.dropout7(F.relu(self.fc2(x))))
        x = self.out(x)

        # a modified x, having gone through all the layers of your model, should be returned
        return x


from rest_framework.throttling import UserRateThrottle

class UserThrottle(UserRateThrottle):
    def get_cache_key(self, request, view):
        if request.user and request.user.is_authenticated:
            ident = request.user.pk
        else:
            ident = self.get_ident(request)

        return self.cache_format % {
            'scope': self.scope,
            'ident': ident
        }


# -*- coding: utf-8 -*-
"""
Created on Wed Jan 22 20:49:29 2020

Set of functions to create classification and regression trees.

The whole script is based on:
    https://github.com/SebastianMantey

"""

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
# import seaborn as sns

import random
import os

# TRAIN-TEST-SPLIT
def train_test_split(df, test_size):
    """
    DESCRIPTION:
        Performs train-test-split.
        Test size can be entered a percentage of the total entries or as an integer

    """

    if isinstance(test_size, float):                                # if test_size is entered as a percentage (float)...
        test_size = round(test_size * len(df))                      # ...make it an integer

    indices = df.index.tolist()                                      # which is here extrated a transformed to a list
    test_indices = random.sample(population=indices, k= test_size)   # train and test values are randomly picked based on their row index,

    test_df = df.loc[test_indices]
    train_df = df.drop(test_indices)

    return train_df, test_df

def check_purity(data):
    """
    DESCRIPTION:
        Check if is a certain partition of the data separates/isolates a whole class of the target variable.
        The term 'pure' is intended in that sense  i.e. the partition contains only one class.

    INPUT:
        - data (numpy array)  -> value of the dataframe i.e. no index or column headers

    OUTPUT:
        - True/False (boolean)

    """

    label_column = data[:, -1]                  # get label column, which HAS to be the leftmost
    unique_classes = np.unique(label_column)    # get number of unique classes

    if len(unique_classes) == 1:
        return True
    else:
        return False

def create_leaf(data, ml_task):
    """
    DESCRIPTION:
        Depending on the specified algorithm i.e. classification or regression different tasks are performed.
        If regression is chosen the mean of the label values is returned.

        If classification is chosen:
        Returns the label for the class that occurs more often in the input dataset.
        If there are two classes that appear equally often one of them is picked randomly.
        Ideally the function runs only if a pure group is found (using function 'check_purity').
        However it can be used also if data are not pure and in that case it returns the most recurring values

    INPUT:
        - data    (np array)    -> value of the dataframe i.e. no index or column headers
        - ml_task (str)         -> can either be 'regression' or 'classification'

    OUTPUT:
        - classification (str)   -> label of the classified class

    EXAMPLE:
        create_leaf(data, ml_task='regression')
        create_leaf(data, ml_task='classification')

    """

    label_column = data[:, -1]                  # get label column, which HAS to be the leftmost

    if ml_task == 'regression':
        leaf = np.mean(label_column)

    else: # classification
        unique_classes, counts_unique_classes = np.unique(label_column, return_counts=True)     # unique values and respective occurrence

        index = counts_unique_classes.argmax()      # find the class that appears most often
        leaf = unique_classes[index]                # class that appears more often in the data

    return leaf

def get_potential_splits(data):
    """
    DESCRIPTION:
        Returns a dictionary where the keys are the indices of the columns i.e. 0, 1, 2, 3, etc.
        The dictionary values are lists with potential splits for the feature corresping to the considered key i.e. 0, 1, 2, etc.
        For each split obtained the purity can then be assessed.

    INPUT:
        - data           (np array)  -> value of the dataframe i.e. no index or column headers

    OUTPUT:
        - potential_splits (dict)    -> keys are the featurs and values are the values where the splits can be made

    PRECONDITION:
        - The input np array 'data' cannot be a single column but it must have > 1 columns

    EXAMPLE:
        potential_splits = get_potential_splits(df.values)

    """

    potential_splits = {}
    _ , n_columns = data.shape                       # getting the number of columns to iterate on
    for column_index in range(n_columns - 1):       # skip the label column
        # potential_splits[column_index] = []
        values = data[:, column_index]
        unique_values = np.unique(values)           # the unique values are used to set the splitting points

        potential_splits[column_index] = unique_values

    return potential_splits

def split_data(data, split_column, split_value):
    """
    DESCRIPTION:
        Split data in 2 parts at a specified value.

    INPUT:
        - data          (numpy array)  -> Dataframe values or numpy matrix. It does not work if it is a pandas df with header!
        - split_column  (int)          -> Column to consider for the split
        - split_value   (float)        -> Value along the column to split at

    OUTPUT:
        - data_below    (np array)     -> data in the left section of the splitted range
        - data_above    (np array)     -> data in the right section of the splitted range

    EXAMPLE:
        data_below, data_above = split_data(df.values, split_column=1, split_value='male')

    """

    split_column_values = data[:, split_column]

    type_of_feature = FEATURE_TYPES[split_column]           # check what type of values are in this column. See function 'determine_type_of_feature(df)'

    if type_of_feature == 'continuous':
        data_below = data[split_column_values <= split_value]
        data_above = data[split_column_values > split_value]

    else:
        data_below = data[split_column_values == split_value]
        data_above = data[split_column_values != split_value]

    return data_below, data_above

def calculate_mse(data):
    """
    DESCRIPTION:
        Calculate mean square error of the label column. It will be used in case of regression.
    """

    actual_values = data[:,-1]

    if len(actual_values) == 0: # empty data
        mse = 0
    else:
        prediction = np.mean(actual_values)
        mse = np.mean((actual_values - prediction)**2)

    return mse

def calculate_entropy(data):
    """
    DESCRIPTION:
        Calculate entropy of one of the 2 partition of the target resulting from a certain split.
        The calculation is done according to the formula sum(p_i*(-log2p_i)),
        where index 'i' refers to the unique classes of the target variable in the considered partition.

    INPUT:
        - data (np array) -> Dataframe values or numpy matrix. It does not work if it is a pandas df with header!

    OUTPUT:
        - entropy (float) -> Entropy of the target variable in the considered partition

    """

    label_column = data[:, -1]
    _ , counts =  np.unique(label_column, return_counts=True)

    probabilities = counts / counts.sum()                       # array of probabilities, used to calculate entropy
    entropy = sum(probabilities * -np.log2(probabilities))      # entropy of the target variable associated to a certain split

    return entropy

def calculate_overall_metric(data_below, data_above, metric_function):
    """
    DESCRIPTION:
        Calculate total entropy resulting from a split as the sum of the entropies of the 2 partitions
        weighed by the respective probabilities (or percentages).
        The calculation is done using formula sum(p_j*Entropy_j),
        where the index j refers to each of the 2 partitions resulting from the split.

    INPUT:
        - data_below (np array)     -> data in the left section of the splitted range
        - data_above (np array)     -> data in the right section of the splitted range
        - metric_function (str)     -> can either be 'calculate_entropy' for classification or 'calculate_mse' for regression

    """

    n_data_points = len(data_below) + len(data_above)

    p_data_below = len(data_below) / n_data_points      # probability / weight for the partition on the left of the split
    p_data_above = len(data_above) / n_data_points      # probability / weight for the partition on the right of the split

    overall_metric = (p_data_below * metric_function(data_below) + p_data_above * metric_function(data_above))

    return overall_metric

def determine_best_split(data, potential_splits, ml_task):
    """
    DESCRIPTION:
        Determines the split that results in the lowest entropy (if classification) or lowest mse (regression)

    INPUT:
        - data          (numpy array)   -> Dataframe values or numpy matrix. It does not work if it is a pandas df with header!
        - potential_splits (dict)       -> keys are the featurs and values are the values where the splits can be made
        - ml_task           (str)       -> can either be 'regression' or 'classification'
    """

    first_iteration = True       # makes sure the cycle 'if first_iteration or (current_overall_metric <= overall_metric):' is executed at least once

    for column_index in potential_splits:           	                                # loop through the keys (features) of the dictionary
        for value in potential_splits[column_index]:                                    # loop through the possible splits for each feature
            data_below, data_above = split_data(data, split_column=column_index, split_value=value)              # derive the 2 partitions

            if ml_task == 'regression':
                current_overall_metric = calculate_overall_metric(data_below, data_above, calculate_mse) # calculate overall entropy resulting from the current partition

            else:   # classification
                current_overall_metric = calculate_overall_metric(data_below, data_above, calculate_entropy) # calculate overall entropy resulting from the current partition

            if first_iteration or (current_overall_metric <= best_overall_metric):
                first_iteration = False

                best_overall_metric = current_overall_metric
                best_split_column = column_index
                best_split_value = value

    return best_split_column, best_split_value

def determine_type_of_feature(df):
    """
    DESCRIPTION:
        Determine is a column feature is a contiunous value or a categorical.
        To evaluate this the number of different values in a column is considered: it is assumed that
        a categorical feature should not have 'too many' different values. A continuous feature has typically many values.

    INPUT:
        - df (pd dataframe)         -> dataset containing column names

    OUTPUT:
        - feature_types (list(str)) -> list of column types

    """
    feature_types = []                  # list containing the type of a feature (categorical or continuous)
    n_unique_values_threeshold = 15     # max number of different unique values for a feature to be considered categorical

    for column in df.columns:
        unique_values = df[column].unique()     # number of unique values for that feature
        example_value = unique_values[0]        # take first element to check its type

        if (isinstance(example_value, str)) or (len(unique_values) <= n_unique_values_threeshold):  # strings or features with few unique values
            feature_types.append('categorical')
        else:
            feature_types.append('continuous')

    return feature_types

def decision_tree_algorithm(df, ml_task, counter=0, min_samples=2, max_depth=5):
    """
    DESCRIPTION:
        Create the decision tree using a recursive function starting with a base case.
        The function allows to decide if using a pandas dataframe or a numpy array.
        The parameter that regulates this is 'counter'. Pandas dataframes are slower to process than numpy arrays.

    INPUT:
        - df (pd dataframe / np array)    -> data, including features and target.
        - counter (float)                 -> indicates if df is a pd dataframe or a numpy array
        - min_samples (int)               -> minimum number of samples
        - ml_task (str)                   -> either 'regression' or 'classification'

    OUTPUT:
        - classification (str)            -> label of the classified class
        - sub_tree (dict)                 -> tree containing splits (questions) and answers

    """

    # DATA PREPARATION
    if counter == 0:                    # this means that the input df is a pandas dataframe
        global COLUMN_HEADERS           # has to be global otherwise in the recursive steps it would be ignored
        global FEATURE_TYPES            # list of types of features
        COLUMN_HEADERS = df.columns     # get the name of the columns
        FEATURE_TYPES = determine_type_of_feature(df)
        data = df.values                # and it has to be transformed in a numpy array
    else:
        data = df

    # BUILD DECISION TREE ALGORITHM USING A RECURSIVE FUNCTION
    if (check_purity(data)) or (len(data) < min_samples) or (counter==max_depth):       # The data contains only 1 class i.e. pure or there are very few samples
        leaf = create_leaf(data, ml_task)                    # get the name of the class that makes up the whole target
        return leaf

    # RECURSIVE PART
    else:                                       # the partition is not pure, there are > 1 classes
        counter += 1

        # HELPER FUNCTIONS
        potential_splits = get_potential_splits(data)
        split_column, split_value = determine_best_split(data, potential_splits, ml_task)
        data_below, data_above = split_data(data, split_column, split_value)

        # CHECK FOR EMPTY DATA
        if len(data_below) == 0 or len(data_above) == 0:
            leaf = create_leaf(data, ml_task)                    # get the name of the class that makes up the whole target
            return leaf

        # INSTANTIATE SUB-TREE
        feature_name = COLUMN_HEADERS[split_column]

        # CHECK THE TYPE OF THE FEATURE - CONTINUOUS OR CATAEGORICAL?
        type_of_feature = FEATURE_TYPES[split_column]           # check what type of values are in this column. See function 'determine_type_of_feature(df)'

        if type_of_feature == 'continuous':
            question = f'{feature_name} <= {split_value}'
        else:
            question = f'{feature_name} = {split_value}'

        sub_tree = {question: []}

        # FIND ANSWERS (RECURSIVE PART)
        yes_answer = decision_tree_algorithm(data_below, ml_task, counter, min_samples, max_depth)
        no_answer = decision_tree_algorithm(data_above, ml_task, counter, min_samples, max_depth)

        if yes_answer == no_answer:
            sub_tree = yes_answer

        else:
            sub_tree[question].append(yes_answer)
            sub_tree[question].append(no_answer)

        return sub_tree

def predict_example(example, tree):
    """
    DESCRIPTION:
        Make prediction for one specific case using a specified tree.

    INPUT:
        - example (np array) -> data point i.e. a signle row of the dataset
        - tree      (dic)    -> decision tree obtained using the function 'decision_tree_algorithm'
    """

    # sub_tree = {question: [yes_answer, no_answer]}                # this is shown for convenience: reminds that at each subtree there is a yes and no answer

    question = list(tree.keys())[0]                                 # take the questions from the tree, which is a dictionary
    feature_name, comparison_operator, value = question.split()     # split the string to derive its constituents

    # ASKING QUESTION
    if comparison_operator == '<=':
        if example[feature_name] <= float(value):
            answer = tree[question][0]                                  # 'yes' answer at that split of the decision tree. Note it can be a dict or a str.
        else:
            answer = tree[question][1]                                  # 'no' answer at that split of the decision tree. Note it can be a dict or a str.

    else:                                                               # categorical feature
        if str(example[feature_name]) == value:
            answer = tree[question][0]                                  # 'yes' answer at that split of the decision tree. Note it can be a dict or a str.
        else:
            answer = tree[question][1]

    # BASE CASE
    if not isinstance(answer, dict):                                # not a dictionary, the end of a branch was reached and a classification (str) was given
        return answer                                               # classified item

    # RECURSIVE PART
    else:                                                           # answer 'no', another dictionary that has to be processed by the function classify_example
        residual_tree = answer                                      # remind the answer is a dict
        return predict_example(example, residual_tree)             # recursion

def calculate_r_squared(df, tree):
    """
    DESCRIPTION:
        Calculate r^2 to check the accuracy of the prediction.
    """

    labels = df['label']
    mean = labels.mean()
    predictions = df.apply(predict_example, args=(tree,), axis=1)

    ss_res = sum((labels - predictions)**2)
    ss_tot = sum((labels - mean)**2)
    r_squared = 1 - ss_res / ss_tot

    return r_squared

def calculate_accuracy(df, tree):

    df['classification'] = df.apply(predict_example, axis=1, args=(tree,))
    df['classification_correct'] = df['classification'] == df['label']

    accuracy = df['classification_correct'].mean()

    return accuracy

def hyperparameter_tuning(train_df, val_df, max_depth_range, min_samples_range):
    """
    DESCRIPTION:
        Performs parameter optimisation for decision tree regression.

    INPUT:
        - train_df (pd dataframe)           -> train dataframe
        - val_df (pd dataframe)             -> validation dataframe
        - max_depth_range (list(int))       -> range of the maximum depth of the tree to be investigated. The order has to be [min_value, max_value, step]
        - min_samples_range (list(int))     -> range of the minimum number of samples of the tree to be investigated. The order has to be [min_value, max_value, step]

    OUTPUT:
        - opt_max_depth (int)               -> optimal value (the one that maximises r^2) of the parameter 'max_depth'
        - opt_min_samples (int)             -> optimal value (the one that maximises r^2) of the parameter 'min_samples'
        - opt_r_squared_train (float)       -> maximised train r^2 obtained using 'opt_max_depth' and 'opt_max_depth'
        - opt_r_squared_val (float)         -> maximised validation r^2 obtained using 'opt_max_depth' and 'opt_max_depth'

    PRECONDITION:
        - it is supposed the function 'decision_tree_algorithm' is used to do the grid search
        - the function optimises the parameters 'max_depth' and 'min_samples'
        - no check is performed whether the input lists 'max_depth_range' and 'min_samples_range' are correct [min_value, max_value, step] (see INPUT)

    EXAMPLE:
        max_depth_range = [2,11]
        min_samples_range = [5,10,5]
        opt_max_depth, opt_min_samples, opt_r_squared_train, opt_r_squared_val = hyperparameter_tuning(train_df, val_df, [2,11], [5,10,5])

    """

    grid_search = {'max_depth':[], 'min_samples':[], 'r_squared_train':[], 'r_squared_val':[]}

    # DERIVE LIMITS FOR THE PARAMET 'max_depth' BASED ON THE INPUT LIST
    min_max_depth_range = max_depth_range[0]              # minimum
    max_max_depth_range = max_depth_range[1]              # maximum

    if len(max_depth_range) == 3:               # if also the step is imput in 3rd position of the list
        step_depth = max_depth_range[2]         # step
    else:                                       # otherwise (step not input -> list of length 2) use a step=1 by default
        step_depth = 1

    # DERIVE LIMITS FOR THE PARAMET 'min_samples' BASED ON THE INPUT LIST
    min_min_samples_range = min_samples_range[0]              # minimum
    max_min_samples_range = min_samples_range[1]              # maximum

    if len(min_samples_range) == 3:               # if also the step is imput in 3rd position of the list
        step_samples = min_samples_range[2]         # step
    else:
        step_samples = 1

    for max_depth in range(min_max_depth_range, max_max_depth_range, step_depth):
        for min_samples in range(min_min_samples_range, max_min_samples_range, step_samples):
            tree = decision_tree_algorithm(train_df, ml_task='regression', max_depth=max_depth, min_samples=min_samples)

            r_squared_train = calculate_r_squared(train_df, tree)
            r_squared_val = calculate_r_squared(val_df, tree)

            grid_search['max_depth'].append(max_depth)
            grid_search['min_samples'].append(min_samples)
            grid_search['r_squared_train'].append(r_squared_train)
            grid_search['r_squared_val'].append(r_squared_val)

    grid_search = pd.DataFrame(grid_search)
    grid_search.sort_values('r_squared_val', ascending=False)

    max_r_squared_val = grid_search['r_squared_val'].max()
    best_params = grid_search[grid_search['r_squared_val']==max_r_squared_val].values[0]

    opt_max_depth = int(best_params[0])
    opt_min_samples = int(best_params[1])
    opt_r_squared_train = best_params[2]
    opt_r_squared_val = best_params[3]

    return opt_max_depth, opt_min_samples, opt_r_squared_train, opt_r_squared_val


###
# TEMPORARY
###

import sympy

# Paste the output of `cargo run 21` here
eqn = """

"""

expr = sympy.parsing.sympy_parser.parse_expr(eqn)

ans = sympy.solve(expr)[0]

print(f"Part 2: {ans}")


#!/usr/bin/python3
"""
This script recommends recipes based on input ingredients.
"""

import os
import pickle
import pandas as pd
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

def recommend_recipes(ingredients):
    """
    Recommends recipes based on the input ingredients.

    Args:
        ingredients (str): A comma-separated string of ingredient names.

    Returns:
        list: A list of the top 10 recommended recipe names.
    """

    # Define the file paths
    parent_dir = os.path.abspath(
        os.path.join(os.path.dirname(__file__), os.pardir))
    parsed_recipe_file = os.path.join(parent_dir, 'input', 'final_parsed_recipe.csv')
    model_file = os.path.join(parent_dir, 'input', 'final_tdidf_model')
    encoding_file = os.path.join(parent_dir, 'input', 'final_tdidf_encoding')

    # Load the TfidfVectorizer model and encoding
    with open(model_file, 'rb') as file:
        tfidf = pickle.load(file)
    with open(encoding_file, 'rb') as file:
        tfidf_recipe = pickle.load(file)

    # Parse input ingredients and transform into Tfidf vector
    parsed_ingredients = " ".join(ingredients.split(",")).encode('utf-8')
    input_vector = tfidf.transform([parsed_ingredients])

    # Calculate cosine similarity between input vector and all recipe vectors
    cosine_similarities = cosine_similarity(input_vector, tfidf_recipe)

    # Select top 10 recipes with highest cosine similarity and output their names
    recipe_df = pd.read_csv(parsed_recipe_file)
    similar_recipe_indices = np.argsort(cosine_similarities)
    recommended_recipes = recipe_df.iloc[similar_recipe_indices.ravel()]
    recommended_recipes_list = recommended_recipes[['Recipe_name'
                                                    ]].values.tolist()
    return recommended_recipes_list[::-1][:10]


from dataclasses import dataclass, field
from enum import Enum
from typing import List, Optional

__NAMESPACE__ = "http://checklists.nist.gov/xccdf-p/1.1"

@dataclass
class CheckContentRefType:
    """Data type for the check-content-ref element, which points to the code
    for a detached check in another file.

    This element has no body, just a couple of attributes: href and
    name.  The name is optional, if it does not appear then this
    reference is to the entire other document.
    """
    class Meta:
        name = "checkContentRefType"

    href: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )
    name: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
        }
    )

@dataclass
class CheckContentType:
    """Data type for the check-content element, which holds the actual code of
    an enveloped check in some other (non-XCCDF-P) language.

    This element can hold almost anything.  The content is not
    meaningful as XCCDF-P, though tools may process it or hand it off to
    other tools.
    """
    class Meta:
        name = "checkContentType"

    other_element: List[object] = field(
        default_factory=list,
        metadata={
            "type": "Wildcard",
            "namespace": "##other",
            "mixed": True,
        }
    )

@dataclass
class FactRefType:
    """Type for a reference to Fact; the reference is always by name.

    This is the type for the element fact-ref, which can appear in a
    Platform definition or in a logical-test in a Platform definition.
    """
    class Meta:
        name = "factRefType"

    name: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )

class LogicOperatorEnumType(Enum):
    """Allowed operators for logic tests: we only have two, AND and OR.

    They're capitalized for consistency with usage in OVAL v4.
    """
    OR_VALUE = "OR"
    AND_VALUE = "AND"

@dataclass
class TextType:
    """
    Type for a string with an xml:lang attribute.
    """
    class Meta:
        name = "textType"

    value: Optional[str] = field(
        default=None,
        metadata={
            "required": True,
        }
    )
    lang: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "namespace": "http://www.w3.org/XML/1998/namespace",
        }
    )

@dataclass
class CheckType:
    """Data type for the check element, a checking system specification URI,
    and XML content.

    The check element may appear inside a Fact, giving a means to
    ascertain the value of that Fact using a particular checking engine.
    (This checkType is based on the one in XCCDF, but is somewhat
    simpler. It does not include the notion of exporting values from the
    scope of an XCCDF document to the checking engine.)
    """
    class Meta:
        name = "checkType"

    check_content: Optional[CheckContentType] = field(
        default=None,
        metadata={
            "name": "check-content",
            "type": "Element",
            "namespace": "http://checklists.nist.gov/xccdf-p/1.1",
        }
    )
    check_content_ref: Optional[CheckContentRefType] = field(
        default=None,
        metadata={
            "name": "check-content-ref",
            "type": "Element",
            "namespace": "http://checklists.nist.gov/xccdf-p/1.1",
        }
    )
    system: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )

@dataclass
class LogicTestType:
    """Type for a test against several Facts; the content is one or more fact-
    refs and nested logical-tests.

    Allowed operators are AND and OR. The negate attribute, if set,
    makes the test its logical inverse (so you get NAND and NOR). Note
    that the output of a logical-test is always TRUE or FALSE, Unknowns
    map to FALSE.
    """
    class Meta:
        name = "logicTestType"

    fact_ref: List[FactRefType] = field(
        default_factory=list,
        metadata={
            "name": "fact-ref",
            "type": "Element",
            "namespace": "http://checklists.nist.gov/xccdf-p/1.1",
        }
    )
    logical_test: List["LogicTestType"] = field(
        default_factory=list,
        metadata={
            "name": "logical-test",
            "type": "Element",
            "namespace": "http://checklists.nist.gov/xccdf-p/1.1",
        }
    )
    operator: Optional[LogicOperatorEnumType] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )
    negate: bool = field(
        default=False,
        metadata={
            "type": "Attribute",
        }
    )

@dataclass
class Fact:
    """This element denotes a single named Fact.  Every fact has the following:

    - name, a URI, which must be a unique key
    - title, arbitrary text with xml:lang, optional
    - remark, arbitrary text with xml:lang, optional
    - check, XML content, optional
    """
    class Meta:
        namespace = "http://checklists.nist.gov/xccdf-p/1.1"

    title: List[TextType] = field(
        default_factory=list,
        metadata={
            "type": "Element",
        }
    )
    remark: List[TextType] = field(
        default_factory=list,
        metadata={
            "type": "Element",
        }
    )
    check: List[CheckType] = field(
        default_factory=list,
        metadata={
            "type": "Element",
        }
    )
    name: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )

@dataclass
class Platform:
    """This element denotes a single Platform definition. A Platform definition
    represents the qualifications an IT asset or target must have to be
    considered an instance of a particular Platform.  A Platform has the
    following:

    - id, a locally unique id
    - name, a URI, which must be a unique key
    - title, arbitrary text with xml:lang, optional
    - remark, arbitrary text with xml:lang, optional
    - definition ref, either a fact ref or a logical
    test (boolean combination of fact refs)
    """
    class Meta:
        namespace = "http://checklists.nist.gov/xccdf-p/1.1"

    title: List[TextType] = field(
        default_factory=list,
        metadata={
            "type": "Element",
        }
    )
    remark: List[TextType] = field(
        default_factory=list,
        metadata={
            "type": "Element",
        }
    )
    fact_ref: Optional[FactRefType] = field(
        default=None,
        metadata={
            "name": "fact-ref",
            "type": "Element",
        }
    )
    logical_test: Optional[LogicTestType] = field(
        default=None,
        metadata={
            "name": "logical-test",
            "type": "Element",
        }
    )
    name: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
        }
    )
    id: Optional[str] = field(
        default=None,
        metadata={
            "type": "Attribute",
            "required": True,
        }
    )

@dataclass
class PlatformSpecification:
    """This element can act as a top-level container for the Fact definitions
    and Platform definitions that make up a full XCCDF-P specification.  It
    should be used when a XCCDF-P spec is being distributed as a standalone
    document, or included in an XCCDF 1.1 specification.

    This element schema used to include a keyref for Fact names, but it
    has been removed to allow for pre-defined Fact dictionaries.
    """
    class Meta:
        name = "Platform-Specification"
        namespace = "http://checklists.nist.gov/xccdf-p/1.1"

    fact: List[Fact] = field(
        default_factory=list,
        metadata={
            "name": "Fact",
            "type": "Element",
        }
    )
    platform: List[Platform] = field(
        default_factory=list,
        metadata={
            "name": "Platform",
            "type": "Element",
        }
    )


import sys
import os
import glob
import csv
import string
import xlwt
import xlrd
import datetime
from xlutils.copy import copy
from optparse import OptionParser
from xml.dom import minidom

class XmlCfdi(object):

	cfdi_list = []

	def __init__(self, xml_file):
		""" Initialize instance. """
		self.xml_file = xml_file

	def get_cfdi_data(self, action):
		cfdi = {}
		xml = minidom.parse(self.xml_file)
		nodos = xml.childNodes
		comprobante = nodos[0]
		comp_atributos = dict(comprobante.attributes.items())

		xml_file = str(self.xml_file)
		if "/" in xml_file:
			xml_file = str(self.xml_file).split("/")[2]

		try:
			version = float(comp_atributos['version']) if 'version' in comp_atributos else float(comp_atributos['Version'])
			descuento = None
			subtotal = float(comp_atributos['subTotal']) if 'subTotal' in comp_atributos else float(comp_atributos['SubTotal'])
			total = float(comp_atributos['total']) if 'total' in comp_atributos else float(comp_atributos['Total'])

			if 'descuento' in comp_atributos:
				descuento = float(comp_atributos['descuento'])
			elif 'Descuento' in comp_atributos:
				descuento = float(comp_atributos['Descuento'])

			if 'serie' in comp_atributos:
				serie = comp_atributos['serie']
			elif 'Serie' in comp_atributos:
				serie = comp_atributos['Serie']
			else:
				serie = ''

			fecha = comp_atributos['fecha'] if 'fecha' in comp_atributos else comp_atributos['Fecha']
			if 'T' in fecha:
				fecha = fecha.split('T')[0]

			try:
				folio = comp_atributos['folio'] if 'folio' in comp_atributos else comp_atributos['Folio']
			except:
				print('El archivo %s no tiene folio' % xml_file)
				folio = 'sin folio'

			# Voy a calcular los impuestos
			total_iva = 0.0
			if version == 3.2:
				impuestos = comprobante.getElementsByTagName('cfdi:Impuestos')[0]
				try:
					if impuestos.hasAttribute('totalImpuestosTrasladados'):
						total_iva = float(impuestos.getAttribute('totalImpuestosTrasladados'))
					else:
						print('El comprobante %s no tiene IVA' % xml_file)
				except Exception as e:
					print('%s' % str(e))
			elif version == 3.3:
				impuestos = comprobante.getElementsByTagName('cfdi:Impuestos')
				try:
					for item in impuestos:
						if item.hasAttribute('TotalImpuestosTrasladados'):
							total_iva = float(item.getAttribute('TotalImpuestosTrasladados'))
				except Exception as e:
					print('%s' % str(e))

			if descuento:
				subtotal_sin_desc = round(subtotal - descuento, 2)
			else:
				subtotal_sin_desc = subtotal

			subtotal_iva = round(total_iva * 100 / 16, 2) # En esta linea calculo el subtotal en base al total del iva declarado
			diferencia_subs = round(subtotal_sin_desc - subtotal_iva, 2) # En esta linea calculo la diferencia entre el subtotal calculado y el declarado

			# Ahora compruebo si el subtotal_iva coincide con el subtotal declarado
			if diferencia_subs < .10:
				gastos16 = subtotal_sin_desc
				gastos0 = 0.0
			else:
				gastos16 = subtotal_iva
				gastos0 = subtotal_sin_desc - subtotal_iva

			# Obtengo los datos de la empresa con la que se trabaja, emisor o receptor dependiendo de la accion requerida
			if action == 'egresos' or action == 'acumulado-proveedores':
				tag_name_empresa = 'cfdi:Emisor'
				tag_domicilio = 'cfdi:DomicilioFiscal'
			elif action == 'ingresos' or action == 'acumulado-clientes':
				tag_name_empresa = 'cfdi:Receptor'
				tag_domicilio = 'cfdi:Domicilio'

			tag_empresa = comprobante.getElementsByTagName(tag_name_empresa)[0]
			empresa = tag_empresa.getAttribute('nombre') if tag_empresa.hasAttribute('nombre') else tag_empresa.getAttribute('Nombre')
			rfc = tag_empresa.getAttribute('rfc') if tag_empresa.hasAttribute('rfc') else tag_empresa.getAttribute('Rfc')

			calle = ''
			cp = ''
			municipio = ''
			estado = ''
			pais = ''
			try:
				tag_domicilio = comprobante.getElementsByTagName(tag_domicilio)[0]
				calle = tag_domicilio.getAttribute('calle')
				cp = tag_domicilio.getAttribute('codigoPostal')
				municipio = tag_domicilio.getAttribute('municipio')
				estado = tag_domicilio.getAttribute('estado')
				pais = tag_domicilio.getAttribute('pais')
			except:
				print('El archivo %s no tiene domicilio' % self.xml_file)
				pass

			no_exterior = ''
			colonia = ''
			localidad = ''
			try:
				no_exterior = tag_domicilio.getAttribute('noExterior')
				colonia = tag_domicilio.getAttribute('colonia')
				localidad = tag_domicilio.getAttribute('localidad')
			except:
				pass

			conceptos_list = []
			try:
				conceptos = comprobante.getElementsByTagName('cfdi:Concepto')
				for item in conceptos:
					conceptos_list.append(item.getAttribute('descripcion'))
			except:
				pass

			timbre_fiscal = comprobante.getElementsByTagName('tfd:TimbreFiscalDigital')[0]
			try:
				uuid = timbre_fiscal.getAttribute('UUID')
			except:
				print('El xml %s no tiene UUID, verificar que este correcto en el validador del SAT' % xml_file)
				uuid = None

			cfdi['serie'] = serie
			cfdi['fecha'] = fecha
			cfdi['descuento'] = descuento
			cfdi['subtotal'] = subtotal
			cfdi['total'] = total
			cfdi['file'] = xml_file
			cfdi['folio'] = folio
			cfdi['total_iva'] = total_iva
			cfdi['gastos16'] = gastos16
			cfdi['gastos0'] = gastos0
			cfdi['empresa'] = empresa
			cfdi['calle'] = calle
			cfdi['cp'] = cp
			cfdi['municipio'] = municipio
			cfdi['estado'] = estado
			cfdi['pais'] = pais
			cfdi['no_exterior'] = no_exterior
			cfdi['colonia'] = colonia
			cfdi['localidad'] = localidad
			cfdi['rfc'] = rfc
			cfdi['conceptos'] = conceptos_list
			cfdi['uuid'] = uuid

			if not self.is_duplicated(uuid, self.cfdi_list):
				self.cfdi_list.append(cfdi)
			else:
				duplicado = {'file': xml_file}
				print('El archivo %s esta duplicado, puedes borrarlo!' % xml_file)
		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('%s, %s, %s' % (exc_type, fname, exc_tb.tb_lineno))
			print('Error: %s' % str(e))
			print('El archivo %s tiene un error' % xml_file)
			print('----------------------------------------')

	def clean_acumulado(self, ws):
		row_initial = 3
		row_final = len(ws.rows)
		for row in range(row_initial, row_final):
			for cell in range(0, 20):
				ws.write(row, cell, '')

	def generate_cfdi_on_xls(self, action):
		cfdi_list = self.cfdi_list
		bk = book()
		date_format = xlwt.easyxf('', num_format_str='YYYY-MM-DD')
		currency_format = xlwt.easyxf('', '#,###.00')
		wb = bk.get_workbook()
		cfdi_list.sort(key=lambda k: k['fecha']) # Ordeno todos los diccionarios por fecha ascendente
		start = 4

		if action == 'acumulado-proveedores' or action == 'acumulado-clientes':
			empresas_dict = {}
			if action == 'acumulado-proveedores':
				ws = wb.get_sheet(2)
				leyenda_total = 'Gran total de Egresos'
			elif action == 'acumulado-clientes':
				ws = wb.get_sheet(3)
				leyenda_total = 'Gran total de Ingresos'
			self.clean_acumulado(ws)
			for i in range(0, len(cfdi_list)):
				empresa = cfdi_list[i]['rfc']
				if not empresa in empresas_dict:
					empresas_dict[empresa] = {
						1: 0.0,
						2: 0.0,
						3: 0.0,
						4: 0.0,
						5: 0.0,
						6: 0.0,
						7: 0.0,
						8: 0.0,
						9: 0.0,
						10: 0.0,
						11: 0.0,
						12: 0.0,
						'calle': cfdi_list[i]['calle'],
						'cp': cfdi_list[i]['cp'],
						'municipio': cfdi_list[i]['municipio'],
						'estado': cfdi_list[i]['estado'],
						'pais': cfdi_list[i]['pais'],
						'no_exterior': cfdi_list[i]['no_exterior'],
						'colonia': cfdi_list[i]['colonia'],
						'localidad': cfdi_list[i]['localidad'],
						'rfc': cfdi_list[i]['rfc'],
						'empresa': cfdi_list[i]['empresa']
					}

				fecha = cfdi_list[i]['fecha']
				mes = int(fecha.split('-')[1])
				empresas_dict[empresa][mes] += float(cfdi_list[i]['total'])

			row = start
			ind = 1
			for empresa, totales in sorted(empresas_dict.items()):
				ws.write(row, 0, ind)
				ws.write(row, 1, totales['empresa'])
				for i in range(1, 13):
					if totales[i] > 0.0:
						ws.write(row, i+1, totales[i], currency_format)
				totalxempresa = "SUM(C%i:N%i)" % (row+1, row+1)
				ws.write(row, 14, xlwt.Formula(totalxempresa), currency_format)
				ws.write(row, 15, totales['rfc'])
				ws.write(row, 16, totales['calle'] + ' ' + empresas_dict[empresa]['no_exterior'] + ' ' + empresas_dict[empresa]['colonia'] + ' ' + empresas_dict[empresa]['localidad'])
				ws.write(row, 17, totales['cp'])
				ws.write(row, 18, totales['municipio'])
				ws.write(row, 19, totales['estado'])
				row += 1
				ind += 1

			row += 1
			rango = list(string.ascii_uppercase)[2:15]
			ws.write(row, 1, leyenda_total)
			for i in range(1, 14):
				col = rango[i-1]
				totalxmes = "SUM(%s%i:%s%i)" % (col, start+1, col, row-1)
				ws.write(row, i+1, xlwt.Formula(totalxmes), currency_format)

		if action == 'egresos':
			ws = wb.get_sheet(1)
			last_row = len(ws.rows) + 1
			for i in range(0, len(cfdi_list)):
				ind = last_row + i
				ws.write(ind, 0, cfdi_list[i]['serie'] + cfdi_list[i]['folio'])
				ws.write(ind, 1, cfdi_list[i]['fecha'], date_format)
				ws.write(ind, 2, cfdi_list[i]['empresa'])
				ws.write(ind, 3, cfdi_list[i]['gastos16'], currency_format)
				ws.write(ind, 4, cfdi_list[i]['gastos0'], currency_format)
				ws.write(ind, 5, cfdi_list[i]['total_iva'], currency_format)
				ws.write(ind, 6, cfdi_list[i]['total'], currency_format)
				ws.write(ind, 7, cfdi_list[i]['file'])

		if action == 'ingresos':
			ws = wb.get_sheet(0)
			last_row = len(ws.rows) + 1
			for i in range(0, len(cfdi_list)):
				ind = last_row + i
				ws.write(ind, 0, cfdi_list[i]['empresa'])
				ws.write(ind, 1, cfdi_list[i]['rfc'])
				ws.write(ind, 2, cfdi_list[i]['fecha'], date_format)
				ws.write(ind, 3, cfdi_list[i]['serie'] + cfdi_list[i]['folio'])
				ws.write(ind, 4, cfdi_list[i]['conceptos'])
				ws.write(ind, 5, cfdi_list[i]['gastos16'], currency_format)
				ws.write(ind, 6, cfdi_list[i]['total_iva'], currency_format)
				ws.write(ind, 7, cfdi_list[i]['total'], currency_format)

		workbook_name = bk.get_workbook_name()
		os.remove(workbook_name) # Borro el libro del cual cree la copia, para poder guardar los cambios con el mismo nombre
		wb.save(workbook_name)
		print('Listo!')

	def is_duplicated(self, uuid, ls):
		for item in ls:
			if item['uuid'] == uuid:
				return True
		return False

class book():
	# Esta funcion devolvera una copia del libro contabilidad_ANIO_ACTUAL.xls o bien una copia del libro patron.xls con el nombre de contabilidad_ANIO_ACTUAL.xls
	def get_workbook(self):
		path = self.get_workbook_name()
		if os.path.isfile(path):
			wb = xlrd.open_workbook(path, formatting_info=True)
			workbook = copy(wb)
		else:
			patron = xlrd.open_workbook('patron.xls', formatting_info=True)
			workbook = copy(patron)
			workbook.save(path)
		return workbook

	def get_workbook_name(self):
		hoy = datetime.date.today()
		year = hoy.year
		path = 'contabilidad_%i.xls' % year
		return path

def main(argv):

	usage = "%prog [opciones] archivocfd.xml|*.xml"
	# usage = "%prog [opciones] archivocfd.xml|*.xml"
	add_help_option = False
	parser = OptionParser(usage=usage, add_help_option=add_help_option)

	parser.add_option("-e", "--egresos", dest="generaEgresos",
		help=u"Genera la lista de egresos del mes que se pasa como argumento")

	parser.add_option("-h", "--help", action="help",
		help=u"Ejecutar generate_cfdi_xls.py con archivo_cfdi.xml accion o folder_mes/*.xml accion como parametro")

	(options, args) = parser.parse_args()

	actions = ['egresos', 'ingresos', 'acumulado-proveedores', 'acumulado-clientes']

	if len(args) <= 1:
	    parser.print_help()
	    sys.exit(0)

	action = args[0]

	if not action in actions:
		print('Debes pasar una de las siguientes acciones %s' % actions)
		print('Pasaste %s' % action)
		sys.exit(0)

	# Se obtiene la lista de archivos
	if len(args) == 1 and "*" not in args[1:2]:
		files = args
	elif len(args) == 1 and "*" in args[1:2]:
		files = glob.glob(args[1:2])
	else:
		files = args[1:]
		print('Archivos XML a procesar: %i' % len(files))

	for item in files:
		xml_file = item
		if not os.path.isfile(xml_file):
			print("El archivo " + xml_file + " no existe.")
		else:
			xmlcfdi = XmlCfdi(xml_file)
			xmlcfdi.get_cfdi_data(action)

	print('Generando %s ...' % action)
	xmlcfdi.generate_cfdi_on_xls(action)

if __name__ == "__main__":
	main(sys.argv[1:])


#!/usr/bin/env python3
import pigpio # Library for getting temps and humdity from sensor
import DHT22
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
from time import sleep
from datetime import datetime

cred = credentials.Certificate('/home/pi/SmartWeather/Smart Weather-831fcc7de0e7.json')
default_app = firebase_admin.initialize_app(cred, {
    'databaseURL' : 'https://smart-weather-58527.firebaseio.com/'})

# this connects to the pigpio daemon which must be started first
pi = pigpio.pi()
# Pigpio DHT22 module should be in same folder as your program
s = DHT22.sensor(pi, 4)

root = db.reference()
while(True):
    now = datetime.now()
    #currentDate = root.child((str(now.month) + "-" + str(now.day) + "-" + str(now.year)))
    currentDate = root.child(now.strftime('%Y-%m-%d'))
    temps = currentDate.child('temps')
    humiditys = currentDate.child('humidity')
    url = currentDate.child('imgURL')
    checkURL = url.get(etag = False)
    if(checkURL == None):
        currentDate.update({'imgURL': 'nothing'})
    s.trigger()	 # reads in new temp and humidity
    sleep(.5)	 # the trigger() call needs time before values are taken from 's'
    humidity = s.humidity()
    temp = s.temperature() * (9/5) + 32

    print(now.strftime('%H:%M:%S'))
    temps.update({now.strftime('%H:%M:%S'): round(temp,2)})
    humiditys.update({now.strftime('%H:%M:%S'): round(humidity,2)})

    print("Time\t" + str(now.time()))
    print("Humidity:\t"+str(round(humidity,2))+"%")
    print("Temperature:\t"+str(round(temp,2))+" F")

    sleep(600)


from nfstream import NFPlugin
import dpkt

class MDNS(NFPlugin):
    """ MDNS plugin

    This plugin extracts answer information from MDNS requests. The following information are retrieved:

    - mdns_ptr: An ordered list of PTR answsers.

    """
    def on_init(self, packet, flow):
        flow.udps.mdns_ptr = []
        self.on_update(packet, flow)

    def on_update(self, packet, flow):
        if flow.dst_port == 5353:
            try:
                ip = dpkt.ip.IP(packet.ip_packet)
                udp = ip.data
                dns = dpkt.dns.DNS(udp.data)
            except (dpkt.NeedData, dpkt.UnpackError):
                return

            if len(dns.an) > 0:
                for answer in dns.an:
                    if answer.type == 12:  # PTR
                        ptr = answer.ptrname.replace(',', ' ')
                        if ptr not in flow.udps.mdns_ptr:
                            flow.udps.mdns_ptr.append(ptr)


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('auth', '0001_initial'),
        ('cegroups', '0012_auto_20141007_1557'),
    ]

    operations = [
        migrations.AddField(
            model_name='grouppermissions',
            name='groupId',
            field=models.ForeignKey(related_name=b'+', to='auth.Group', null=True),
            preserve_default=True,
        ),
    ]


import smbus
import time

class MPU6050:
    def __init__(self, bus_num=1, address=0x68):
        self.bus = smbus.SMBus(bus_num)
        self.address = address

        # Registros de configuración del MPU6050
        self.PWR_MGMT_1 = 0x6B
        self.ACCEL_CONFIG = 0x1C
        self.GYRO_CONFIG = 0x1B

        # Escala de la aceleración y el giroscopio
        self.ACCEL_SCALE = 16384.0
        self.GYRO_SCALE = 131.0

        # Configura el MPU6050 para medir la aceleración y la velocidad angular
        self.bus.write_byte_data(self.address, self.PWR_MGMT_1, 0)
        self.bus.write_byte_data(self.address, self.ACCEL_CONFIG, 0x00)
        self.bus.write_byte_data(self.address, self.GYRO_CONFIG, 0x00)

    def read_word_2c(self, address):
        high = self.bus.read_byte_data(self.address, address)
        low = self.bus.read_byte_data(self.address, address + 1)
        value = (high << 8) + low
        if (value >= 0x8000):
            return -((65535 - value) + 1)
        else:
            return value

    def read_acceleration(self):
        accel_x = self.read_word_2c(0x3B) / self.ACCEL_SCALE
        accel_y = self.read_word_2c(0x3D) / self.ACCEL_SCALE
        accel_z = self.read_word_2c(0x3F) / self.ACCEL_SCALE
        return (accel_x * 9.81, accel_y * 9.81, accel_z * 9.81)

    def read_gyro(self):
        gyro_x = self.read_word_2c(0x43) / self.GYRO_SCALE
        gyro_y = self.read_word_2c(0x45) / self.GYRO_SCALE
        gyro_z = self.read_word_2c(0x47) / self.GYRO_SCALE
        return (gyro_x, gyro_y, gyro_z)

if __name__ == '__main__':
    mpu = MPU6050()

    while True:
        accel_data = mpu.read_acceleration()
        gyro_data = mpu.read_gyro()
        print('Aceleracion (m/s^2): X={0:.2f}, Y={1:.2f}, Z={2:.2f}'.format(*accel_data))
        print('Giroscopio (grados/s): X={0:.2f}, Y={1:.2f}, Z={2:.2f}'.format(*gyro_data))
        time.sleep(1)


"""
Using Dask for single-machine parallel computing
================================================

This example shows the simplest usage of the
`Dask <https://docs.dask.org/en/stable/>`_
backend on your local machine.

This is useful for prototyping a solution, to later be run on a truly
`distributed Dask cluster
<https://docs.dask.org/en/stable/deploying.html#distributed-computing>`_,
as the only change needed is the cluster class.

Another realistic usage scenario: combining dask code with joblib code,
for instance using dask for preprocessing data, and scikit-learn for
machine learning. In such a setting, it may be interesting to use
distributed as a backend scheduler for both dask and joblib, to
orchestrate the computation.

"""

###############################################################################
# Setup the distributed client
###############################################################################
from dask.distributed import Client, LocalCluster

# replace with whichever cluster class you're using
# https://docs.dask.org/en/stable/deploying.html#distributed-computing
cluster = LocalCluster()
# connect client to your cluster
client = Client(cluster)

# Monitor your computation with the Dask dashboard
print(client.dashboard_link)

###############################################################################
# Run parallel computation using dask.distributed
###############################################################################

import time
import joblib

def long_running_function(i):
    time.sleep(0.1)
    return i

###############################################################################
# The verbose messages below show that the backend is indeed the
# dask.distributed one
with joblib.parallel_config(backend="dask"):
    joblib.Parallel(verbose=100)(
        joblib.delayed(long_running_function)(i) for i in range(10)
    )

###############################################################################


#!/usr/bin/python3
'''
Module for matrix_divided method
it divides all elements of a matrix
returns the new matrix
'''

def matrix_divided(matrix, div):
    """Divide each element in a matrix by a divisor.
    matrix [[]]: a list of lists of float or int.
    div (int): non-zero integer divisor.

    Returns a new matrix.
    See tests/2-matrix_divided.txt for test cases.
    """

    if not isinstance(div, int) and not isinstance(div, float):
        raise TypeError('div must be a number')
    if not isinstance(matrix, list) or len(matrix) == 0:
        raise TypeError('matrix must be a matrix (list of lists)' +
                        ' of integers/floats')
    for inner in matrix:
        if not isinstance(inner, list) or len(inner) == 0:
            raise TypeError('matrix must be a matrix (list of lists)' +
                            ' of integers/floats')
        if len(inner) != len(matrix[0]):
            raise TypeError('Each row of the matrix must have the same size')
        for item in inner:
            if not isinstance(item, (int, float)):
                raise TypeError('matrix must be a matrix (list of lists)' +
                                ' of integers/floats')
    return [[round(item / div, 2) for item in inner] for inner in matrix]


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

# In[10]:

result = 1
for i in range(1,11):
    result *=i
print(result)
#for문을 이용해 변수와 범위를 정하고 1부터 10까지의 곱을 구한다.

# In[ ]:


"""
Written by: Christopher Hutchings | M00571702
Last edit: 25/02/21
Module: CST4550
---
The first and main tool, scans for devices on the network and attempts to bannergrab them, producing a report at the end.
"""

#Dictionary Imports
from pprint import pprint
from colorama import Fore, Back, Style
from pyfiglet import Figlet
import PySimpleGUI as sg
import tkinter as tk

#Process imports
import os
import subprocess
import time
import socket

import ipaddress
import nmap
import scapy.all as scapy
import progressbar

#Module imports
from . import Menu as menu
from . import Database as db

#Preset OS functions
clear = lambda: os.system('clear')

# Target ports
portList = [21, 22, 25, 80, 443, 139, 445]

def main(ip, SubIP):
	clear()
	###########################
	#Fancy looking CMD screen
	print('\033[1m')
	print(Fore.YELLOW + '-----------------------------------------------------------------------------------------------') # May the gods forgive me for this part.
	print(Fore.RED + '				NETWORK VULNERABILITY SCANNER')
	print(Fore.YELLOW + '-----------------------------------------------------------------------------------------------') # But it looks cool.

	###########################
	#Socket preferences
	socket.setdefaulttimeout(5)
	###########################v

	#Packet preferences
	###########################
	arp_r = scapy.ARP(pdst='10.0.2.0/24') # Target network
	br = scapy.Ether(dst='ff:ff:ff:ff:ff:ff') # Default MAC Broadcast
	###########################

	#Packet deployment & Data Recollection
	###########################
	request = br/arp_r
	answered, unanswered = scapy.srp(request, timeout=1, verbose = False)
	###########################

	#Visual loading bar setup for user readability
	###########################
	print(Fore.YELLOW)
	err = 0
	step = 0
	steps = 0
	for i in answered:
		steps = steps + 1
	steps = steps * len(portList)
	print("Collecing Network Devices...")
	print("Devices detected: " + str(len(answered)))
	time.sleep(.5)
	print("\nCollecting Device Ports...")
	print("Ports targetted: " + str(steps))
	print("")
	bar = progressbar.ProgressBar(max_value=steps)
	time.sleep(1.5)
	###########################

	exploits = db.create_connection()

	VulnerablePorts = []
	#Initialise device & banner scanning
	###########################
	print(Fore.RED)
	print("Probing ports...")
	for i in answered:
		ip, mac = i[1].psrc, i[1].hwsrc
		data = [ip]
		taggedexploits = []
		for port in portList:
			step = step + 1
			bar.update(step)
			try:
				s = socket.socket()
				s.connect((ip, port))
				gbanner = s.recv(1024)
				s.close()

				## String splitting
				banner = str(gbanner)
				banner = banner[2:]
				banner = banner[:-3]
				banner.replace(' ', '')
				print(banner)
				## Add Vuln data to Data, if it's vulnerable!
				appending = False
				for exploit in exploits:
					if str(exploit[0]) == banner:
						taggedexploits.append([exploit[0], exploit[1], exploit[2]])
						data.append(taggedexploits)

				if len(taggedexploits) > 0:
					VulnerablePorts.append(data)
				##
				time.sleep(.1)
				taggedexploits = []
			except:
				err + 1
				time.sleep(.1)
				#print(str(port) + ': CLOSED / BLOCKED') # Odd issues with P139, 445.
	print(Fore.YELLOW + '-----------------------------------------------------------------------')
	#psrint('\nDEBUG: Detected Vulnerabilities;')
	#print(VulnerablePorts)
	#Run the vulnerability report.
	###########################
	vulnerability_report(VulnerablePorts)

def vulnerability_report(Data):
	#Fancy stuff.
	###########################
	print(Fore.GREEN)
	print('Banner Scanning Complete...')
	print('Generating vulnerability report...')
	time.sleep(1)
	x = 380
	y = 0
	for i in Data:
		y = y + 150
	# Initiate Main Menu
	root = tk.Tk(className = 'vulnerability report | Network Scanner')
	root.geometry(str(x) + "x" + str(y))
	setRow = 1
	setCol = 1
	for device in Data:
		setCol = 1
		tk.Label(root, text=device[0], borderwidth=0 ).grid(row=setRow,column=setCol)
		setRow = setRow + 1
		for vuns in device[1]:
			tk.Label(root, text="Vulnerability: " + vuns[0], borderwidth=0 ).grid(row=setRow,column=setCol)
			setRow = setRow + 1
			tk.Label(root, text="Type: " + vuns[1], borderwidth=0 ).grid(row=setRow,column=setCol)
			setRow = setRow + 1
			tk.Label(root, text="Exploit: " + vuns[2], borderwidth=0 ).grid(row=setRow,column=setCol)
			setRow = setRow + 1
			tk.Label(root, text='-------------', borderwidth=0 ).grid(row=setRow,column=setCol)
			setRow = setRow + 1
			tk.Button(root, text="RUN EXPLOIT", command = lambda: runExploit(device[0], vuns[2])).grid(row=setRow, column=setCol)
			setRow = setRow + 1
		tk.Label(root, text='-------------------------------------------------------------------------', borderwidth=0 ).grid(row=setRow,column=setCol)
		setRow = setRow + 1
		tk.Label(root, text='-------------------------------------------------------------------------', borderwidth=0 ).grid(row=setRow,column=setCol)
		setRow = setRow + 1

	root.mainloop()
	root.quit()
	menu.menu()

def runExploit(ip, exploit):
	subprocess.Popen(['gnome-terminal', 'executeExploit.sh test'], cwd='.', shell=False)

	#Return the user to the menu
	###########################


from network import average as network_average
from network import Network
import mathFunctions as mf

from copy import deepcopy
import warnings
import numpy as npy
import pylab as plb

class NetworkSet(object):
	'''
	A set of Networks.

	This class allows functions on sets of Networks, such as mean or
	standard deviation, to be calculated conveniently. The results are
	returned in :class:`~mwavepy.network.Network` objects, so that they may be
	plotted and saved in like :class:`~mwavepy.network.Network` objects.

	This class also provides methods which can be used to plot uncertainty
	bounds for a set of :class:`~mwavepy.network.Network`.

	The names of the :class:`NetworkSet` properties are generated
	dynamically upon ititialization, and thus documentation for
	individual properties and methods is not available. However, the
	properties do follow the convention::

		>>> my_network_set.function_name_network_property_name

	For example, the complex average (mean)
	:class:`~mwavepy.network.Network` for a
	:class:`NetworkSet` is::

		>>> my_network_set.mean_s

	This accesses the  property 's', for each element in the
	set, and **then** calculates the 'mean' of the resultant set. The
	order of operations is important.

	Results are returned as :class:`~mwavepy.network.Network` objects,
	so they may be plotted or saved in the same way as for
	:class:`~mwavepy.network.Network` objects::

		>>> my_network_set.mean_s.plot_s_mag()
		>>> my_network_set.mean_s.write_touchstone('mean_response')

	If you are calculating functions that return scalar variables, then
	the result is accessable through the Network property .s_re. For
	example::

		>>> std_s_deg = my_network_set.std_s_deg

	This result would be plotted by::

		>>> std_s_deg.plot_s_re()

	The operators, properties, and methods of NetworkSet object are
	dynamically generated by private methods

	 * :func:`~NetworkSet.__add_a_operator`
	 * :func:`~NetworkSet.__add_a_func_on_property`
	 * :func:`~NetworkSet.__add_a_element_wise_method`
	 * :func:`~NetworkSet.__add_a_plot_uncertainty`

	thus, documentation on the individual methods and properties are
	not available.

	'''

	def __init__(self, ntwk_set):
		'''
		Initializer for NetworkSet
		Parameters
		-----------
		ntwk_set : list of :class:`~mwavepy.network.Network` objects
			the set of :class:`~mwavepy.network.Network` objects

		'''
		## type checking
		# did they pass a list of Networks?
		if not isinstance(ntwk_set[0], Network):
			raise(TypeError('input must be list of Network types'))

		# do all Networks have the same # ports?
		if len (set([ntwk.number_of_ports for ntwk in ntwk_set])) >1:
			raise(ValueError('All elements in list of Networks must have same number of ports'))

		# is all frequency information the same?
		if npy.all([(ntwk_set[0].frequency == ntwk.frequency) \
			for ntwk in ntwk_set]) == False:
			raise(ValueError('All elements in list of Networks must have same frequency information'))

		## initialization
		# we are good to go
		self.ntwk_set = ntwk_set

		# dynamically generate properties. this is slick.
		for network_property_name in \
			['s','s_re','s_im','s_mag','s_deg','s_deg_unwrap','s_rad',\
			's_rad_unwrap','s_arcl','s_arcl_unwrap','passivity']:
			for func in [npy.mean, npy.std]:
				self.__add_a_func_on_property(func, network_property_name)

			self.__add_a_plot_uncertainty(network_property_name)
			self.__add_a_element_wise_method('plot_'+network_property_name)

		for network_method_name in \
			['write_touchstone','interpolate','plot_s_smith']:
			self.__add_a_element_wise_method(network_method_name)

		for operator_name in \
			['__pow__','__floordiv__','__mul__','__div__','__add__','__sub__']:
			self.__add_a_operator(operator_name)

	def __add_a_operator(self,operator_name):
		'''
		adds a operator method to the NetworkSet.

		this is made to
		take either a Network or a NetworkSet. if a Network is passed
		to the operator, each element of the set will operate on the
		Network. If a NetworkSet is passed to the operator, and is the
		same length as self. then it will operate element-to-element
		like a dot-product.
		'''
		def operator_func(self, other):
			if isinstance(other, NetworkSet):
				if len(other) != len(self):
					raise(ValueError('Network sets must be of same length to be casacaded'))
				return NetworkSet([self.ntwk_set[k].__getattribute__(operator_name)(other.ntwk_set[k]) for k in range(len(self))])

			elif isinstance(other, Network):
				return NetworkSet([ntwk.__getattribute__(operator_name)(other) for ntwk in self.ntwk_set])

			else:
				raise(TypeError('NetworkSet operators operate on either Network, or NetworkSet types'))
		setattr(self.__class__,operator_name,operator_func)

	def __str__(self):
		'''
		'''
		output =  \
			'A NetworkSet of length %i'%len(self.ntwk_set)

		return output

	def __repr__(self):
		return self.__str__()

	def __getitem__(self,key):
		'''
		returns an element of the network set
		'''
		return self.ntwk_set[key]

	def __len__(self):
		'''
		returns an element of the network set
		'''
		return len(self.ntwk_set)

	def __add_a_element_wise_method(self,network_method_name):
		 def func(self,  *args, **kwargs):
			 return self.element_wise_method(network_method_name, *args, **kwargs)
		 setattr(self.__class__,network_method_name,func)

	def __add_a_func_on_property(self,func,network_property_name):
		'''
		dynamically adds a property to this class (NetworkSet).
		this is mostly used internally to genrate all of the classes
		properties.

		takes:
			network_property_name: a property of the Network class,
				a string. this must have a matrix output of shape fxnxn
			func: a function to be applied to the network_property
				accross the first axis of the property's output

		example:
			my_ntwk_set.add_a_func_on_property('s',mean)

		'''
		fget = lambda self: fon(self.ntwk_set,func,network_property_name)
		setattr(self.__class__,func.__name__+'_'+network_property_name,\
			property(fget))

	def __add_a_plot_uncertainty(self,network_property_name):
		'''

		takes:
			network_property_name: a property of the Network class,
				a string. this must have a matrix output of shape fxnxn

		example:
			my_ntwk_set.add_a_func_on_property('s',mean)

		'''
		def plot_func(self,*args, **kwargs):
			kwargs.update({'attribute':network_property_name})
			self.plot_uncertainty_bounds_component(*args,**kwargs)

		setattr(self.__class__,'plot_uncertainty_bounds_'+\
			network_property_name,plot_func)

	def element_wise_method(self,network_method_name, *args, **kwargs):
		'''
		calls a given method of each element and returns the result as
		a new NetworkSet if the output is a Network.
		'''
		output = [ntwk.__getattribute__(network_method_name)(*args, **kwargs) for ntwk in self.ntwk_set]
		if isinstance(output[0],Network):
			return NetworkSet(output)
		else:
			return output

	@property
	def mean_s_db(self):
		'''
		the mean magnitude in dB.

		note:
			the mean is taken on the magnitude before convertedto db, so
				magnitude_2_db( mean(s_mag))
			which is NOT the same as
				mean(s_db)
		'''
		ntwk= self.mean_s_mag
		ntwk.s = ntwk.s_db
		return ntwk

	@property
	def std_s_db(self):
		'''
		the mean magnitude in dB.

		note:
			the mean is taken on the magnitude before convertedto db, so
				magnitude_2_db( mean(s_mag))
			which is NOT the same as
				mean(s_db)
		'''
		ntwk= self.std_s_mag
		ntwk.s = ntwk.s_db
		return ntwk

	@property
	def inv(self):
		return NetworkSet( [ntwk.inv for ntwk in self.ntwk_set])

	def set_wise_function(self, func, a_property, *args, **kwargs):
		'''
		calls a function on a specific property of the networks in
		this NetworkSet.

		example:
			my_ntwk_set.set_wise_func(mean,'s')
		'''
		return fon(self.ntwk_set, func, a_property, *args, **kwargs)

	# plotting functions
	#def plot_uncertainty_bounds(self,attribute='s_mag',m=0,n=0,\
		#n_deviations=3, alpha=.3,fill_color ='b',std_attribute=None,*args,**kwargs):
		#'''
		#plots mean value with +- uncertainty bounds in an Network attribute,
		#for a list of Networks.

		#takes:
			#attribute: attribute of Network type to analyze [string]
			#m: first index of attribute matrix [int]
			#n: second index of attribute matrix [int]
			#n_deviations: number of std deviations to plot as bounds [number]
			#alpha: passed to matplotlib.fill_between() command. [number, 0-1]
			#*args,**kwargs: passed to Network.plot_'attribute' command

		#returns:
			#None

		#Caution:
			 #if your list_of_networks is for a calibrated short, then the
			#std dev of deg_unwrap might blow up, because even though each
			#network is unwrapped, they may fall on either side fo the pi
			#relative to one another.
		#'''

		## calculate mean response, and std dev of given attribute
		#ntwk_mean = average(self.ntwk_set)
		#if std_attribute is None:
			## they want to calculate teh std deviation on a different attribute
			#std_attribute = attribute
		#ntwk_std = func_on_networks(self.ntwk_set,npy.std, attribute=std_attribute)

		## pull out port of interest
		#ntwk_mean.s = ntwk_mean.s[:,m,n]
		#ntwk_std.s = ntwk_std.s[:,m,n]

		## create bounds (the s_mag here is confusing but is realy in units
		## of whatever 'attribute' is. read the func_on_networks call to understand
		#upper_bound =  ntwk_mean.__getattribute__(attribute) +\
			#ntwk_std.s_mag*n_deviations
		#lower_bound =   ntwk_mean.__getattribute__(attribute) -\
			#ntwk_std.s_mag*n_deviations

		## find the correct ploting method
		#plot_func = ntwk_mean.__getattribute__('plot_'+attribute)

		##plot mean response
		#plot_func(*args,**kwargs)

		##plot bounds
		#plb.fill_between(ntwk_mean.frequency.f_scaled, \
			#lower_bound.squeeze(),upper_bound.squeeze(), alpha=alpha, color=fill_color)
		#plb.axis('tight')
		#plb.draw()

	def uncertainty_ntwk_triplet(self, attribute,n_deviations=3):
		'''
		returns a 3-tuple of Network objects which contain the
		mean, upper_bound, and lower_bound for the given Network
		attribute.

		Used to save and plot uncertainty information data
		'''
		ntwk_mean = self.__getattribute__('mean_'+attribute)
		ntwk_std = self.__getattribute__('std_'+attribute)
		ntwk_std.s = n_deviations * ntwk_std.s

		upper_bound = (ntwk_mean +ntwk_std)
		lower_bound = (ntwk_mean -ntwk_std)

		return (ntwk_mean, lower_bound, upper_bound)

	def plot_uncertainty_bounds_component(self,attribute,m=0,n=0,\
		type='shade',n_deviations=3, alpha=.3, color_error ='b',markevery_error=20,
		ax=None,ppf=None,kwargs_error={},*args,**kwargs):
		'''
		plots mean value of the NetworkSet with +- uncertainty bounds
		in an Network's attribute. This is designed to represent
		uncertainty in a scalar component of the s-parameter. for example
		ploting the uncertainty in the magnitude would be expressed by,

			mean(abs(s)) +- std(abs(s))

		the order of mean and abs is important.

		takes:
			attribute: attribute of Network type to analyze [string]
			m: first index of attribute matrix [int]
			n: second index of attribute matrix [int]
			type: ['shade' | 'bar'], type of plot to draw
			n_deviations: number of std deviations to plot as bounds [number]
			alpha: passed to matplotlib.fill_between() command. [number, 0-1]
			color_error: color of the +- std dev fill shading
			markevery_error: if type=='bar', this controls frequency
				of error bars
			ax: Axes to plot on
			ppf: post processing function. a function applied to the
				upper and low
			*args,**kwargs: passed to Network.plot_s_re command used
				to plot mean response
			kwargs_error: dictionary of kwargs to pass to the fill_between
				or errorbar plot command depending on value of type.

		returns:
			None

		Note:
			for phase uncertainty you probably want s_deg_unwrap, or
			similar.  uncerainty for wrapped phase blows up at +-pi.

		'''
		ylabel_dict = {'s_mag':'Magnitude','s_deg':'Phase [deg]',
			's_deg_unwrap':'Phase [deg]','s_deg_unwrapped':'Phase [deg]',
			's_db':'Magnitude [dB]'}

		ax = plb.gca()

		ntwk_mean = self.__getattribute__('mean_'+attribute)
		ntwk_std = self.__getattribute__('std_'+attribute)
		ntwk_std.s = n_deviations * ntwk_std.s

		upper_bound = (ntwk_mean.s[:,m,n] +ntwk_std.s[:,m,n]).squeeze()
		lower_bound = (ntwk_mean.s[:,m,n] -ntwk_std.s[:,m,n]).squeeze()

		if ppf is not None:
			if type =='bar':
				warnings.warn('the \'ppf\' options doesnt work correctly with the bar-type error plots')
			ntwk_mean.s = ppf(ntwk_mean.s)
			upper_bound = ppf(upper_bound)
			lower_bound = ppf(lower_bound)
			lower_bound[npy.isnan(lower_bound)]=min(lower_bound)

		if type == 'shade':
			ntwk_mean.plot_s_re(ax=ax,m=m,n=n,*args, **kwargs)
			ax.fill_between(ntwk_mean.frequency.f_scaled, \
				lower_bound,upper_bound, alpha=alpha, color=color_error,
				**kwargs_error)
			#ax.plot(ntwk_mean.frequency.f_scaled,ntwk_mean.s[:,m,n],*args,**kwargs)
		elif type =='bar':
			ntwk_mean.plot_s_re(ax=ax,m=m,n=n,*args, **kwargs)
			ax.errorbar(ntwk_mean.frequency.f_scaled[::markevery_error],\
				ntwk_mean.s_re[:,m,n].squeeze()[::markevery_error], \
				yerr=ntwk_std.s_mag[:,m,n].squeeze()[::markevery_error],\
				color=color_error,**kwargs_error)

		else:
			raise(ValueError('incorrect plot type'))

		ax.set_ylabel(ylabel_dict.get(attribute,''))

	def plot_uncertainty_bounds_s_db(self,*args, **kwargs):
		'''
		this just calls
			plot_uncertainty_bounds(attribute= 's_mag',*args,**kwargs)
		see plot_uncertainty_bounds for help

		'''
		kwargs.update({'attribute':'s_mag','ppf':mf.magnitude_2_db})
		self.plot_uncertainty_bounds_component(*args,**kwargs)

def plot_uncertainty_bounds_s_db(ntwk_list, *args, **kwargs):
	NetworkSet(ntwk_list).plot_uncertainty_bounds_s_db(*args, **kwargs)

def func_on_networks(ntwk_list, func, attribute='s',*args, **kwargs):
	'''
	Applies a function to some attribute of aa list of networks, and
	returns the result in the form of a Network. This means information
	that may not be s-parameters is stored in the s-matrix of the
	returned Network.

	takes:
		ntwk_list: list of mwavepy.Network types
		func: function to operate on ntwk_list s-matrices
		attribute: attribute of Network's  in ntwk_list for func to act on
		*args: passed to func
		**kwargs: passed to func

	returns:
		mwavepy.Network type, with s-matrix the result of func,
			operating on ntwk_list's s-matrices

	example:
		averaging can be implemented with func_on_networks by
			func_on_networks(ntwk_list,mean)
	'''
	data_matrix = \
		npy.array([ntwk.__getattribute__(attribute) for ntwk in ntwk_list])

	new_ntwk = deepcopy(ntwk_list[0])
	new_ntwk.s = func(data_matrix,axis=0,*args,**kwargs)
	return new_ntwk

# short hand name for convenience
fon = func_on_networks