import math
def findLargest(num, limit):
    for m in range(limit, 0, -1):
        if num>= m*m: return m
    return 1

def solution(area):
    upper_limit = 1000
    out = []
    while area>0:
        n = findLargest(area,upper_limit)
        num = n*n
        out.append(num)
        area-= num
        upper_limit = n
    return out

###############################################
# Code below is not submitted
import unittest

class sTest(unittest.TestCase):

    def test_function(self):
        test_cases = \
            [
                (15324, [15129,169,25,1]),\
                (12, [9,1,1,1]),\
                (1, [1]),\
                (4, [4]),\
                (5, [4,1]),\
                (85, [81, 4]),\
            ]
        for arguments, expected in test_cases:
            with self.subTest(arguments=arguments, expected=expected):
                self.assertEqual(solution(arguments), expected)

if __name__ == '__main__':
    unittest.main()


from __future__ import annotations
import io
import datetime

import asyncpg
import yarl
import re
import pprint
import tabulate
from typing import Optional, Union, cast, TYPE_CHECKING

import aiohttp
import discord

from discord.ext import commands

from utils import paginator, errors
from utils.context import Context

if TYPE_CHECKING:
    from bot import Bot

URL_RE = re.compile(r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*(),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+")

SPECIAL_CHANNEL_INDEX = {
    669115391880069150: "twitchio",
    491048464831086592: "twitchio",
    531269424523771934: "wavelink",
    739788459006492752: "wavelink",
    491048383578898441: "discord.py"
}

class _libconverter(commands.Converter):
    async def convert(self, ctx: Context, param):
        if param.lower() in {"dpy", "discordpy", "discord.py"}:
            return "discord.py-2"
        elif param.lower() in {"dpy2", "discord.py2", "discordpy2", "2.0", "master"}:
            return "discord.py-2"
        elif param.lower() in {"tio", "twitch", "twitchio"}:
            return "twitchio"
        elif param.lower() in {"wl", "wave", "link", "wavelink"}:
            return "wavelink"
        elif param.lower() in {"ahttp", "aiohttp"}:
            return "aiohttp"
        elif param.lower() in {"enhanced-dpy", "enhanced-discord.py", "edpy"}:
            return "enhanced-discord.py"
        else:
            raise commands.UserInputError("Must be one of discord.py, discord.py2, enhanced-dpy, twitchio, wavelink, or aiohttp")

async def setup(bot):
    await bot.add_cog(Idevision(bot))

v = {
    "link1": "https://github.com/",
    "link1_name": "Github",
    "link2": "https://metrics.idevision.net",
    "link2_name": "Idevision",
    "link3": "https://twitch.tv",
    "link3_name": "Twitch",
    "link4": "https://youtube.com",
    "link4_name": "Youtube"
}

class Idevision(commands.Cog):
    url = "https://idevision.net/api/"

    def __init__(self, bot: Bot):
        self.bot = bot
        self.defaults = {}
        self.pages = {}
        self.usage = {}
        self.session = aiohttp.ClientSession(headers={"Authorization": bot.config['idevision']['token'], "User-Agent": "MiniBOB"})
        self._hook_session = aiohttp.ClientSession()
        self.hook: discord.Webhook = discord.Webhook.from_url(bot.config['idevision']['webhook_url'], session=self._hook_session) # noqa

    async def cog_load(self) -> None:
        self.db: asyncpg.Pool = await asyncpg.create_pool(self.bot.config["db"]["dsn"])

    async def cog_unload(self):
        await self._unload()

    async def _unload(self):
        await self.db.close()
        await self.session.close()
        await self._hook_session.close()

    async def do_rtfm(self, ctx, key, obj, labels=True, obvious_labels=False):
        if ctx.guild is not None and ctx.guild.id in self.defaults and key is None:
            key = self.defaults[ctx.guild.id]

        target = self.pages[key]['url']

        if obj is None:
            return await ctx.send(target)

        url = yarl.URL(self.url + "public/rtfm.sphinx").with_query(
            {
                "query": obj,
                "location": target,
                "show-labels": str(labels),
                "label-labels": str(obvious_labels)
            }
        )

        headers = {"User-Agent": f"BOB discord bot (squawking {ctx.author})"}
        async with self.session.get(url, headers=headers) as resp:
            if resp.status != 200:
                return await ctx.send(f"The api returned an irregular status ({resp.status}) ({await resp.text()})")

            matches = await resp.json()
            if not matches['nodes']:
                return await ctx.send("Could not find anything. Sorry.")

        e = discord.Embed(colour=0x36393E)
        e.title = f"{self.pages[key]['long']}: {obj}"
        e.description = '\n'.join(f'[`{key}`]({url})' for key, url in matches['nodes'].items())
        e.url = self.url.replace("api/", "docs")
        e.set_footer(text=f"rtfm api available at {e.url}")
        await ctx.send(embed=e)

    @commands.group(aliases=['rtfd', "rtm", "rtd"], invoke_without_command=True, help="read the fucking docs. see `!help rtfm`")
    async def rtfm(self, ctx: Context, *, obj: Optional[str] = None):
        """
        view the documentation of the modules available in `rtfm list`.
        use their *quick* name to access it in the rtfm command, as such:
        `rtfm py sys`

        By default, labels are not shown in the results. To enable labels, use the `--labels` flag
        """
        await ctx.typing()

        labels = False
        obvious_labels = False
        if obj is not None:
            if "--labels" in obj:
                labels = True
                obvious_labels = True
                obj = obj.replace("--labels", "")

            from discord.ext.commands.view import StringView
            view = StringView(obj)
            key = view.get_word()  # check if the first arg is specifying a certain rtfm
            approved_key = None

            for k, v in self.pages.items():
                if key == k:
                    approved_key = k
                    view.skip_ws()
                    obj = view.read_rest().strip()
                    if not obj: obj = None
                    break

                elif key.lower() == v['long'].lower():
                    approved_key = k
                    view.skip_ws()
                    obj = view.read_rest().strip()
                    if not obj:
                        obj = None
                    break

            if approved_key is None:
                obj = (key + " " + view.read_rest()).strip()
                if not obj: obj = None

            if not approved_key and ctx.channel.id in SPECIAL_CHANNEL_INDEX:
                approved_key = list(filter(lambda m: m['long'].lower() == SPECIAL_CHANNEL_INDEX[ctx.channel.id],
                                           self.pages.values()))[0]['quick']

            if not approved_key and ctx.guild.id in self.defaults:
                approved_key = self.defaults[ctx.guild.id]

            elif not approved_key:
                raise errors.CommandInterrupt("No rtfm selected, and no default doc is set for your guild.")

        elif ctx.guild.id in self.defaults:
            approved_key = self.defaults[ctx.guild.id]
        else:
            raise errors.CommandInterrupt("No rtfm selected, and no default doc is set for your guild.")

        self.usage[approved_key] = datetime.datetime.utcnow()
        await self.do_rtfm(ctx, approved_key, obj, labels, obvious_labels)

    @rtfm.command()
    async def list(self, ctx):
        """
        shows a list of the current documentation entries. you can use the short name to use the doc. ex: !rtfm py {{insert thing here}}
        """
        all_entries = await self.db.fetch("SELECT * FROM pages")
        entries = [(a['quick'], f"{a['long']}: {a['url']}") for a in all_entries]
        pages = paginator.FieldPages(ctx, entries=entries, per_page=5)
        await pages.paginate()

    @rtfm.command()
    @commands.has_permissions(manage_guild=True)
    async def default(self, ctx: Context, default: str):
        """
        sets a default rtfm for your guild, so you don't need to type the docs prefix each time.
        requires the `Bot Editor` role or higher
        note that you can only have 1 default per guild.
        """
        if default not in self.pages:
            return await ctx.send(f"`{default}` is not a valid doc! If you wish to add one, please use `!rtfm add` to submit it for review")
        else:
            self.defaults[ctx.guild.id] = default
            await self.db.execute("INSERT INTO default_rtfm VALUES ($1,$2)", ctx.guild.id, default)
            await ctx.send(f"set the guild's default rtfm to `{self.pages[default]['long']}`")

    @rtfm.command(usage="")
    async def add(self, ctx: Context, quick=None, long=None, url=None):
        """
        Have some documentation you want to see here? Add it here!
        there are a few requirements for your docs to be approved.
        - it **must** be created with Sphinx.

        You will be prompted for the documentation information
        """
        if await ctx.bot.is_owner(ctx.author) and quick and long and url:
            if quick in self.pages:
                return await ctx.send("Already exists")
            await self.db.execute("INSERT INTO pages VALUES ($1,$2,$3)", quick, long, url)

            self.pages[quick] = {"quick": quick, "long": long, "url": url}
            return await ctx.send("\U0001f44d")

        def check_cancel(content):
            if "cancel" in content:
                raise errors.CommandInterrupt("aborting")

        await ctx.send("By adding documentation, you agree that you have read the rules to adding documentation. type cancel to abort the creation process\n\n"
                       "Please provide a quick default for your rtfm (used when not accessing your guild's default, max 7 characters)")
        msg = await ctx.bot.wait_for("message", check=lambda m: m.channel == ctx.channel and m.author == ctx.author, timeout=30)
        quick = await commands.clean_content().convert(ctx, msg.content)
        if len(quick) > 7:
            raise commands.CommandError("That's more than 7 characters!")

        check_cancel(msg.content)
        if quick in self.pages:
            raise commands.CommandError("That documentation already exists!")

        try:
            long = await commands.clean_content().convert(ctx, await ctx.ask("Now, please provide the full documentation name", return_bool=False))
            check_cancel(long)
        except errors.CommandInterrupt: raise
        except: pass

        msg = await ctx.ask("Now, provide the url to the documentation.", return_bool=False)
        check_cancel(msg)

        url = await commands.clean_content().convert(ctx, msg)
        async with self.bot.session.get(url.strip("/")+"/objects.inv") as resp:
            if resp.status != 200:
                raise errors.CommandInterrupt("Invalid url provided (no /objects.inv found). remember to remove the current page! ex. https://docs.readthedocs.io/latest")

        await self.db.execute("INSERT INTO pages VALUES ($1,$2,$3)", quick, long, url)
        self.pages[quick] = {"quick": quick, "long": long, "url": url}
        await ctx.send("Your documentation has been added")

    @rtfm.command(hidden=True)
    @commands.is_owner()
    async def remove(self, ctx: Context, quick: str):
        await self.db.execute("DELETE FROM pages WHERE quick = $1", quick)
        if quick in self.pages:
            del self.pages[quick]
            return await ctx.send(f"removed `{quick}` from rtfm")
        await ctx.send(f"`{quick}` not found")

    @rtfm.before_invoke
    @default.before_invoke
    @add.before_invoke
    async def rtfm_pre(self, ctx: Context):
        if not self.pages:
            data = await self.db.fetch("SELECT * FROM pages")
            for record in data:
                self.pages[record['quick']] = dict(record)

        if not self.defaults:
            v = await self.db.fetch("SELECT * FROM default_rtfm")
            for record in v:
                self.defaults[record['guild_id']] = record['name']

    @commands.command()
    async def xkcd(self, ctx: Context, *, no_or_search: Union[int, str]):
        nsfw = ctx.guild is None or cast(discord.TextChannel, ctx.channel).is_nsfw()

        if isinstance(no_or_search, int):
            async with self.bot.session.get(f"https://xkcd.com{no_or_search}/info.0.json") as resp:
                if resp.status == 404:
                    return await ctx.send("XKCD not found")

                resp = await resp.json()
                e = discord.Embed(title=resp['safe_title' if nsfw else "title"],
                                   description=resp['alt'], color=0x36393E)
                e.set_footer(text=f"#{resp['num']}  • {resp['month']}/{resp['day']}/{resp['year']}")
                e.set_image(url=resp['img'])
                await ctx.send(embed=e)

        else:
            async with self.session.get(self.url + f"public/xkcd?search={no_or_search}") as resp:
                if resp.status != 200:
                    return await ctx.send(f"Something messed up :( ({resp.status, resp.reason})")

                data = await resp.json()

            if not data['nodes']:
                return await ctx.send("Nothing found :(")

            ems = []
            for d in data['nodes']:
                e = discord.Embed(
                    title=d['safe_title' if nsfw else "title"],
                    description=d['alt'],
                    color=0x36393E,
                    url=d['url'],
                    timestamp=datetime.datetime.fromisoformat(d['posted'])
                ).set_footer(
                    text=f"#{d['num']}"
                ).set_image(
                    url=d['image_url']
                )
                ems.append(e)

            if len(ems) == 1:
                return await ctx.send(embed=ems[0])

            e = paginator.EmbedPages(ctx, entries=ems)
            await e.paginate()

    @commands.command("chess")
    @commands.is_owner()
    async def chess(self, ctx: Context, target: discord.User, board_theme: str="walnut"):
        th1 = await ctx.ask(f"{ctx.author.mention}, theme?", return_bool=False)
        th2 = await ctx.ask(f"{target.mention}, theme?", return_bool=False, target=target)

        async with self.session.post(self.url + "games/chess", json={"white-theme": th1, "black-theme": th2, "board-theme": board_theme}) as resp:
            if resp.status != 200:
                return await ctx.send(f"Failed to generate a game: {resp.reason}")

            board: dict = await resp.json()

        #board = {'pieces': ['1a11', '0a21', None, None, None, '0a60', None, '1a80', None, '0b21', None, None, None, '0b60', None, '2b80', None, '0c21', '2c31', None, None, '0c60', None, '3c80', None, '4d21', None, '0d41', None, '0d60', None, '4d80', '5e11', '0e21', '3e31', None, None, None, '0e70', '5e80', '3f11', '0f21', None, None, None, None, '0f70', '3f80', '2g11', '0g21', None, None, None, None, '0g70', '2g80', '1h11', '0h21', None, None, None, None, '0h70', '1h80'], 'turn': 1, 'transcript': ['10d2d4nn', '00d7d6nn', '13c1e3nn', '00b7b6nn', '12b1c3nn', '00c7c6nn', '14d1d2nn', '00a7a6nn'], 'white-theme': 'glass', 'black-theme': 'glass', 'board-theme': 'walnut', 'castling': '1111'}

        last: Optional[discord.Message] = None
        _last: Optional[discord.Message] = None
        arrow = None
        do_render = True

        while True:
            if last:
                await last.delete()
                if _last:
                    await _last.delete()
                last = _last = None

            if do_render:
                _last = await ctx.send(str((board, arrow)))
                async with self.session.post(self.url + "games/chess/render", json={"board": board, "arrow": arrow}) as resp:
                    if resp.status != 200:
                        return await ctx.send(f"Failed to generate a board: {resp.reason}")

                    data = io.BytesIO()
                    data.write(await resp.read())
                    data.seek(0)

                last = await ctx.send(file=discord.File(data, "board.png"))

            turn = board['turn']
            t: discord.Member | discord.User
            if turn == 1:
                t = ctx.author
            else:
                t = target

            while True:
                move = await ctx.ask(None, target=t, return_bool=False, timeout=None)
                if move in ("cancel", "stop"):
                    await ctx.send("\U0001f44d")
                    return

                if "-" in move:
                    break

                if move == "transcript":
                    async with self.session.post(self.url + "games/chess/transcript", json={"board": board}) as resp:
                        transcript = await resp.text()

                    if len(transcript) > 1500:

                        pages = paginator.TextPages(ctx, "Click \U000023f9 to continue with the game\n" + transcript, max_size=1500, prefix="", suffix="")
                        await pages.paginate()
                    else:
                        await ctx.send(transcript)

            async with self.session.post(self.url + "games/chess/turn", json={"board": board, "move": move, "move-turn": "black" if turn else "white"}) as resp:
                if resp.status == 417:
                    data = await resp.json()
                    board = data['board']
                    await ctx.send(f"expectation failed: {data['error']}")
                    do_render = True

                elif resp.status == 200:
                    data = await resp.json()
                    board = data['board']
                    arrow = data.get("arrow")
                    do_render = True

                else:
                    await ctx.send(f"something fucked up: {resp.status, resp.reason, await resp.text()}")

    @commands.command("math")
    async def math(self, ctx: Context, *, expression: str):
        r"""
        This math parser makes use of the idevision math endpoint.
        This accepts mathematical functions (P(x) = expression)
        \> Functions in the form of `y = ...` will be graphed, and will not be usable in expressions. These functions have one implicit argument, `x`.
        Geometric sequences can be created using the `s` variable: `s=1,2,4`. The third arg is optional. You can then access the sequence via `s(n)`
        The following variables are currently built in:
        - pi
        - E
        The following functions are currently built in:
        - sin(x) / asin(x, y)
        - cos(x) / acos(x, y)
        - tan(x) / atan(x, y)
        - log(n, base)
        Each line will be treated as a separate expression. Functions defined are available for all expressions.
        If you find something that returns a 500 error, or something is missing that'd you'd like to see in this endpoint, feel free to contact me @IAmTomahawkx

        *This endpoint is in heavy beta, and is inherently unstable*
        """
        headers = {"User-Agent": f"BOB discord bot (squawking {ctx.author})"}

        async with self.session.post(self.url + "public/math", data=expression, headers=headers) as resp:
            if resp.status == 417:
                await ctx.send(f"Your expression contains errors\n```\n{(await resp.text()).strip()}\n```",
                               allowed_mentions=discord.AllowedMentions.none())

            elif resp.status == 200:
                data = await resp.json()
                files = []
                if data['images']:
                    for i, x in enumerate(data['images']):
                        async with self.session.get(x) as r:
                            if r.status == 200:
                                files.append(discord.File(io.BytesIO(await r.read()), filename=f"file-{i}.png"))

                await ctx.send(data['text'], files=files)

            else:
                await ctx.send(f"The api returned a non-ok response ({resp.status}: {await resp.text()}). "
                               f"<@547861735391100931> something fucked up", allowed_mentions=discord.AllowedMentions.all())

    @commands.command("rtfm-rs")
    async def rtfm_rs(self, ctx: Context, crate: str, *, query: str):
        """
        Searches https://docs.rs/ crates using the idevision.net api. alternatively, pass `std` as the crate to search the stdlib docs.
        """
        if not crate.startswith("https") and crate != "std":
            crate = f"https://docs.rs/{crate}"

        headers = {"User-Agent": f"BOB discord bot (squawking {ctx.author})"}
        async with self.session.get(self.url + "public/rtfm.rustdoc", params={"location": crate, "query": query}, headers=headers) as resp:
            if resp.status != 200:
                return await ctx.send(f"Api returned a bad response {resp.status, resp.reason}")

            data = await resp.json()
            await ctx.send(data)

    @commands.command()
    async def rtfs(self, ctx: Context, lib: Optional[_libconverter], *, item: str): # type: ignore
        """
        Indexes a library for items matching the given input.
        The following libraries are valid: dpy, dpy2, aiohttp, twitchio, wavelink

        you can use the --source flag to receive the source directly in discord, instead of a link to github
        """
        lib: str = lib # type: ignore

        fmt: str = "links"
        if "--source" in item:
            item = item.replace("--source", "")
            fmt = "source"

        if lib is None:
            lib = SPECIAL_CHANNEL_INDEX.get(ctx.channel.id, "discord.py-2")

        url = yarl.URL(self.url + "public/rtfs").with_query(
            {
                "query": item,
                "library": cast(str, lib),
                "format": fmt
            }
        )
        headers = {"User-Agent": f"BOB discord bot (squawking {ctx.author})"}
        async with self.session.get(url, headers=headers) as resp:
            if resp.status != 200:
                return await ctx.reply(f"The api returned a non-ok response: {resp.status} ({resp.reason})", mention_author=False)

            data = await resp.json()

        nodes = data['nodes']
        time = float(data['query_time'])

        if not nodes:
            return await ctx.reply(f"Could not find anything. Sorry. (query time {round(time, 4)})", mention_author=False)

        if fmt == "links":
            out = [f"[{name}]({url})" for name, url in nodes.items()]

            await ctx.send(embed=discord.Embed(
                description="\n".join(out),
                title=f"Result for {lib}: {item}"
            )
                .set_footer(text=f"Is the api behind on commits? Use {discord.utils.escape_mentions(cast(str, ctx.prefix))}rtfs-reload")
                .set_author(name=f"query time: {round(time, 3)} • commit {data['commit'][:6]}"))

        else:
            n = next(iter(nodes.items()))
            await ctx.reply(f"Showing source for {n[0]}\nCommit: {data['commit']}", mention_author=False)
            pages = paginator.TextPages(ctx, n[1], prefix="```py")
            await pages.paginate()

    @commands.command("rtfs-reload")
    @commands.cooldown(1, 180)
    async def rtfs_reload(self, ctx):
        """
        Reloads all rtfs nodes up to the latest commit.
        This command should not be called frequently.
        """
        url = yarl.URL(self.url + "public/rtfs.reload")

        async with self.session.put(url) as resp:
            if resp.status != 200:
                return await ctx.send(f"The api returned a non-ok response: {resp.status} ({resp.reason})")

            data = await resp.json()

        if data['fail']:
            fails = "(Failed to reload: " + ", ".join(data['fails']) + ")"
        else:
            fails = ""

        commits = "\n".join(f"{name}: {commit}" for name, commit in data['commits'].items())

        await ctx.reply(f"Reloaded {len(data['success'])} repos {fails}.\n{commits}", mention_author=False)

    @commands.command()
    @commands.cooldown(4, 10)
    async def ocr(self, ctx: Context, attachment: Optional[str] = None):
        """
        Preforms OCR on an image. This image can be uploaded as an attachment, or passed as a url link.
        OCR api: https://idevision.net/docs
        """
        if not ctx.message.attachments and not attachment:
            return await ctx.send("Please provide an attachment or a url")

        if ctx.message.attachments:
            url = ctx.message.attachments[0].url
        else:
            url = attachment

        resp = await self.bot.session.get(url) #type: aiohttp.ClientResponse
        if "image" not in resp.content_type:
            resp.close()
            return await ctx.send("URL/attachment was not an image")

        filetype = (ctx.message.attachments[0].filename if ctx.message.attachments else cast(str, attachment).split("/")[-1]).split(".")[-1]

        headers = {"User-Agent": f"BOB discord bot (squawking {ctx.author})"}
        async with self.session.get(self.url + f"public/ocr?filetype={filetype}", data=resp.content, headers=headers) as r:
            resp.close()
            if r.status != 200:
                return await ctx.send(f"The api responded with {r.status}: {r.reason}")
            else:
                d = await r.json()
                pages = paginator.TextPages(ctx, d['data'])

    @commands.group(aliases=["id"], invoke_without_command=True)
    async def idevision(self, ctx):
        """
        API control for the idevision site.
        Most of this is owner-only, the `apply` and `token` subcommands are available for public use.
        """
        await ctx.send("Idevision documentation can be found at https://idevision.net/docs\n"
                       f"To regenerate your idevision token, use `{ctx.invoked_with} token`\n"
                       f"To apply for an idevision API token, use `{ctx.invoked_with} apply`")

    @idevision.command("token")
    async def api_token(self, ctx):
        """
        Regenerates your idevision token, and dms it to you.
        """
        async with self.session.post(self.url + "internal/users/token", json={
            "discord_id": ctx.author.id
        }) as resp:
            if resp.status == 400:
                pref = await commands.clean_content().convert(ctx, ctx.prefix)
                return await ctx.send(f"You do not have an idevision account. Use `{pref}idevision apply <reason>` to apply for an account.")
            if resp.status != 200:
                return await ctx.send(f"Internal error: {resp.status}, {await resp.text()}")
            else:
                data = await resp.json()
                await ctx.author.send(f"Your idevision token is `{data['token']}`. Use the `idevision token` command to regenerate your token at any time.")
                await ctx.message.add_reaction("thumbsup")

    @idevision.command("apply")
    async def api_apply(self, ctx: Context, *, reason: str):
        """
        Allows you to apply for a token on the idevision API.
        This will give you access to the OCR endpoint, CDN endpoints, along with higher ratelimits.
        Anyone may use this command.
        """
        async with self.session.post(self.url + "internal/users/apply", json={
            "username": ctx.author.name + ctx.author.discriminator, # intentionally not using str() here
            "userid": ctx.author.id,
            "reason": reason,
            "permissions": ["public.ocr", "cdn"]
        }) as resp:
            if resp.status == 403:
                return await ctx.send(f"Failed to make the request (unauthorized): {await resp.text(), resp.reason}")
            elif resp.status != 201:
                return await ctx.send(f"Internal error: {resp.status}, {resp.reason}")
            else:
                emb = discord.Embed(colour=discord.Colour.dark_gold(), title="Application Recieved", description=f"([jump url]({ctx.message.jump_url}))\nReason:\n{reason}")
                emb = emb.set_author(name=str(ctx.author), icon_url=str(cast(discord.Asset, ctx.author.avatar).url)).set_footer(text=str(ctx.author.id))
                emb.timestamp = datetime.datetime.utcnow()
                await self.hook.send(embed=emb)
                await ctx.send("Application recieved. Please make sure your dms are enabled, or you will not recieve your token when your application is accepted.")

    @idevision.command("accept", aliases=['approve'])
    @commands.is_owner()
    async def accept(self, ctx: Context, user: discord.User):
        async with self.session.post(self.url + "internal/users/accept", json={"userid": user.id}) as resp:
            if resp.status != 201:
                return await ctx.send(f"Internal error: {resp.status}, {resp.reason}")
            else:
                data = await resp.json()
                try:
                    await user.send(f"Your idevision token is `{data['token']}`. Use the `idevision token` command to regenerate your token at any time.")
                except:
                    await ctx.send(f"Accepted {user} (failed to dm the user)")
                else:
                    await ctx.send(f"Accepted {user} (user notified through dm)")

    @idevision.command("deny", aliases=['decline'])
    @commands.is_owner()
    async def user_deny(self, ctx: Context, user: discord.User, allow_reapply=False, *, reason):
        async with self.session.post(self.url + "internal/users/deny", json={"userid": user.id, "retry": allow_reapply, "reason": reason}) as resp:
            if resp.status != 204:
                return await ctx.send(f"Internal error: {resp.status}, {resp.reason}")
            else:
                try:
                    await ctx.author.send(
                        f"Your idevision application has been denied. {'you may reapply' if allow_reapply else ''}\nreason: \n```\n{reason}\n```")
                except:
                    await ctx.send(f"Denied {user} (failed to dm the user)")
                else:
                    await ctx.send(f"Denied {user} (user notified through dm)")

    @idevision.group(invoke_without_command=True, aliases=['user'])
    @commands.is_owner()
    async def users(self, ctx: Context, username: Union[discord.User, str], unsafe:bool=False):
        url = yarl.URL(self.url + "internal/users")
        if isinstance(username, discord.User):
            url = url.with_query(username=username.name + username.discriminator, discord_id=username.id)
        else:
            url = url.with_query(username=username)

        async with self.session.get(url) as resp:
            if 200 <= resp.status < 300:
                data = await resp.json()
                if "auth_key" in data and not unsafe:
                    del data['auth_key']

                await ctx.send("\n".join(f"{x}: {v}" for x, v in data.items()))

            elif resp.reason:
                await ctx.send(f"Failed: ({resp.status})" + resp.reason)

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command("add")
    @commands.is_owner()
    async def user_add(self, ctx: Context, user: discord.User, *perms):
        async with self.session.post(self.url + "internal/users",
                                     json={"username": user.name + user.discriminator, "permissions": perms, "discord_id": user.id}) as resp:
            if 200 <= resp.status < 300:
                data = await resp.json()
                await ctx.send(f"User has been added, token: {data['token']}")

            elif resp.reason:
                await ctx.send(f"Failed: ({resp.status})" + resp.reason)

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command("addperms", aliases=['addperm', 'ap'])
    @commands.is_owner()
    async def user_add_perm(self, ctx: Context, username: Union[discord.User, str], *perms):
        url = yarl.URL(self.url + "internal/users")
        if isinstance(username, discord.User):
            url = url.with_query(username=username.name + username.discriminator, discord_id=username.id)
        else:
            url = url.with_query(username=username)

        async with self.session.get(url) as resp:
            if 200 <= resp.status < 300:
                data = await resp.json()
                _perms = data['permissions']

            else:
                return await ctx.send(f"Failed: ({resp.status})" + cast(str,resp.reason))

        perms = set(perms)
        perms.update(_perms)
        perms = list(perms)

        async with self.session.patch(self.url + "internal/users",
                                     json={"username": username, "permissions": perms}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send(f"User modified successfully")

            elif resp.reason:
                await ctx.send(f"Failed: {resp.status, resp.reason}")

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command("removeperm", aliases=['removeperms', 'rmperm', 'rmperms', 'rp'])
    @commands.is_owner()
    async def user_rm_perm(self, ctx: Context, username: Union[discord.User, str], *perms):
        url = yarl.URL(self.url + "internal/users")
        if isinstance(username, discord.User):
            url = url.with_query(username=username.name + username.discriminator, discord_id=username.id)
        else:
            url = url.with_query(username=username)

        async with self.session.get(url) as resp:
            if 200 <= resp.status < 300:
                data = await resp.json()
                _perms = data['permissions']

            else:
                return await ctx.send(f"Failed: ({resp.status})" + cast(str, resp.reason))

        for p in perms:
            if p in _perms:
                _perms.remove(p)

        async with self.session.patch(self.url + "internal/users",
                                     json={"username": username, "permissions": _perms}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send(f"User modified successfully")

            elif resp.reason:
                await ctx.send(f"Failed: {resp.status, resp.reason}")

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command("setname")
    @commands.is_owner()
    async def user_set_name(self, ctx: Context, currentname: Union[discord.User, str], new_name: str):
        if isinstance(currentname, discord.User):
            currentname = currentname.name + currentname.discriminator

        async with self.session.patch(self.url + "internal/users",
                                     json={"username": currentname, "new_username": new_name}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send(f"User modified successfully")

            elif resp.reason:
                await ctx.send(f"Failed: {resp.status, resp.reason}")

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command()
    @commands.is_owner()
    async def deauth(self, ctx: Context, username: str):
        async with self.session.post(self.url + "internal/users/deauth", json={"username": username}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send("User has been deauthed")

            elif resp.reason:
                await ctx.send(f"Failed: ({resp.status})" + resp.reason)

            else:
                await ctx.send("Failed. " + str(resp.status))

    @users.command()
    @commands.is_owner()
    async def reauth(self, ctx: Context, username: str):
        async with self.session.post(self.url + "internal/users/auth", json={"username": username}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send("User has been reauthed")

            elif resp.reason:
                await ctx.send(f"Failed: ({resp.status})" + resp.reason)

            else:
                await ctx.send("Failed. " + str(resp.status))

    @idevision.group(invoke_without_command=True)
    @commands.is_owner()
    async def cdn(self, ctx):
        await ctx.send_help(ctx.command)

    @cdn.command("stats")
    @commands.is_owner()
    async def cdn_stats(self, ctx: Context, user: Optional[str] = None):
        if user:
            async with self.session.get(self.url + f"cdn/user?username={user}") as resp:
                if 200 <= resp.status < 300:
                    data = await resp.json()
                    await ctx.send(f"Image count: {data['upload_count']}. "
                                   f"Most recent upload: <{data['last_upload']}>")
                else:
                    await ctx.send(f"fetching from the api failed: {resp.reason}")
        else:
            async with self.session.get(self.url + "cdn") as resp:
                if 200 <= resp.status < 300:
                    data = await resp.json()
                    await ctx.send(f"Image count: {data['upload_count']}. Count today: {data['uploaded_today']} "
                                   f"Most recent: <{data['last_upload']}>")

                else:
                    await ctx.send(f"fetching from the api failed: {resp.reason}")

    @cdn.command("purge", hidden=True)
    @commands.is_owner()
    async def cdn_purge(self, ctx: Context, user: str):
        async with self.session.post(self.url + "cdn/purge", json={"username": user}) as resp:
            if 200 <= resp.status < 300:
                await ctx.send(f"Purged {user}'s images")
            else:
                await ctx.send(f"Failed to purge: {resp.reason}")

    def _parse_args(self, args):
        url = None
        node = None
        filename = None
        for arg in args:
            if not arg.strip():
                continue

            if URL_RE.match(arg):
                if url:
                    raise commands.UserInputError("You may only pass 1 URL to upload")
                url = arg
            else:
                if node:
                    raise commands.UserInputError("You may only pass 1 node and 1 filename")
                if filename:
                    node = arg
                else:
                    filename = arg

        return url, node, filename

    @cdn.command("upload", usage="[url or attachment] [filename] [node]")
    async def cdn_upload(self, ctx: Context, *args):
        """
        Uploads an image to the cdn on your behalf. You must have an idevision auth token with the `cdn` permission group.
        """
        async with ctx.typing():
            url, node, filename = self._parse_args(args)

            if not ctx.message.attachments and not url:
                return await ctx.send("Provide an attachment or link to an image")

            async with self.session.get(self.url + f"internal/users?discordid={ctx.author.id}") as resp:
                if resp.status == 204:
                    return await ctx.send("Public CDN posting is coming soon... for now you must have an idevision login to use this command")
                elif resp.status != 200:
                    return await ctx.send(f"Something went wrong... {resp.reason}")

                user = await resp.json()
                if not user['administrator'] and 'cdn' not in user['permissions']:
                    return await ctx.send("Public CDN posting is coming soon... currently you are not authorized to use the cdn")

                if node and not user['administrator'] and 'cdn.manage' not in user['permissions']:
                    return await ctx.send("You do not have permission to select the node")

                if filename and not user['administrator'] and 'cdn.manage' not in user['permissions']:
                    return await ctx.send("You do not have permission to select the filename")

            if url:
                fn = url.split("/")[-1]
                async with self.bot.session.get(url, headers={"Accept": "image/*"}) as resp:
                    if resp.content_length > 30000000:
                        return await ctx.send("target URL file is too large")

                    fp = io.BytesIO()
                    fp.write(await resp.read())
                    fp.seek(0)

            else:
                fp = io.BytesIO()
                await ctx.message.attachments[0].save(fp)
                fp.seek(0)
                fn = ctx.message.attachments[0].filename

            data = aiohttp.FormData()
            data.add_field('file',
                           fp,
                           filename=fn)

            url = yarl.URL(self.url + "cdn")
            if node and filename:
                url = url.with_query(node=node, name=filename)
            elif node:
                url = url.with_query(node=node)
            elif filename:
                url = url.with_query(name=filename)

            async with self.session.post(url, data=data, headers={"Authorization": user['auth_key'], "File-Name": fn}) as resp:
                if 200 <= resp.status < 300:
                    d = await resp.json()
                    return await ctx.send(f"Uploaded {fn} to the cdn, at {d['url']}")

                else:
                    await ctx.send(f"There was an error uploading the file: ({resp.status}) {resp.reason}")

    @cdn.command("delete", hidden=True)
    async def cdn_delete(self, ctx: Context, url: str):
        """
        deletes an image from the cdn.
        You may only delete your own images unless you have the `cdn.manage` permission group
        """
        try:
            url_ = yarl.URL(url)
            if url_.host != "cdn.idevision.net":
                return await ctx.send("Not a valid image")
            pth = url_.path.strip("/")
            node, image = pth.split("/")
        except:
            return await ctx.send("Not a valid image")

        async with self.session.get(self.url + f"internal/users?discordid={ctx.author.id}") as resp:
            if resp.status == 204:
                return await ctx.send("You are not authorized to use this command")
            elif resp.status != 200:
                return await ctx.send(f"Something went wrong... {resp.reason}")

            user = await resp.json()
            if not user['administrator'] and 'cdn' not in user['permissions']:
                return await ctx.send("You are not authorized to use this command")

        async with self.session.delete(f"{self.url}cdn/{node}/{image}", headers={"Authorization": user['auth_key']}) as resp:
            if resp.status == 404:
                return await ctx.send("Image not found")

            elif resp.status == 401:
                return await ctx.send(resp.reason)

            elif resp.status == 204:
                return await ctx.send("Image deleted successfully")

            else:
                return await ctx.send(f"Something went wrong: {resp.status}, {resp.reason}")

    @cdn.command("listnode")
    @commands.is_owner()
    async def cdn_listnode(self, ctx: Context, node: str):
        """
        Lists the content of a node
        """
        async with self.session.get(self.url + f"cdn/list?node={node}&sort=nodename") as resp:
            if resp.status != 200:
                return await ctx.send(f"Something fucked up: {resp.status} ({resp.reason})")

            data = await resp.json()
            d = [list(x.values()) for x in data[node]]
            d = tabulate.tabulate(d, headers=list(data[node][-1].keys()), tablefmt="psql")
            pages = paginator.TextPages(ctx, d)
            await pages.paginate()

    @cdn.command("nodes")
    @commands.is_owner()
    async def cdn_nodes(self, ctx: Context, safe=True):
        """
        Lists all nodes
        """
        async with self.session.get(self.url + f"cdn/nodes?safe={safe}") as resp:
            if resp.status != 200:
                return await ctx.send(f"Something fucked up: {resp.status} ({resp.reason})")

            data = await resp.json()

        data = sorted({int(x): y for x, y in data.items()}.items(), key=lambda x: x[0])
        fmt = ""

        for (id, node) in data:
            fmt += f"__Node {id}__\n- Name: {node['name']}\n- Port: {node['port']}\n- Last Contact: {round(node['signin'])} Seconds Ago\n"
            if 'ip' in node:
                fmt += f"- IP: {node['ip']}\n"

            fmt += "\n"

        pages = paginator.TextPages(ctx, fmt, prefix="", suffix="")
        await pages.paginate()

    @idevision.command("homepage")
    async def cdn_homepage(self, ctx: Context,
                       display_name,
                       link1="", link1name="",
                       link2="", link2name="",
                       link3="", link3name="",
                       link4="", link4name=""):
        """
        Allows you to set homepage links for https://idevision.net/homepage
        To access your homepage, go to https://idevision.net/homepage?user={ctx.author.name}
        """
        resp = await self.session.post(self.url + "homepage", json={
            "display_name": display_name,
            "user": ctx.author.name,
            "link1": link1,
            "link2": link2,
            "link3": link3,
            "link4": link4,
            "link1_name": link1name,
            "link2_name": link2name,
            "link3_name": link3name,
            "link4_name": link4name,
        })
        resp.close()
        await ctx.send(f"Page is now available at {self.url.replace('api/', 'homepage')}?user={ctx.author.name}")

    @idevision.command("logs")
    @commands.is_owner()
    async def get_logs(self, ctx: Context, unsafe: Optional[bool], page: Optional[int] = 0, oldest_first: Optional[bool]=False):
        url = yarl.URL(self.url+"internal/logs").with_query({
            "page": str(page),
            "oldest-first": str(oldest_first).lower(),
            "safe": str(unsafe if unsafe is not None else True).lower()
        })
        async with self.session.get(url) as resp:
            if resp.status != 200:
                return await ctx.send("Bad Response")

            data = await resp.json()
            data = data['rows']

        table = tabulate.tabulate([list(x.values()) for x in data], headers=list(data[0].keys()))
        pages = paginator.TextPages(ctx, table)
        await pages.paginate()

    @idevision.group("permissions", aliases=['perms'], invoke_without_command=True)
    @commands.is_owner()
    async def perms(self, ctx):
        async with self.session.get(self.url + "internal/permissions") as resp:
            if resp.status != 200:
                return await ctx.send(f"Bad Response {resp.status, resp.reason}")

            await ctx.send(", ".join((await resp.json())['permissions']))

    @perms.command("routes")
    @commands.is_owner()
    async def routes(self, ctx):
        async with self.session.get(self.url + "internal/routes") as resp:
            if resp.status != 200:
                return await ctx.send(f"Bad Response {resp.status, resp.reason}")

            await ctx.paginate_text(pprint.pformat(await resp.json()))

    @perms.command("add")
    @commands.is_owner()
    async def routes_add(self, ctx: Context, perm: str):
        async with self.session.post(self.url + "internal/permissions", json={"permission": perm}) as resp:
            if resp.status != 204:
                return await ctx.send(f"Failed to add permission: {resp.status, resp.reason}")

            await ctx.send("Success")

    @perms.command("remove")
    @commands.is_owner()
    async def routes_remove(self, ctx: Context, perm: str):
        async with self.session.delete(self.url + "internal/permissions", json={"permission": perm}) as resp:
            if resp.status != 204:
                return await ctx.send(f"Failed to add permission: {resp.status, resp.reason}")

            await ctx.send("Success")

    @perms.command(aliases=['removeroute', 'editroute'])
    @commands.is_owner()
    async def addroute(self, ctx: Context, route: str, method: str, perm: Optional[str] = None, force = False):
        if all((route, method, perm)):
            async with self.session.post(self.url + "internal/routes?force=" + str(force), json={
                "endpoint": route,
                "method": method,
                "permission": perm
            }) as resp:
                if resp.status != 204:
                    return await ctx.send(f"Failed to add route: {resp.status, resp.reason}")

                await ctx.send("Success")

        else:
            async with self.session.delete(self.url + "internal/routes", json={
                "endpoint": route,
                "method": method
            }) as resp:
                if resp.status != 204:
                    return await ctx.send(f"Failed to remove the route: {resp.status, resp.reason}")

                await ctx.send("Success")

    @idevision.command("beta")
    @commands.is_owner()
    async def toggle_beta(self, ctx: Context, state: Optional[bool] = None):
        if state is None:
            return await ctx.send(f"Currently targeting {'beta' if self.url == 'https://beta.idevision.net/api/' else 'production'} idevision site ({self.url.replace('api/', '')})")

        if state:
            self.url = "https://beta.idevision.net/api/"
        else:
            self.url = "https://idevision.net/api/"

        await ctx.send(f"Now targeting {'beta' if self.url == 'https://beta.idevision.net/api/' else 'production'} idevision site ({self.url.replace('api/', '')})")

    @commands.command()
    async def retry(self, ctx: Context):
        """
        yeet
        """
        msg: discord.Message = ctx.message
        if not msg.reference:
            return await ctx.send("Missing a reply")

        if msg.reference.cached_message:
            if not await self.bot.is_owner(ctx.author) and msg.reference.cached_message.author.id != ctx.author.id:
                return await ctx.send("Fuck off")

            await self.bot.process_commands(msg.reference.cached_message)

        else:
            data = await ctx.channel.fetch_message(cast(int, msg.reference.message_id))
            if not await self.bot.is_owner(ctx.author) and data.author.id != ctx.author.id:
                return await ctx.send("Fuck off")

            await self.bot.process_commands(data)


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.ops import gen_math_ops
from tensorflow.python.ops import math_ops
from tensorflow.python.platform import googletest
from tensorflow.python.platform import test
from tensorflow.python.platform import tf_logging
from tensorflow.python.util import deprecation
from tensorflow.python.util import dispatch
from tensorflow.python.util.tf_export import tf_export

class CustomTensor(object):
  """A fake composite tensor class, for testing type-based dispatching."""

  def __init__(self, tensor, score):
    self.tensor = ops.convert_to_tensor(tensor)
    self.score = score

@tf_export("test_op")
@dispatch.add_dispatch_support
def test_op(x, y, z):
  """A fake op for testing dispatch of Python ops."""
  return x + (2 * y) + (3 * z)

@test_util.run_all_in_graph_and_eager_modes
class DispatchTest(test_util.TensorFlowTestCase):

  def testAddDispatchForTypes_With_CppOp(self):
    original_handlers = gen_math_ops.add._tf_dispatchers[:]

    # Override the behavior of gen_math_ops.add.
    @dispatch.dispatch_for_types(gen_math_ops.add, CustomTensor)
    def custom_add(x, y, name=None):  # pylint: disable=unused-variable
      return CustomTensor(gen_math_ops.add(x.tensor, y.tensor, name),
                          (x.score+y.score) / 2.0)
    self.assertEqual(len(math_ops.add._tf_dispatchers),
                     len(original_handlers) + 1)

    # Test that we see the overridden behavior when using CustomTensors.
    x = CustomTensor([1, 2, 3], 2.0)
    y = CustomTensor([7, 8, 2], 0.0)
    x_plus_y = gen_math_ops.add(x, y)
    self.assertAllEqual(self.evaluate(x_plus_y.tensor), [8, 10, 5])
    self.assertNear(x_plus_y.score, 1.0, 0.001)

    # Test that we still get the right behavior when using normal Tensors.
    a = [1, 2, 3]
    b = [4, 5, 6]
    a_plus_b = gen_math_ops.add(a, b)
    self.assertAllEqual(a_plus_b, [5, 7, 9])

    # Test that we still get a TypeError or ValueError if we pass some
    # type that's not supported by any dispatcher.
    with self.assertRaises((TypeError, ValueError)):
      gen_math_ops.add(a, None)

    # Clean up
    gen_math_ops.add._tf_dispatchers = original_handlers

  def testAddDispatchForTypes_With_PythonOp(self):
    original_handlers = test_op._tf_dispatchers[:]

    @dispatch.dispatch_for_types(test_op, CustomTensor)
    def override_for_test_op(x, y, z):  # pylint: disable=unused-variable
      return CustomTensor(test_op(x.tensor, y.tensor, z.tensor),
                          (x.score + y.score + z.score) / 3.0)

    x = CustomTensor([1, 2, 3], 0.2)
    y = CustomTensor([7, 8, 2], 0.4)
    z = CustomTensor([0, 1, 2], 0.6)

    result = test_op(x, y, z)
    self.assertAllEqual(self.evaluate(result.tensor), [15, 21, 13])
    self.assertNear(result.score, 0.4, 0.001)

    # Clean up
    test_op._tf_dispatchers = original_handlers

  def testDispatchForTypes_SignatureMismatch(self):
    with self.assertRaisesRegexp(AssertionError, "The decorated function's "
                                 "signature must exactly match.*"):
      @dispatch.dispatch_for_types(test_op, CustomTensor)
      def override_for_test_op(a, b, c):  # pylint: disable=unused-variable
        return CustomTensor(test_op(a.tensor, b.tensor, c.tensor),
                            (a.score + b.score + c.score) / 3.0)

  def testDispatchForTypes_OpDoesNotSupportDispatch(self):
    def some_op(x, y):
      return x + y

    with self.assertRaisesRegexp(AssertionError, "Dispatching not enabled for"):
      @dispatch.dispatch_for_types(some_op, CustomTensor)
      def override_for_some_op(x, y):  # pylint: disable=unused-variable
        return x if x.score > 0 else y

  '''
  @test.mock.patch.object(tf_logging, "warning", autospec=True)
  def testInteractionWithDeprecationWarning(self, mock_warning):
    @deprecation.deprecated(date=None, instructions="Instructions")
    @dispatch.add_dispatch_support
    def some_op(x):
      return x

    some_op(5)

    message = mock_warning.call_args[0][0] % mock_warning.call_args[0][1:]
    self.assertRegexpMatches(
        message,
        r".*some_op \(from __main__\) is deprecated and will be "
        "removed in a future version.*")
  '''

if __name__ == "__main__":
  googletest.main()


import time

from selenium.webdriver.common.action_chains import ActionChains

from Selenium.findElementByXpathToSelenium import search

def click(driver, xpathOrElement, debug=True, timer=2):
    try:
        if type(xpathOrElement).__name__ == "str":
            element = search(driver, xpathOrElement, debug=debug)
        else:
            element = xpathOrElement
        ActionChains(driver).move_to_element(element).click(element).perform()
        time.sleep(timer)
        return element
    except:
        return None


from enum import Enum
from functools import lru_cache

from pydantic import BaseSettings

from morpheus_data.registry.model_manager import (
    download_controlnet_model_from_huggingface,
    download_magicprompt_model_from_huggingface,
    download_model_from_huggingface,
)

MODEL_FILE_PATH = "./scripts/models/models-info.yaml"
CONTROLNET_FILE_PATH = "./scripts/models/controlnet-models-info.yaml"
MAGICPROMPT_FILE_PATH = "./scripts/models/magicprompt-models-info.yaml"

class APIServer(str, Enum):
    local = "local"
    staging = "staging"
    production = "production"

class Target(str, Enum):
    sdiffusion = "sdiffusion"
    controlnet = "controlnet"
    magicprompt = "magicprompt"

class DBTarget(str, Enum):
    sdiffusion = "sdiffusion"
    controlnet = "controlnet"
    magicprompt = "magicprompt"

class Settings(BaseSettings):
    hf_auth_token: str = ""

@lru_cache()
def get_settings() -> Settings:
    settings = Settings()
    return settings

api_server_urls = {
    "local": "http://api:8001",
    "staging": "http://198.176.97.61:8001",
    "production": "https://api-morpheus.monadical.io",
}

# API service endpoints
models_url = {
    "sdiffusion": "models",
    "controlnet": "cnmodels",
}

# Map targets to their corresponding functions to download models
download_model = {
    "sdiffusion": download_model_from_huggingface,
    "controlnet": download_controlnet_model_from_huggingface,
    "magicprompt": download_magicprompt_model_from_huggingface,
}

# Map targets to their corresponding config file
config_file = {
    "sdiffusion": MODEL_FILE_PATH,
    "controlnet": CONTROLNET_FILE_PATH,
    "magicprompt": MAGICPROMPT_FILE_PATH,
}


import sys
import math

def get():
    return sys.stdin.readline()

num_class = int(get())
num_student_per_class = list(map(int, get().split()))

main, sub = map(int, get().split())

answer = 0

for i in num_student_per_class:
    answer += 1 # main
    temp_i = i - main

    if temp_i <= 0: continue

    elif temp_i > 0:
        answer += math.ceil(temp_i / sub)

print(answer)


import numerical_integrators
import numpy as np
import matplotlib.pyplot as plt
import math
from matplotlib import rc

#V : Viewed
#N : Not viewed
#ideas: more than one sigma
#V/j*(N/k) gets sigma1 V/(V-j)*(1-(N/K)) gets sigma^2
#sigma_1 only friends , but, note assumptions: still randomly mixed group of groups of friends!
#sigma_2 mass platforms, but, assumption is it will reach everyone.
#sigma: Sharing rate
#assumptions: make sure V/j < ...
#assumptions: once you've seen it from one person, that's it
#asumption: "People's friends" and "general public" are separate groups

def dV_dt(V_t, N_t, sigma_1, sigma_2, j=1, k=1):
    #rate of Viewing sigma
    if (j >= 1 or k >= 1):
        print("j and k must be fractions less than 1 (and greater than 0)")
        return None
    else:
        try:
            dV = sigma_1*((j*V_t)*(N_t*k)) + sigma_2*((V_t-(V_t*j))*(N_t-(N_t*k)))
        except OverflowError:
            dV = 0
        if (np.isnan(dV) or np.isinf(dV)):
            dV = 0
        return dV

def prop_memes_euler(T, V_0, N_0, sigma_1, sigma_2, V_partition_frac, N_partition_frac, step_size_h, steps):
    V_current = V_0
    N_current = N_0
    V_vec=[V_current]
    N_vec=[N_current]
    time = 0
    time_vec = [0]
    for t in range(1, math.floor(steps/step_size_h)):
            dV_dt_current = dV_dt(V_current, N_current, sigma_1, sigma_2, V_partition_frac, N_partition_frac)
            if dV_dt_current is None:
                return None
            V_plus_t = numerical_integrators.euler_step(step_size_h, V_current, dV_dt_current)
            V_vec.append(V_plus_t)
            N_vec.append(T-V_plus_t)
            V_current=V_vec[-1]
            N_current=N_vec[-1]
            time += step_size_h
            time_vec.append(time)
    time_vec = time_vec[:len(V_vec)]
    rc('font', **{'family': 'sans-serif', 'sans-serif': ['Helvetica']})
    rc('text', usetex=True)
    plt.plot(time_vec[1:], V_vec[1:], alpha=0.6, lw=2, label=make_label(sigma_1, sigma_2, V_partition_frac, N_partition_frac))
    plt.xlabel('Log time t with step size '+str(step_size_h))
    plt.ylabel('$V$: number who have viewed the meme')
    plt.legend(loc='lower right')
    return V_vec

def make_label(sigma_1, sigma_2, V_frac, N_frac):
    label = '$\\sigma_1=$' + str(sigma_1) + ', $\\sigma_2=$' + str(sigma_2) + ', $j=$'+str(V_frac)+', $k=$'+str(N_frac)
    return label


import time
import random
import requests
import tkinter as tk
from tkinter import ttk, messagebox
from tkinter.messagebox import showinfo
from PIL import Image, ImageTk
# from modules.banner import *
from tkinter import filedialog as fd
from modules.views import viewUrl
import threading
from modules.data import useragent_path, proxies_path

# Create variables
success = 0
fails = 0
useragent = 'loading...'
proxies = 'loading...'
running = True

# Functions
def btns_disable():
    StartBtnTabOne["state"] = "disabled"
    ClearAll["state"] = "disabled"
    ResetProxy["state"] = "disabled"
    ThreadingTabOne["state"] = "disabled"
    UrlTabOne["state"] = "disabled"
    TimesTabOne["state"] = "disabled"

def btns_enable():
    StartBtnTabOne["state"] = "enabled"
    ClearAll["state"] = "normal"
    ResetProxy["state"] = "normal"
    ThreadingTabOne["state"] = "normal"
    UrlTabOne["state"] = "normal"
    TimesTabOne["state"] = "normal"

def clearAll():
    UrlTabOne.delete(0, tk.END)
    TimesTabOne.delete(0, tk.END)

def resetProxy():
    global useragent, proxies
    with open(useragent_path , encoding="utf-8") as f:
        useragent = random.choice(f.readlines()).split("\n")[0]

    with open("./files/proxies.txt" , encoding="utf-8") as f:
        proxies = random.choice(f.readlines()).split("\n")[0]

    ProxyStatus['text'] = 'Proxy: %s' % proxies

def finishRun(x):
    timer = round((time.time() - x) ,2)
    TimeCount['text'] = 'Time: %ss' % timer
    ProgressStatus['text'] = 'Status: Finished!'
    btns_enable()

def checkViewUrl():

    global success, fails

    # Validate user input
    if len(UrlTabOne.get()) == 0 or len(TimesTabOne.get()) == 0:
        messagebox.showwarning("Dumb alert!", "Empty input... Use brain!")
        return False

    # Reset these
    success = 0
    fails = 0
    TextSuccess['text'] = 'Success: 0'
    TextFailed['text'] = 'Failed: 0'
    TimeCount['text'] = 'Time: Measurement...'
    ProgessBarTabOne['value'] = 0

    ProgressStatus['text'] = 'Progress: Preparing...'

    # Disable buttons
    btns_disable()

    # Setup
    limit = int(TimesTabOne.get())
    ProgessBarTabOne['maximum'] = limit
    ProxyStatus['text'] = 'Proxy: %s' % proxies

    # Check for threading option
    if checkVar.get() == 0:
        ProgressStatus['text'] = 'Status: Running...'
        start_time = time.time()
        for i in range(limit):
            cmd = viewUrl(UrlTabOne.get(), useragent, proxies)
            if cmd == True:
                success = success + 1
                TextSuccess['text'] = 'Success: %s' % success
                ProgessBarTabOne['value'] = success
                root.update()
            else:
                fails = fails + 1
                TextFailed['text'] = 'Fails: %s' % fails
                ProgessBarTabOne['value'] += 1
                root.update()
        finishRun(start_time)
    else:
        cmd = viewUrl(UrlTabOne.get(), useragent, proxies)
        threads = []
        start_time = time.time()
        ProgressStatus['text'] = 'Status: Running threads...'

        for i in range(limit):
            threads.append(threading.Thread(target=viewUrl, args=(UrlTabOne.get(), useragent, proxies)))
            if cmd == True:
                success = success + 1
                TextSuccess['text'] = 'Success: %s' % success
                ProgessBarTabOne['value'] = success
                root.update()
            else:
                fails = fails + 1
                TextFailed['text'] = 'Fails: %s' % fails
                ProgessBarTabOne['value'] += 1
                root.update()

        for thread in threads:
            thread.start()
        finishRun(start_time)

root = tk.Tk()
style = ttk.Style()
root.iconbitmap("./icon/seeker_icon.ico")
root.title("Tab Widget")
tabControl = ttk.Notebook(root)

root.title('SeaFrog v1.0')
root.geometry("850x650")
root.resizable(False, False)

tab1 = ttk.Frame(tabControl)
tab2 = ttk.Frame(tabControl)

tabControl.add(tab1, text='Views bot')
tabControl.add(tab2, text='About')

Banner = ImageTk.PhotoImage(Image.open('./icon/banner.png'))
LabelImage = ttk.Label(tab1, image=Banner)
UrlLabelTabOne = ttk.Label(tab1, text='Enter Your URL:')
UrlTabOne = ttk.Entry(tab1, width=50)
UrlTabOne.insert(0, 'https://example.com') # Placeholder
TimesLabelTabOne = ttk.Label(tab1, text='Views:')
TimesTabOne = ttk.Entry(tab1, width=10)
TimesTabOne.insert(1, '100') # Placeholder

checkVar = tk.IntVar()
ThreadingTabOne = ttk.Checkbutton(tab1, text='Multi-Threading (beta)', onvalue=1, offvalue=0, variable=checkVar)

ClearAll = ttk.Button(tab1, text='Clear', command=clearAll)
StartBtnTabOne = ttk.Button(tab1, text='Start', command=checkViewUrl)
ResetProxy = ttk.Button(tab1, text='Reset proxy', command=resetProxy)

ProgessBarTabOne = ttk.Progressbar(tab1, orient='horizontal', mode='determinate', length=400, value=0)
TextFailed = ttk.Label(tab1, text='Failed: 0')
TextSuccess = ttk.Label(tab1, text='Success: 0')
TimeCount = ttk.Label(tab1, text='Time: none')
ProgressStatus = ttk.Label(tab1, text='Status: none')
ProxyStatus = ttk.Label(tab1, text=f'Proxy: {proxies}')

LabelImage.place(relx=.5, rely=.1, anchor="center")

UrlLabelTabOne.place(relx=.3, rely=.3, anchor="center")
UrlTabOne.place(relx=.6, rely=.3, anchor="center")

TimesLabelTabOne.place(relx=.3, rely=.4, anchor="center")
TimesTabOne.place(relx=.6, rely=.4, anchor="center")

ThreadingTabOne.place(relx=.5, rely=.5, anchor="center")

ClearAll.place(relx=.4, rely=.6, anchor="center")
StartBtnTabOne.place(relx=.5, rely=.6, anchor="center")
ResetProxy.place(relx=.6, rely=.6, anchor="center")

ProgessBarTabOne.place(relx=.5, rely=.7, anchor="center")

TextFailed.place(relx=.4, rely=.8, anchor="center")
TextSuccess.place(relx=.6, rely=.8, anchor="center")
TimeCount.place(relx=.3, rely=.9, anchor="center")
ProgressStatus.place(relx=.5, rely=.9, anchor="center")
ProxyStatus.place(relx=.7, rely=.9, anchor="center")

# Second tab

nowarranty = '''No Warranty. LICENSOR PROVIDES THE LICENSED PROPERTY AND LICENSED IP ON AN "AS IS" BASIS WITHOUT WARRANTY OF ANY KIND. LICENSOR EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, WITH RESPECT TO THE LICENSED PROPERTY AND LICENSED IP, INCLUDING ALL IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, NONINFRINGEMENT, TITLE AND FITNESS FOR A PARTICULAR PURPOSE, OR ARISING OUT OF A COURSE OF DEALING, USAGE OR TRADE PRACTICE. LICENSOR SPECIFICALLY DISCLAIMS ANY WARRANTY THAT THE FUNCTIONS CONTAINED IN THE IT PROPERTY WILL MEET ASPENTECH'S REQUIREMENTS OR WILL OPERATE IN COMBINATIONS OR IN A MANNER SELECTED FOR USE BY ASPENTECH, OR THAT THE OPERATION OF ANY LICENSED PROPERTY WILL BE UNINTERRUPTED OR ERROR FREE.'''

Info = ttk.Label(tab2, text='Created by @cr4sh https://github.com/cr4sh/seafrog')
Warranty = tk.Text(tab2, height=8, width=80)
scroll_bar = tk.Scrollbar(tab2)

Warranty.config(state='normal')
Warranty.insert(tk.END, nowarranty)
Warranty.config(state='disabled')

scroll_bar.config(command=Warranty.yview)

Info.place(relx=.5, rely=.2, anchor="center")
Warranty.place(relx=.5, rely=.5, anchor="center")
scroll_bar.place(relx=.9, rely=.5, anchor="center")

tabControl.pack(expand=1, fill='both')

# Set proxy and useragent for the first time
resetProxy()

root.mainloop()


#ethan pham id 2384312
#stores first name and age of friends in text file
#each entry is written to its own line in text file

def main():
    #open file for writing
    myfile = open('fightformyfriends.txt', 'w')

    #initialize friend count variable
    friendCount = 0

    #use while loop to have maximum of four friends to write data to
    while friendCount < 4:
        #prompt for name
        name = input("Enter name of friend (<Enter> to quit): ")
        #prompt for age
        age = int(input("Enter age (integer) of friend: "))
        #write name and print new line for age
        myfile.write(name + '\n')
        #write age and print new line for next name
        myfile.write(str(age) + '\n')
        #increments count variable
        friendCount += 1

    #close file
    myfile.close()
    print("File created.")

#call main function
main()


'''
Description:
version:
Author: Data Designer
Date: 2021-08-10 21:05:32
LastEditors: Data Designer
LastEditTime: 2021-08-10 21:18:13
'''
#
# @lc app=leetcode.cn id=187 lang=python3
#
# [187] 重复的DNA序列
#

# @lc code=start
class Solution:
    def findRepeatedDnaSequences(self, s: str) -> List[str]:
        seen = set()
        res = set()
        win = 10
        N = len(s)
        for i in range(N-win+1):
            tmp = s[i:i+win]
            if tmp in seen:
                res.add(tmp[:])
            seen.add(tmp)
        return list(res)

# @lc code=end


from enum import Enum, EnumMeta
from six import with_metaclass

class _CaseInsensitiveEnumMeta(EnumMeta):
    def __getitem__(self, name):
        return super().__getitem__(name.upper())

    def __getattr__(cls, name):
        """Return the enum member matching `name`
        We use __getattr__ instead of descriptors or inserting into the enum
        class' __dict__ in order to support `name` and `value` being both
        properties for enum members (which live in the class' __dict__) and
        enum members themselves.
        """
        try:
            return cls._member_map_[name.upper()]
        except KeyError:
            raise AttributeError(name)

class CostThresholdStatus(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Indicates whether this threshold will be displayed on cost charts.
    """

    ENABLED = "Enabled"
    DISABLED = "Disabled"

class CostType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The type of the cost.
    """

    UNAVAILABLE = "Unavailable"
    REPORTED = "Reported"
    PROJECTED = "Projected"

class CustomImageOsType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The OS type of the custom image (i.e. Windows, Linux)
    """

    WINDOWS = "Windows"
    LINUX = "Linux"
    NONE = "None"

class EnableStatus(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """If notifications are enabled for this schedule (i.e. Enabled, Disabled).
    """

    ENABLED = "Enabled"
    DISABLED = "Disabled"

class EnvironmentPermission(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The access rights to be granted to the user when provisioning an environment
    """

    READER = "Reader"
    CONTRIBUTOR = "Contributor"

class FileUploadOptions(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Options for uploading the files for the artifact. UploadFilesAndGenerateSasTokens is the
    default value.
    """

    UPLOAD_FILES_AND_GENERATE_SAS_TOKENS = "UploadFilesAndGenerateSasTokens"
    NONE = "None"

class HostCachingOptions(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Caching option for a data disk (i.e. None, ReadOnly, ReadWrite).
    """

    NONE = "None"
    READ_ONLY = "ReadOnly"
    READ_WRITE = "ReadWrite"

class HttpStatusCode(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The status code for the operation.
    """

    CONTINUE_ENUM = "Continue"
    SWITCHING_PROTOCOLS = "SwitchingProtocols"
    OK = "OK"
    CREATED = "Created"
    ACCEPTED = "Accepted"
    NON_AUTHORITATIVE_INFORMATION = "NonAuthoritativeInformation"
    NO_CONTENT = "NoContent"
    RESET_CONTENT = "ResetContent"
    PARTIAL_CONTENT = "PartialContent"
    MULTIPLE_CHOICES = "MultipleChoices"
    MOVED_PERMANENTLY = "MovedPermanently"
    REDIRECT = "Redirect"
    SEE_OTHER = "SeeOther"
    NOT_MODIFIED = "NotModified"
    USE_PROXY = "UseProxy"
    UNUSED = "Unused"
    TEMPORARY_REDIRECT = "TemporaryRedirect"
    BAD_REQUEST = "BadRequest"
    UNAUTHORIZED = "Unauthorized"
    PAYMENT_REQUIRED = "PaymentRequired"
    FORBIDDEN = "Forbidden"
    NOT_FOUND = "NotFound"
    METHOD_NOT_ALLOWED = "MethodNotAllowed"
    NOT_ACCEPTABLE = "NotAcceptable"
    PROXY_AUTHENTICATION_REQUIRED = "ProxyAuthenticationRequired"
    REQUEST_TIMEOUT = "RequestTimeout"
    CONFLICT = "Conflict"
    GONE = "Gone"
    LENGTH_REQUIRED = "LengthRequired"
    PRECONDITION_FAILED = "PreconditionFailed"
    REQUEST_ENTITY_TOO_LARGE = "RequestEntityTooLarge"
    REQUEST_URI_TOO_LONG = "RequestUriTooLong"
    UNSUPPORTED_MEDIA_TYPE = "UnsupportedMediaType"
    REQUESTED_RANGE_NOT_SATISFIABLE = "RequestedRangeNotSatisfiable"
    EXPECTATION_FAILED = "ExpectationFailed"
    UPGRADE_REQUIRED = "UpgradeRequired"
    INTERNAL_SERVER_ERROR = "InternalServerError"
    NOT_IMPLEMENTED = "NotImplemented"
    BAD_GATEWAY = "BadGateway"
    SERVICE_UNAVAILABLE = "ServiceUnavailable"
    GATEWAY_TIMEOUT = "GatewayTimeout"
    HTTP_VERSION_NOT_SUPPORTED = "HttpVersionNotSupported"

class LinuxOsState(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The state of the Linux OS (i.e. NonDeprovisioned, DeprovisionRequested, DeprovisionApplied).
    """

    NON_DEPROVISIONED = "NonDeprovisioned"
    DEPROVISION_REQUESTED = "DeprovisionRequested"
    DEPROVISION_APPLIED = "DeprovisionApplied"

class NotificationChannelEventType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The event type for which this notification is enabled (i.e. AutoShutdown, Cost)
    """

    AUTO_SHUTDOWN = "AutoShutdown"
    COST = "Cost"

class PolicyEvaluatorType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The evaluator type of the policy (i.e. AllowedValuesPolicy, MaxValuePolicy).
    """

    ALLOWED_VALUES_POLICY = "AllowedValuesPolicy"
    MAX_VALUE_POLICY = "MaxValuePolicy"

class PolicyFactName(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The fact name of the policy (e.g. LabVmCount, LabVmSize, MaxVmsAllowedPerLab, etc.
    """

    USER_OWNED_LAB_VM_COUNT = "UserOwnedLabVmCount"
    USER_OWNED_LAB_PREMIUM_VM_COUNT = "UserOwnedLabPremiumVmCount"
    LAB_VM_COUNT = "LabVmCount"
    LAB_PREMIUM_VM_COUNT = "LabPremiumVmCount"
    LAB_VM_SIZE = "LabVmSize"
    GALLERY_IMAGE = "GalleryImage"
    USER_OWNED_LAB_VM_COUNT_IN_SUBNET = "UserOwnedLabVmCountInSubnet"
    LAB_TARGET_COST = "LabTargetCost"
    ENVIRONMENT_TEMPLATE = "EnvironmentTemplate"
    SCHEDULE_EDIT_PERMISSION = "ScheduleEditPermission"

class PolicyStatus(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The status of the policy.
    """

    ENABLED = "Enabled"
    DISABLED = "Disabled"

class PremiumDataDisk(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The setting to enable usage of premium data disks.
    When its value is 'Enabled', creation of standard or premium data disks is allowed.
    When its value is 'Disabled', only creation of standard data disks is allowed.
    """

    DISABLED = "Disabled"
    ENABLED = "Enabled"

class ReportingCycleType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Reporting cycle type.
    """

    CALENDAR_MONTH = "CalendarMonth"
    CUSTOM = "Custom"

class SourceControlType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The artifact source's type.
    """

    VSO_GIT = "VsoGit"
    GIT_HUB = "GitHub"

class StorageType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The storage type for the disk (i.e. Standard, Premium).
    """

    STANDARD = "Standard"
    PREMIUM = "Premium"
    STANDARD_SSD = "StandardSSD"

class TargetCostStatus(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Target cost status
    """

    ENABLED = "Enabled"
    DISABLED = "Disabled"

class TransportProtocol(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The transport protocol for the endpoint.
    """

    TCP = "Tcp"
    UDP = "Udp"

class UsagePermissionType(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The permission policy of the subnet for allowing public IP addresses (i.e. Allow, Deny)).
    """

    DEFAULT = "Default"
    DENY = "Deny"
    ALLOW = "Allow"

class VirtualMachineCreationSource(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """Tells source of creation of lab virtual machine. Output property only.
    """

    FROM_CUSTOM_IMAGE = "FromCustomImage"
    FROM_GALLERY_IMAGE = "FromGalleryImage"
    FROM_SHARED_GALLERY_IMAGE = "FromSharedGalleryImage"

class WindowsOsState(with_metaclass(_CaseInsensitiveEnumMeta, str, Enum)):
    """The state of the Windows OS (i.e. NonSysprepped, SysprepRequested, SysprepApplied).
    """

    NON_SYSPREPPED = "NonSysprepped"
    SYSPREP_REQUESTED = "SysprepRequested"
    SYSPREP_APPLIED = "SysprepApplied"


#!/usr/bin/env python

#
# copy firmware to remote octopi host with marlin usb connection
#

import os
import sys
import time
import logging
import paramiko
import argparse
import functools
import subprocess

logging.basicConfig(
    level=logging.INFO,
    datefmt='%Y-%m-%d %H:%M:%S',
    format='%(asctime)s %(levelname)-8s %(message)s',
)

logger = logging.getLogger(__name__)

parser = argparse.ArgumentParser()
parser.add_argument('--user_name', dest="user_name", default="pi")
parser.add_argument('--host_name', dest="host_name", default="octopi2")
parser.add_argument('--disk_label', dest="disk_label", default="MKS-BASE")
parser.add_argument('--firmware_path', dest="firmware_path", required=True)

params = parser.parse_args()

logger.info(f"params report:")
for key, value in vars(params).items():
    logger.info(f"   {key}={value}")

def trace(func):

    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        id = func.__name__
        try:
            logger.info(f"{id}")
            return func(*args, *kwargs)
        except Exception as error:
            logger.exception(error)
            raise error

    return wrapper

@trace
def disk_find_name(label:str) -> str:
    "resolve disk label into disk name"
    result = invoke_ssh(f"sudo lsblk -n -l -o name,label | grep {label} | head -n 1")
    if result.returncode == 0:
        part_list = result.stdout.split()
        if len(part_list) == 2:
            disk_name = part_list[0].decode('utf-8')
            return disk_name
        else:
            return None
    else:
        return None

@trace
def disk_has_point(disk_point:str) -> bool:
    "verify if mount point is present"
    result = invoke_ssh(f"cat /proc/mounts | grep {disk_point}")
    return result.returncode == 0

@trace
def disk_point_create(disk_point) -> None:
    invoke_ssh(f"mkdir -p {disk_point}")

@trace
def disk_point_delete(disk_point) -> None:
    invoke_ssh(f"rm -rf {disk_point}")

@trace
def disk_file_copy(local_path:str, remote_dir:str) -> None:
    local_base = os.path.basename(local_path)
    source_path = local_path
    target_path = os.path.join(remote_dir, local_base)
    invoke_scp(source_path, target_path)

@trace
def disk_point_mount(disk_point:str, disk_path:str) -> None:
    if disk_has_point(disk_point) is False:
        invoke_ssh(f"sudo mount {disk_path} {disk_point} -o uid=$USER,gid=$USER")

@trace
def disk_point_unmount(disk_point:str) -> None:
    if disk_has_point(disk_point) is True:
        invoke_ssh(f"sudo umount {disk_point}")

@trace
def disk_setup(disk_name:str, firmware_path:str) -> None:
    disk_path = f"/dev/{disk_name}"
    disk_point = f"/tmp/disk-point/{disk_name}"
    disk_point_unmount(disk_point)
    disk_point_create(disk_point)
    disk_point_mount(disk_point, disk_path)
    disk_file_copy(firmware_path, disk_point)
    disk_point_unmount(disk_point)
    disk_point_delete(disk_point)

def shell(script):
    result = subprocess.run(script, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    return result

def has_host(host_name):
    result = shell(f"ping -c 1 {host_name}")
    return result.returncode == 0

class Result() :
    returncode = 0
    stdout = ""
    stderr = ""

def invoke_ssh(script):
    stdin, stdout, stderr = ssh_client.exec_command(script)
    stdin.flush()
    result = Result()
    result.returncode = stdout.channel.recv_exit_status()
    result.stdout = stdout.read()
    result.stderr = stderr.read()
    return result

def invoke_scp(source_path, target_path):
    ftp_client = ssh_client.open_sftp()
    ftp_client.put(source_path, target_path)
    ftp_client.close()

#
#
#

if not has_host(params.host_name):
    logger.info(f"missing host={params.host_name}")
    sys.exit()

ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(username=params.user_name, hostname=params.host_name,)

disk_name = disk_find_name(params.disk_label)

if disk_name is None:
    logger.info(f"missing disk={params.disk_label}")
    sys.exit()

disk_setup(disk_name, params.firmware_path)


# -*- coding: utf-8 -*-

import sys
from colorama import Fore as clr, Style, Back

if sys.platform.lower().startswith(('os', 'win', 'darwin', 'ios')):
    cyan = green = yellow = red = bright = blue = reset = backred = progress = magenta = ''
else:
    cyan = clr.CYAN
    green = clr.LIGHTGREEN_EX
    yellow = clr.LIGHTYELLOW_EX
    red = clr.RED
    bright = Style.BRIGHT
    blue = clr.BLUE
    reset = Style.RESET_ALL
    backred = Back.RED
    progress = ("[" + blue + bright +'INF' + reset + "] ")
    magenta = clr.LIGHTMAGENTA_EX

banner = f"""{bright}            ___
          ___H___
 _____ _____[{backred}%{reset}{bright}]_____         _ _
|     | __  [{backred}\{reset}{bright}]   __|___ _ _|_| |_ ___
|   --|    -[{backred}#{reset}{bright}]   __|_ -| | | |  _| -_|
|_____|__|__[{backred};{reset}{bright}]__|  |___|___|_|_| |___|
             V                        v2.5.2
                {reset}({green}\x1B[3mBy Nefcore Security\x1B[0m{reset})
"""

def good(msg):
    """
    if URL is vulnerable
    """
    print("[" + green + bright +'VLN' + reset + '] '+msg)

def bad(msg):
    """
    if URL is not vulnerable
    """
    print("[" + magenta + bright + 'NOT VLN' + reset + '] '+msg)

def error(msg):
    """
    if any error occurs
    """
    print("[" + red + bright + 'ERR' + reset + ']',msg)

def info(msg):
    """
    basic informations
    """
    print("[" + blue + bright +'INF' + reset + "] "+msg)

def warn(msg):
    """
    warnings
    """
    print("[" + yellow + bright +'WRN' + reset + "] "+msg)

def args_info(url, urls, stdin, nout, jout, method, data, cookies, timeout, verify, threads, verbosity, delay):
    """
    Prints information about user supplied arguments
    """
    if url:
        print(':: TARGET     : %s' % url)
    elif urls:
        print(':: TARGET     : %s' % urls)
    elif stdin:
        print(':: TARGET     : Stdin')
    else:
        print(':: TARGET     : None')
    if nout:
        print(':: OUTPUT     : %s' % nout)
    elif jout:
        print(':: OUTPUT     : %s' % jout)

    print(':: THREADS    : %i' % threads)
    print(':: METHOD     : %s' % method)

    if data:
        print(':: DATA       : %s' % data)

    print(':: Delay      : %s' % delay)

    if cookies:
        print(':: COOKIES    : %s' % cookies)

    print(':: TIMEOUT    : %i' % timeout)

    if verify:
        print(':: VERIFY     : True')
    else:
        print(':: VERIFY     : False')
    if verbosity:
        print(':: Verbosity  : %i' % verbosity)
    print('')


# Создайте программу для игры с конфетами человек против человека.
# Условие задачи: На столе лежит 2021 конфета. Играют два игрока делая ход друг после друга. Первый ход определяется жеребьёвкой.
# За один ход можно забрать не более чем 28 конфет. Все конфеты оппонента достаются сделавшему последний ход.
# Сколько конфет нужно взять первому игроку, чтобы забрать все конфеты у своего конкурента?
# Добавьте игру против бота
# Подумайте как наделить бота "интеллектом"
from random import randint

def who_first():
    print("Определяем, какой игрок ходит первым:")
    person = randint(0, 1)
    if person:
        print("Первым ходит игрок 2!")
    else:
        print("Первым ходит игрок 1!")
    return person

def is_right(number: str):
    result = number.isdigit() and int(number) > 0 and int(number) < 29
    return result

def is_right_option(number: str):
    result = number.isdigit() and int(number) > 0 and int(number) < 3
    return result

def change_person(person):
    person = int(not bool(person))
    return person

def game_persons(candies, person):
    print(f'Ход игрока {person+1}.', end=" ")
    number = input("Введите число от 1 до 28: ")
    while not is_right(number):
        print('Введены неверные данные!')
        number = input("Введите число от 1 до 28: ")
    number = int(number)
    candies = candies - number
    print(f'Осталось {candies} конфет.')
    return candies

def play_the_game_human():
    person = who_first()
    candies = 2021
    print(f'На столе {candies} конфет.')
    while candies > 0:
        candies = game_persons(candies, person)
        person = change_person(person)
    person = change_person(person)
    print(f'Победа!!! Выиграл игрок {person+1}!!!')

def who_first_bot():
    print("Определяем, кто ходит первым:")
    person = randint(0, 1)
    if person:
        print("Первым ходит игрок 1!")
    else:
        print("Первым ходит бот!")
    return person

def find_numb_bot(candies):
    if candies % 29:
        number = candies % 29
    else:
        # т.к. 0 конфет брать нельзя, вщзьмем рандомное число
        number = randint(1, 29)
    return number

def game_bot(candies, person):
    if person:
        print(f'Ход игрока.', end=" ")
        number = input("Введите число от 1 до 28: ")
        while not is_right(number):
            print('Введены неверные данные!')
            number = input("Введите число от 1 до 28: ")
        number = int(number)
    else:
        print(f'Ход бота.', end=" ")
        number = find_numb_bot(candies)
        print(f"Введите число от 1 до 28: {number}")

    candies = candies - number
    print(f'Осталось {candies} конфет.')
    return candies

def play_the_game_bot():
    person = who_first_bot()
    candies = 2021
    print(f'На столе {candies} конфет.')
    while candies > 0:
        candies = game_bot(candies, person)
        person = change_person(person)
    person = change_person(person)
    if person:
        print('Победа!!! Выиграл игрок!!!')
    else:
        print('Победа!!! Выиграл бот!!!')

def start_game():
    print('Выберите режим игры: 1 - между игроками, 2 - с ботом.')
    select_option = input('Режим: ')
    while not is_right_option(select_option):
        print('Введены неверные данные!')
        select_option = input("Введите число 1 или 2: ")
    select_option = int(select_option)
    if select_option == 1:
        print('Выбран режим игры между игроками.')
        play_the_game_human()
    else:
        print('Выбран режим игры с ботом.')
        play_the_game_bot()

start_game()


#Desenvolva um programa que faça a tabuada de um número qualquer inteiro que será digitado pelo usuário,
# mas a tabuada não deve necessariamente iniciar em 1 e terminar em 10, o valor inicial e final devem ser informados também pelo usuário

num = int(input("Digite o número que você deseja saber a tabuada: "))

ini = 5
fim = 1
i = 1

while ini>fim:

    ini = int(input("Digite onde você deseja iniciar a tabuada: "))
    fim = int(input("Digite onde você deseja terrminar a tabuada: "))

    if ini>fim:
        print("Número inicial maior que o final, digite novamente")

    else:
        print("A tabuada do número ", num)
        while ini<=fim:
            print('{0} x {1} = {2}'.format(num,ini,(num*ini)))
            ini += 1
        break


class Solution:
    def findMaxAverage(self, nums: List[int], k: int) -> float:

        sums = sum(nums[0:k])
        ans = sums/k
        l = 0
        r = k
        while r < len(nums):
          sums -= nums[l]
          sums += nums[r]
          ans = max(ans, sums/k)

          l += 1
          r += 1

        return ans


################## Libraries ##################
import serial
import struct
import time
import board
import busio
import adafruit_gps
'''#########################################'''

################# Add. Codes ##################
from HaversineFunction import haversine
from BearingAngleFunction import calculate_initial_compass_bearing
'''#########################################'''

#################################### Motors Setup #################################
rightMotor = 0                       ##Motor Values should start at 0
leftMotor = 0
rightMotorTrim = 0                   ##Trim Motor Values (they may not be the same strength out of the box
leftMotorTrim = 0
maxSpeed = 100                       ##Max Value as a percent. The motors may be much stronger than we need them to be
turnFactor = 5                       ##Divide max speed by this factor to slow down turns
headingRange = 10                     ##Allowable Tolerance in degrees
'''#############################################################################'''

################################### Heading Setup #################################
currentHeading = 0                   ##Current Heading of Boat
requiredHeading = 0                  ##Current Required heading to go to the dock
waypointDistance = 0                 ##Distance from boat to waypoint
maxRange = 18                      ##Max Accuaracy of GPS in feet
'''#############################################################################'''

##################################### IMU Setup ###################################
IMU = serial.Serial(
    port='/dev/ttyACM1',
    baudrate=115200,
    parity=serial.PARITY_ODD,
    stopbits=serial.STOPBITS_TWO,
    bytesize=serial.SEVENBITS
)
print (IMU.name)
IMU.isOpen()

total = 0
averageCounter = 0
averageHeading = 0

command = "#o1"
print(command)
send = bytes(command, 'utf-8')
IMU.write(send)
print("IMU Ready")

'''#############################################################################'''

################################## Serial out Setup ###############################

Arduino = serial.Serial('/dev/ttyACM0')  ##Define a Serial Port
print (Arduino.name)                     ##Print The Serial Port
Arduino.baudrate = 115200                ##Define the Baudrate. Must Match Arduino Code
time.sleep(2)                        ##Wait for the Serial Communication to begin
string = ''                          ##Creates an empty string to send over Serial
startMark = '<'
endMark = '/'
delimiter = ','                      ##Creates a demiliter for arduino to split the values
status = 0;                          ##Currently used by the docking code in arduino.
                                     ##If 0, run normally, If 1, ignore Python motor Values
'''#############################################################################'''

##################################### GPS Setup ###################################
waypoint_lat = 30.545517
waypoint_long = -87.216602
uart = serial.Serial("/dev/ttyS0", baudrate=9600, timeout=3000)
print(uart.port)
gps = adafruit_gps.GPS(uart, debug=False)
gps.send_command(b'PMTK314,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0')
gps.send_command(b'PMTK220,1000')
last_print = time.monotonic()
'''#############################################################################'''

def getCurrentHeading():

    #print("IMU")
    IMU.flushInput()
    IMU.flushOutput()
    time.sleep(.1)
    line = IMU.readline().strip()
    strLine = str(line.decode('utf-8'))
    if(strLine):
        rawHeading = float(strLine)
    try:
        calHeading = rawHeading
    except:
        calHeading = 0
        pass
    #print("calHeading = " , calHeading)
    return calHeading

def updateMotors():
    #print("updateMotors")

    angularDiff = angle - getCurrentHeading() ##Final -Initial
    #print("Angle = ", angle)
    if (abs(angularDiff) > headingRange):            ##If we are outside of the set tolerance:

        if (abs(angularDiff) > 180):                  ##If angular difference is greater than 180, optimize turn:
            if (angularDiff >= 0):
                angularDiff = angularDiff - 360
                print("angular Diff = ", angularDiff)
            else:
                angularDiff = angularDiff + 360

            if (angularDiff > 0):                      ##If new angular diff is greater than 0 turn RIGHT
                rightMotor = -maxSpeed*abs(angularDiff/180)
                leftMotor = maxSpeed*abs(angularDiff/180)
            else:                                    ##Else angular diff is less than 0 turn LEFT
                rightMotor = maxSpeed*abs(angularDiff/180)
                leftMotor = -maxSpeed*abs(angularDiff/180)

        elif (abs(angularDiff) == 180):            ##Else If angular difference is 180, turn RIGHT
                rightMotor = -maxSpeed
                leftMotor = maxSpeed

        else:                                        ##Else no optimization is required
            if (angularDiff > 0):                       ##If required > current turn right
                rightMotor = -maxSpeed*abs(angularDiff/180)
                leftMotor = maxSpeed*abs(angularDiff/180)
            else:                                     ##Else required < current turn left
                rightMotor = maxSpeed*abs(angularDiff/180)
                leftMotor = -maxSpeed*abs(angularDiff/180)
    else:                                           ##Else if we are in the set tolerance FULL SPEED AHEAD
        rightMotor = maxSpeed
        leftMotor = maxSpeed

    if(abs(rightMotor) < 20):
        if(rightMotor > 0):
            rightMotor = 20
        if(rightMotor < 0):
            rightMotor = -20

    if(abs(leftMotor) < 20):
        if(leftMotor > 0):
            leftMotor = 20
        if(leftMotor < 0):
            leftMotor = -20

    #print("angular Diff = ", angularDiff)
    return rightMotor, leftMotor

def getWayPointHeading():
    #print("getWaypointHeading")
    requiredHeading = calculate_initial_compass_bearing(longit, lat, waypoint_long, waypoint_lat)

    return requiredHeading

def getDistance():
    #print("getDistance")
    waypointDistance = haversine(longit, lat, waypoint_long, waypoint_lat)

    return waypointDistance

def checkStatus():
    #print("checkStatus")
    if distance > maxRange:
        status = 0
    else:
        status = 1

    return status

def toArduino():
    #time.sleep(2)   #must have a delay between sends.
    #print("ToArduino")
    rightMotor, leftMotor = updateMotors()
    #print(checkStatus(), " ", leftMotor, " ", rightMotor)
    string = startMark + str(checkStatus()) + delimiter + str(leftMotor) + delimiter + str(rightMotor) + endMark
    b = bytes(string, 'utf-8')
    print(b)
    Arduino.write(b)
    #print("Exit")

def newToArduino():
    time.sleep(1)
    string = startMark + str(status) + delimiter + str(leftMotor) + delimiter + str(rightMotor) + endMark
    #string = startMark + str(0) + delimiter + str(50) + delimiter + str(100) + endMark
    b = bytes(string, 'utf-8')
    print(b)
    Arduino.write(b)

while True:
    #print("GPS")
    #print(getCurrentHeading())

    # Make sure to call gps.update() every loop iteration and at least twice
    # as fast as data comes from the GPS unit (usually every second).
    try:
        gps.update()
    except:
        print("ERROR IN GPS CODE")
        pass

    # Every second print out current location details if there's a fix.
    #print("UGH")
    current = time.monotonic()
    if current - last_print >= 1.0:
        last_print = current
        if not gps.has_fix:
            # Try again if we don't have a fix yet.
            print('Waiting for fix...')
            continue
        # We have a fix! (gps.has_fix is true)
        # Print out details about the fix like location, date, etc.

        #print('=' * 40)  # Print a separator line.
        #print("loop")
        print('Latitude: {0:.6f} degrees'.format(gps.latitude))
        print('Longitude: {0:.6f} degrees'.format(gps.longitude))

        lat = float(gps.latitude)
        longit = float(gps.longitude)
        #print(type(lat))

        angle = getWayPointHeading()             ##Get heading from GPS
        #print("angle = ", angle)
        distance = getDistance()                 ##Get distance from GPS
        print("distance = ", distance)
        #getCurrentHeading()
        #toArduino()                              ##Send status and motor values to Arduino
        status = checkStatus()
        rightMotor,leftMotor = updateMotors()
        rightMotor = round(rightMotor)
        leftMotor = round(leftMotor)
        newToArduino()