import discord
import time
import asyncio
import traceback
import aiohttp
import aiofiles
import math

import humecord

from humecord.utils import (
    dateutils,
    discordutils,
    hcutils,
    miscutils,
    components
)

class DevCommand:
    def __init__(self):
        self.name = "dev"

        self.description = "All HumeCord development controls."

        self.aliases = ["development"]

        self.permission = "bot.dev"

        self.subcommands = {
            "error": {
                "function": self.error,
                "description": "Forces an error."
            },
            "reload": {
                "function": self.reload,
                "description": "Reloads the bot."
            },
            "close": {
                "function": self.close,
                "description": "Closes the bot."
            },
            "loops": {
                "function": LoopsSubcommand.run,
                "description": "Manages the bot's loops."
            },
            "exec": {
                "function": ExecSubcommand.run,
                "description": "Allows for execution of code."
            },
            "dm": {
                "function": DMSubcommand.run,
                "description": "DMs someone."
            },
            "profile": {
                "function": ProfileSubcommand.run,
                "description": "Manages the bot's profile."
            },
            "botban": {
                "function": BotBanSubcommand.run,
                "description": "Manages bot-banned users."
            }
        }

        self.shortcuts = {
            "loops": "dev loops",
            "loop": "dev loops",
            "exec": "dev exec",
            "dm": "dev dm",
            "reply": "dev dm reply",
            "reload": "dev reload",
            "close": "dev close",
            "botban": "dev botban",
            "literallyjustfuckingdieihateyousomuch": "dev close"
        }

        global bot
        from humecord import bot

    async def error(self, message, resp, args, gdb, alternate_gdb = None, preferred_gdb = None):
        raise humecord.utils.exceptions.TestException("dev.error call")

    async def reload(self, message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) > 2:
            safe = args[2].lower() not in ["f", "force"]

        else:
            safe = True

        await resp.send(
            embed = discordutils.create_embed(
                f"{bot.config.lang['emoji']['loading']}  Reloading bot...",
                description = f"Safe reload: `{'Yes' if safe else 'No'}`",
                color = "icon_blue"
            )
        )

        await bot.loader.load(safe_stop = safe)

        await resp.send(
            embed = discordutils.create_embed(
                f"{bot.config.lang['emoji']['success']}  Reloaded!",
                color = "success"
            )
        )

    async def close(self, message, resp, args, gdb, alternate_gdb, preferred_gdb):
        await resp.send(
            embed = discordutils.create_embed(
                f"{bot.config.lang['emoji']['loading']}  Shutting down...",
                color = "icon_blue"
            )
        )

        raise KeyboardInterrupt

class LoopsSubcommand:
    async def run(message, resp, args, gdb, alternate_gdb = None, preferred_gdb = None):
        # Map:
        # !dev loops
        #   list -> List loops & status
        #   run [loop] -> Force run loop
        #   pause [loop] (time/'forever') -> Pauses a loop for a set time
        #   reset [loop] -> Resets a loop (errors, time, all)

        if len(args) >= 3:
            action = args[2].lower()

            if action in ["list"]:
                await LoopsSubcommand.list_(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["run"]:
                await LoopsSubcommand.run_(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["pause"]:
                await LoopsSubcommand.pause(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["unpause"]:
                await LoopsSubcommand.unpause(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["reset"]:
                await LoopsSubcommand.reset(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

        await resp.send(
            embed = discordutils.error(
                message.author,
                "Invalid syntax!",
                f"Specify an action.\nValid actions: `list`, `run`, `pause`, `unpause`, `reset`"
            )
        )

    async def list_(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        # Compile list
        comp = []

        for loop in bot.loops.loops:
            details = [
                f"â€¢ Runs: `{hcutils.get_loop_runtime(loop)}`",
                f"â€¢ Last run: `{hcutils.get_loop_last_run(loop)}`"
            ]

            if loop.errors > 0:
                if loop.errors >= 3:
                    details.append(f"â€¢ Errors: `{loop.errors} (paused)`")

                else:
                    details.append(f"â€¢ Errors: `{loop.errors}`")

            if loop.pause_until:
                details.append(f"â€¢ Paused for: `{dateutils.get_duration(loop.pause_until - time.time())}`")

            comp.append(
                {
                    "name": f"â†’ {loop.name}",
                    "value": "\n".join(details)
                }
            )

        if len(comp) == 0:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Can't list loops!",
                    "No loops are active."
                )
            )

        else:
            await resp.send(
                embed = discordutils.create_embed(
                    f"Loops",
                    fields = comp,
                    color = "invisible"
                )
            )

    async def run_(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a loop to force run."
                )
            )
            return

        loop = LoopsSubcommand.get_loop(
            args[3].lower()
        )

        if loop is None:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid loop!",
                    f"Loop '{args[3].lower()}' does not exist."
                )
            )
            return

        humecord.bot.client.loop.create_task(
            humecord.utils.errorhandler.wrap(
                loop.run(),
                context = {
                    "Loop details": [
                        f"Loop: `{loop.name}`",
                        f"Manual call by: <@{message.author.id}>"
                    ]
                }
            )
        )

        await resp.send(
            embed = discordutils.create_embed(
                f"Ran loop {loop.name}",
                description = "Make sure to check the debug channel for errors - they will not be forwarded here.",
                color = "green"
            )
        )

    async def pause(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a loop to force run."
                )
            )
            return

        loop = LoopsSubcommand.get_loop(
            args[3].lower()
        )

        if loop is None:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid loop!",
                    f"Loop '{args[3].lower()}' does not exist."
                )
            )
            return

        if len(args) == 4:
            # Pause indefinitely
            loop.pause = True
            details = "Indefinitely"

        else:
            try:
                duration = dateutils.parse_duration(args[4])

            except Exception as e:
                await resp.send(
                    embed = discordutils.error(
                        message.author,
                        "Invalid duration!",
                        f"{str(e)}"
                    )
                )
                return

            # Add to pause_until
            loop.pause_until = int(time.time()) + int(duration)
            details = f"For {dateutils.get_duration(duration)}"

        await resp.send(
            embed = discordutils.create_embed(
                f"Paused loop {loop.name}.",
                description = f"Paused: `{details}`\n\nUnpause it with `{preferred_gdb['prefix']}dev loops unpause {loop.name}`.",
                color = "green"
            )
        )

    async def unpause(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a loop to force run."
                )
            )
            return

        loop = LoopsSubcommand.get_loop(
            args[3].lower()
        )

        if loop is None:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid loop!",
                    f"Loop '{args[3].lower()}' does not exist."
                )
            )
            return

        actions = []

        if loop.pause:
            loop.pause = False
            actions.append("â€¢ Removed indefinite pause")

        if loop.pause_until:
            actions.append(f"â€¢ Removed timer pause (was due to expire in {dateutils.get_duration(loop.pause_until - int(time.time()))})")
            loop.pause_until = None

        if loop.errors >= 3:
            actions.append(f"â€¢ Reset error counter (was at {loop.errors})")

        await resp.send(
            embed = discordutils.create_embed(
                f"Unpaused loop {loop.name}.",
                fields = [
                    {
                        "name": f"â†’ **Actions:**",
                        "value": "\n".join(actions)
                    }
                ],
                color = "green"
            )
        )

    async def reset(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a loop to force run."
                )
            )
            return

        loop = LoopsSubcommand.get_loop(
            args[3].lower()
        )

        if loop is None:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid loop!",
                    f"Loop '{args[3].lower()}' does not exist."
                )
            )
            return

        actions = []

        if loop.pause:
            loop.pause = False
            actions.append("â€¢ Removed indefinite pause")

        if loop.pause_until:
            actions.append(f"â€¢ Removed timer pause (was due to expire in {dateutils.get_duration(loop.pause_until - int(time.time()))})")
            loop.pause_until = None

        if loop.errors > 0:
            actions.append(f"â€¢ Reset error counter (was at {loop.errors})")

        if loop.type == "delay":
            actions.append(f"â€¢ Reset run delay (last run {dateutils.get_duration(int(time.time()) - loop.last_run)} ago)")
            loop.last_run = -1

        elif loop.type == "period":
            actions.append(f"â€¢ Reset last run time (last run on {humecord.bot.files.files['__loops__.json'][loop.name]['last_run']})")
            humecord.bot.files.files["__loops__.json"][loop.name]["last_run"] = "Never"
            humecord.bot.files.write("__loops__.json")

        await resp.send(
            embed = discordutils.create_embed(
                f"Reset loop {loop.name}.",
                fields = [
                    {
                        "name": f"â†’ **Actions:**",
                        "value": "\n".join(actions)
                    }
                ],
                color = "green"
            )
        )

    def get_loop(loop_name):
        for loop in humecord.bot.loops.loops:
            if loop.name == loop_name:
                return loop

class ExecSubcommand:
    async def run(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if message.author.id not in humecord.bot.config.eval_perms:
            await discordutils.error(
                message.author,
                "You can't use this subcommand!",
                "Exec permissions are manually defined in config.yml, and are separate from the dev list."
            )
            return

        if len(args) >= 3:
            action = args[2].lower()

            if action in ["list"]:
                await ExecSubcommand.list(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["cancel"]:
                await ExecSubcommand.cancel(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["eval"]:
                await ExecSubcommand.eval(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif " ".join(args[2:]).strip().startswith("```"):
                await ExecSubcommand.exec(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

        await resp.send(
            embed = discordutils.error(
                message.author,
                "Invalid syntax!",
                "Specify an action.\nValid actions: `list`, `eval`, `cancel` (or specify a function to run with \\```)."
            )
        )

    async def exec(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        function = " ".join(args[2:]).strip()

        if function.endswith("silent"):
            silent = True
            function = function.rsplit("silent", 1)[0].strip()

        else:
            silent = False

        # Remove backticks & whitespace
        function = function.strip("```").strip()

        func_spaced = "\n".join([f"    {x}" for x in function.split("\n")])

        # Store in globals
        exec(f"async def __run_exec(bot, message, resp, args, gdb, alternate_gdb, preferred_gdb):\n{func_spaced}", globals())

        # Create hex ID
        task_id = miscutils.generate_hexid(6)

        # Create task
        task = humecord.bot.client.loop.create_task(
            globals()["__run_exec"](humecord.bot, message, resp, args, gdb, alternate_gdb, preferred_gdb)
        )

        humecord.bot.mem_storage["evals"][task_id] = {
            "started": int(time.time()),
            "task": task,
            "silent": silent
        }

        counter = 0
        long_logged = False
        while not task.done():
            await asyncio.sleep(0.1)
            counter += 0.1

            # If it takes more than a second, we log a task start, and give
            # them the task ID so it can be cancelled.

            if counter >= 1 and not long_logged and not silent:
                await resp.send(
                    embed = discordutils.create_embed(
                        title = "Exec task created",
                        description = f"This task is taking a while. If you'd like to cancel it, use `{preferred_gdb['prefix']}dev exec cancel {task_id}`.",
                        color = "green",
                        footer = f"Task ID {task_id}"
                    )
                )
                long_logged = True

        try:
            result = str(task.result())

        except asyncio.CancelledError:
            if not bot.mem_storage["evals"][task_id]["silent"]:
                await resp.send(
                    embed = discordutils.create_embed(
                        title = f"{humecord.bot.config.lang['emoji']['error']} Exec task cancelled.",
                        footer = f"Task ID {task_id} | Cancelled after {round(time.time() - bot.mem_storage['evals'][task_id]['started'], 1)}s",
                        color = "error"
                    )
                )

            del bot.mem_storage["evals"][task_id]
            return # Will be handled by a "dev cancel" call

        except asyncio.InvalidStateError:
            return

        except:
            result = None
            pass

        exception = task.exception()

        if exception:
            try:
                raise exception

            except Exception as e:
                tb = traceback.format_exc()
                result_title = f"{humecord.bot.config.lang['emoji']['error']} Exec task raised an exception"
                result_name = "Exception"
                result_content = f"```py\n{tb[-1000:] + ('...' if len(tb) >= 1000 else '')}\n```"
                color = "error"

        elif result:
            result_title = f"{humecord.bot.config.lang['emoji']['success']} Exec complete!"
            result_name = "Result"
            result_content = f"```{result[:1000] + ('...' if len(result) >= 1000 else '')}```"
            color = "success"

        else:
            result_title = f"{humecord.bot.config.lang['emoji']['success']} Exec complete!"
            result_name = None
            result_content = None
            color = "success"

        del globals()["__run_exec"]

        if not bot.mem_storage["evals"][task_id]["silent"]: # Refresh it
            await resp.send(
                embed = discordutils.create_embed(
                    title = result_title,
                    fields = [
                        {
                            "name": "Function executed",
                            "value": f"```{function[:1000]}{'...' if len(function) >= 1000 else ''}```"
                        }
                    ] + ([
                        {
                            "name": result_name,
                            "value": result_content
                        }
                    ] if result_name else []),
                    color = color,
                    footer = f"Task ID {task_id} | Took {round(time.time() - bot.mem_storage['evals'][task_id]['started'], 1)}s"
                )
            )

        del bot.mem_storage["evals"][task_id]

    async def eval(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify an expression to evaluate."
                )
            )

        expression = " ".join(args[3:])

        try:
            result = str(eval(expression))

        except Exception as e:
            tb = traceback.format_exc()
            await resp.send(
                embed = discordutils.create_embed(
                    title = f"{humecord.bot.config.lang['emoji']['error']} Eval raised an exception",
                    fields = [
                        {
                            "name": "Expression evaluated",
                            "value": f"```{expression[:1000]}{'...' if len(expression) >= 1000 else ''}```"
                        },
                        {
                            "name": "Exception",
                            "value": f"```py\n{tb[:1000] + ('...' if len(tb) >= 1000 else '')}\n```"
                        }
                    ],
                    color = "error"
                )
            )
            return

        await message.channel.send(
            embed = discordutils.create_embed(
                f"{humecord.bot.config.lang['emoji']['success']} Eval complete!",
                fields = [
                    {
                        "name": "Expression evaluated",
                        "value": f"```{expression[:1000]}{'...' if len(expression) >= 1000 else ''}```"
                    },
                    {
                        "name": "Result",
                        "value": f"```{result[:1000] + ('...' if len(result) >= 1000 else '')}\n```"
                    }
                ],
                color = "success"
            )
        )

    async def cancel(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify exec tasks to cancel."
                )
            )
            return

        ids = args[3].split(",")

        if "all" in ids:
            ids = list(bot.mem_storage["evals"])

        if len(ids) == 0:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Couldn't cancel tasks.",
                    "There are no available tasks to cancel."
                )
            )
            return

        if len(args) > 4:
            silent = args[4].lower() in ['yes', "silent", "s", "true"]

        else:
            silent = False

        killed = []
        passed = []

        for task_id in ids:
            task_id = task_id.lower()
            if task_id in bot.mem_storage["evals"]:
                task_data = bot.mem_storage["evals"][task_id]
                bot.mem_storage["evals"][task_id]["silent"] = task_data["silent"] if not silent else True

                try:
                    task_data["task"].cancel()

                    killed.append(
                        {
                            "id": task_id,
                            "after": time.time() - task_data["started"],
                            "silent": task_data["silent"] if not silent else True
                        }
                    )

                except:
                    passed.append(
                        {
                            "id": task_id,
                            "reason": "Exception raised while attempting to cancel"
                        }
                    )

            else:
                passed.append(
                    {
                        "id": task_id,
                        "reason": "Task does not exist"
                    }
                )

        fields = []
        if len(killed) > 0:
            comp = []
            for task in killed:
                comp.append(f"`{task['id']}` | Cancelled after {round(task['after'], 1)}s of activity{' (silenced)' if task['silent'] else ''}")

            fields.append(
                {
                    "name": f"Task{'s' if len(comp) != 1 else ''} cancelled ({len(comp)})",
                    "value": "\n".join(comp)
                }
            )

        if len(passed) > 0:
            comp = []
            for task in passed:
                comp.append(f"`{task['id']}` | {task['reason']}")

            fields.append(
                {
                    "name": f"Couldn't cancel ({len(comp)})",
                    "value": "\n".join(comp)
                }
            )

        if len(killed) > 0 and len(passed) > 0:
            color = "warning"
            emoji = bot.config.lang['emoji']['warning']

        elif len(passed) > 0:
            color = "error"
            emoji = bot.config.lang['emoji']['error']

        else:
            color = "success"
            emoji = bot.config.lang['emoji']['success']

        await resp.send(
            embed = discordutils.create_embed(
                title = f"{emoji} Sent cancel request to {len(ids)} task{'s' if len(ids) != 1 else ''}",
                color = color,
                fields = fields
            )
        )

    async def list(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        # Compile list
        comp = []
        """
        humecord.bot.mem_storage["evals"][task_id] = {
            "started": int(time.time()),
            "task": task,
            "silent": silent
        }"""

        for eval_id, eval in bot.mem_storage["evals"].items():
            comp.append(
                {
                    "name": f"â†’ {eval_id}",
                    "value": f"â€¢ Running for: `{dateutils.get_duration(int(time.time()) - eval['started'])}`\nâ€¢ Silent: `{'yes' if eval['silent'] else 'no'}`"
                }
            )

        if len(comp) == 0:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Can't list active evals!",
                    "Nothing is being evaluated right now."
                )
            )
            return

        await resp.send(
            embed = discordutils.create_embed(
                f"Active evals ({len(comp)})",
                fields = comp,
                color = "invisible"
            )
        )

class DMSubcommand:
    async def run(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) >= 3:
            action = args[2].lower()

            if action in ["reply"]:
                await DMSubcommand.reply(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            else:
                await DMSubcommand.dm(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

        await resp.send(
            embed = discordutils.error(
                message.author,
                "Invalid syntax!",
                "Specify an action.\nValid actions: `reply` (or specify a user to DM)."
            )
        )

    async def dm(
            message,
            resp,
            args,
            gdb,
            alternate_gdb,
            preferred_gdb
        ):
        # dev dm [user id] [message]

        if len(args) < 3:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a user to DM."
                )
            )
            return

        # Get user ID
        if len(args) < 4:
            if len(message.attachments) == 0:
                await resp.send(
                    embed = discordutils.error(
                        message.author,
                        "Invalid syntax!",
                        "Specify a message to send, or attach something."
                    )
                )
                return

            else:
                msg_args = []

        else:
            msg_args = args[3:]

        await DMSubcommand.send_direct(
            args[2], # User ID
            message,
            resp,
            msg_args # Message
        )

    async def reply(
            message,
            resp,
            args,
            gdb,
            alternate_gdb,
            preferred_gdb
        ):

        if bot.mem_storage["reply"] is None:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Can't reply!",
                    "No one has messaged me recently."
                )
            )
            return

        # dev dm reply [message]

        # Get user ID
        if len(args) < 4:
            if len(message.attachments) == 0:
                await resp.send(
                    embed = discordutils.error(
                        message.author,
                        "Invalid syntax!",
                        "Specify a message to send, or attach something."
                    )
                )
                return

            else:
                msg_args = []

        else:
            msg_args = args[3:]

        await DMSubcommand.send_direct(
            bot.mem_storage["reply"], # User ID
            message,
            resp,
            msg_args # Message
        )

    async def send_direct(
            user_id,
            message,
            resp,
            msg_args
        ):

        # Get user
        try:
            user = discordutils.get_user(
                user_id,
                True,
                message
            )

        except Exception as e:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid user!",
                    str(e)
                )
            )
            return

        # Compile message
        # 1 - check if it's an embed
        if len(msg_args) != 0:
            if msg_args[0].lower() == "--embed":
                # No
                await resp.send(
                    embed = discordutils.error(
                        message.author,
                        "Not implemented yet!",
                        "This feature is disabled until I finish the embed parser."
                    )
                )
                return

                msg_args = msg_args[1:]

        # Check for attachments
        kw = {
            "content": ""
        }
        warn = []
        fields = []

        if len(message.attachments) > 0:
            # Try to attach

            attachment = message.attachments[0]

            attach_manual = False
            if attachment.size < 8000000:
                filename = f"data/tmp/{attachment.filename}"
                # Download & re-attach
                await resp.send(
                    embed = discordutils.create_embed(
                        "Downloading attachment...",
                        f"â€¢ **Name**: `{attachment.filename}`\nâ€¢ **Size**: `{miscutils.get_size(attachment.size)}`",
                        color = "yellow"
                    )
                )

                async with aiohttp.ClientSession(headers = {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11"}) as session:
                    async with session.get(attachment.url) as resp_:
                        if resp_.status == 200:
                            async with aiofiles.open(filename, mode = "wb") as f:
                                await f.write(await resp_.read())

                            # Attach
                            kw["file"] = discord.File(filename, attachment.filename)

                            fields.append(
                                {
                                    "name": "â†’ Attachment",
                                    "value": f"â€¢ **Name**: `{attachment.filename}`\nâ€¢ **Size**: `{miscutils.get_size(attachment.size)}`"
                                }
                            )

                        else:
                            attach_manual = True

            else:
                attach_manual = True

            if attach_manual:
                # Just append the link to content
                kw["content"] = f"{attachment.url}\n"
                fields.append(
                    {
                        "name": "â†’ Attachment (not embedded)",
                        "value": f"â€¢ **URL**: {attachment.url}\nâ€¢ **Name**: `{attachment.filename}`\nâ€¢ **Size**: `{miscutils.get_size(attachment.size)}`"
                    }
                )

        kw["content"] += " ".join(msg_args)

        if len(kw["content"]) > 2000:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Can't send message!",
                    f"Your message was {len(kw['content'])} characters long, which exceeds the limit of 2000. Shorten it and try again."
                )
            )
            return

        # Send it
        try:
            await user.send(
                **kw
            )

        except:
            traceback.print_exc()
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Failed to send DM!",
                    "Either the user blocked me, or I don't share mutual servers with them."
                )
            )
            return

        await resp.send(
            embed = discordutils.create_embed(
                f"Sent DM to {user.name}#{user.discriminator}!",
                description = f"â†’ **Content**:\n{kw['content'][:1900]}{'**...**' if len(kw['content']) > 1900 else ''}",
                fields = fields,
                color = "green"
            )
        )

class ProfileSubcommand:
    async def run(message, resp, args, gdb, alternate_gdb, preferred_gdb):
        if len(args) >= 3:
            action = args[2].lower()

            if action in ["reply"]:
                await DMSubcommand.reply(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            else:
                await DMSubcommand.dm(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

        await resp.send(
            embed = discordutils.error(
                message.author,
                "Invalid syntax!",
                "Specify an action.\nValid actions: `reply` (or specify a user to DM)."
            )
        )

class BotBanSubcommand:
    async def run(message, resp, args, gdb, alternate_gdb = None, preferred_gdb = None):
        # Map:
        # !dev botban
        #   list -> List banned users
        #   add [user] [duration] [description] -> Ban user
        #   remove [user] [duration] [description] -> Unban user

        if len(args) >= 3:
            action = args[2].lower()

            if action in ["list"]:
                await LoopsSubcommand.list_(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["add"]:
                await LoopsSubcommand.add(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

            elif action in ["remove", "del"]:
                await LoopsSubcommand.remove(message, resp, args, gdb, alternate_gdb, preferred_gdb)
                return

        await resp.send(
            embed = discordutils.error(
                message.author,
                "Invalid syntax!",
                f"Specify an action.\nValid actions: `list`, `add`, `remove`"
            )
        )

    async def list(message, resp, args, gdb, alternate_gdb = None, preferred_gdb = None):
        await BotBanSubcommand.generate_list(
            True, # Send new
            0, # Page 1
            message,
            resp,
            args,
            gdb,
            alternate_gdb,
            preferred_gdb
        )

    async def generate_list(
            gen_new,
            page,
            message,
            resp,
            args,
            gdb,
            alternate_gdb,
            preferred_gdb
        ):

        # Compile a list
        comp = []

        for uid, details in bot.files.files["__users__.json"]["blocked"].items():
            user = bot.client.get_user(user)

            if user is None:
                name = f"%-b% {uid}"
                u = [
                    f"  %-b% <@{uid}> (*unknown*)"
                ]

            else:
                name = f"%-a% {user.name}#{user.discriminator}",

                u = [
                    f"  %-b% <@{uid}>"
                    f"  %-b% Banned: `{dateutils.get_datetime(u['banned_on'])}`"
                ]

                if u['duration'] is None:
                    u.append(
                        f"  %-b% Duration: `Permanent`"
                    )

                else:
                    u.append(
                        f"  %-b% Duration: `{dateutils.get_duration(u['duration'])}`"
                    )

                if u['reason'] is not None:
                    u.append(
                        f"  %-b% Reason: `{u['reason'][:75]}{'...' if len(u['reason']) > 75 else ''}"
                    )

            comp.append(
                {
                    "name": name,
                    "value": "\n".join(u)
                }
            )

        if len(comp) == 0:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Can't list botbans!",
                    "No one is botbanned right now."
                )
            )

        # Find page bounds
        max_pages = math.floor(len(comp) / 15)

        if page > max_pages:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid page!",
                    f"Page {page + 1} exceeds max page of {max_pages}."
                )
            )

        # Determine page
        comp = comp[15 * page:15 * (page + 1)]

        embed = discordutils.create_embed(
            f"{bot.config.cool_name}'s botbanned users (Page {page + 1}/{max_pages})",
            fields = comp,
            color = "invisible"
        )

        if page == 0:
            backward_button = ["secondary", BotBanSubcommand.pass_]

        else:
            backward_button = ["primary", lambda *args: BotBanSubcommand.generate_list(False, int(page - 1), *args)]

        if page + 1 >= max_pages:
            forward_button = ["secondary", BotBanSubcommand.pass_]

        else:
            forward_button = ["primary", lambda *args: BotBanSubcommand.generate_list(False, int(page + 1), *args)]

        # Create view
        view = components.create_view(
            [
                components.create_button(
                    message,
                    label = "ðŸ¡¸",
                    id = "back",
                    style = backward_button[0],
                    callback = backward_button[1]
                ),
                components.create_button(
                    message,
                    label = "ðŸ¡º",
                    id = "forward",
                    style = forward_button[0],
                    callback = forward_button[1]
                )
            ]
        )

        # Send
        if gen_new:
            await resp.send(
                embed = embed,
                view = view
            )

        else:
            await resp.edit(
                embed = embed,
                view = view
            )

    async def pass_(*args):
        pass

    async def add(
            message,
            resp,
            args,
            gdb,
            alternate_gdb,
            preferred_gdb
        ):
        # Check args

        # dev botban add [user] [duration] [reason]
        if len(args) < 4:
            await resp.send(
                embed = discordutils.error(
                    message.author,
                    "Invalid syntax!",
                    "Specify a user, and optionally, a duration and reason.\nAdditionally, end the message with `-s` to not send a DM to the user."
                )
            )
            return

        try:
            user = discordutils.get_user(args[3])

        except:
            for char in "<@!>":
                user = user.replace(char, "")

            try:
                uid = int(user)

            except:
                await resp.send(
                    embed = discordutils.error(
                        message.author,
                        "Invalid user!",
                        f"Mention a user, or paste their ID."
                    )
                )
                return

            else:
                details = str(uid)

                dm = False

        else:
            # Get user's details
            uid = int(user.id)

            details = f"{user.name}#{user.discriminator} ({user.id})"

            dm = True

        if args[-1].lower() == "-s":
            silent = True
            args = args[:-1]

        else:
            silent = False

        # Check if duration is defined
        reason = "Not specified"
        try:
            duration = dateutils.get_duration(args[4])

            if len(args) > 5:
                reason = " ".join(args[5:])

        except:
            duration = None

            if len(args) > 4:
                reason = " ".join(args[4:])

        # Send to API
        details = await bot.api.put(
            "users",
            "blocked",
            {
                "id": uid,
                "reason": reason,
                "duration": duration
            }
        )

        # Check if we should dm
        if dm and (not silent):
            await user.send(
                **(
                    await bot.messages.get(
                        gdb,
                        [""],
                        {
                            "bot": bot.config.bot_name,
                            "duration": "Permanent" if duration is None else duration,
                            "reason": reason
                        }
                    )
                )
            )


import sys

sys.path.append("E:\PythonGUI-ManageEmployee\Pyqt5")
from PyQt5.QtWebEngineWidgets import QWebEngineView
from Algorithm.Cluster.Brute_Force import Brute_Force_Cluster
from Algorithm.Cluster.Randomized_Tour import Randomized_Tour_Cluster
from Process_Data.PyQT5_ProcessData import Process_Data_Small
from Process_Data.PyQT5_distanceMatrix import distance_Matrix
from decimal import Decimal, ROUND_DOWN
from Compare_Algorithm.PyQT5_Compare import Compare
import Map_.css_Map_Route as css
from dotenv import load_dotenv
import numpy as np
import os

class Show_Map_Cluster:
    def __init__(
        self,
        dataCenters,
        dataPoints,
        dataLayout,
        layout_Function,
        cb_TSP,
        on,
        off,
        data_Matrix,
        cb_cluster,
        cb_Start,
        data_Map,
    ):
        load_dotenv()
        self.api_Key = os.getenv("API_KEY")
        self.web_view = QWebEngineView()
        self.show_All_Compare = ""
        self.on_Compare = False
        self.values_Title = []
        self.distance = []
        self.all_Points = []
        self.data_Point_Clean = []
        self.distance_Matrix = []
        self.click_count = 0
        # self.change_Compare(layout_Function, dataCenters)
        self.show_Map_HTML(
            dataCenters, dataPoints, dataLayout, cb_TSP, on, off, data_Matrix, data_Map
        )

    def show_Map_HTML(
        self,
        dataCenters,
        dataPoints,
        dataLayout,
        cb_TSP,
        on,
        off,
        data_Matrix,
        data_Map,
    ):
        self.show_valueAllPoint(dataPoints)
        data_Center = [list(point) for point in dataCenters]
        data_Kg = self.show_DataKgM(dataPoints, data_Matrix)
        data_Points_ = np.array(self.data_Point_Clean, dtype=object)
        data_Kg_M3_Drops = np.array(data_Kg, dtype=object)
        data_Radius = self.show_radius_Cluster(data_Points_, dataCenters)
        distances_html = self.calculator_Distance_Trip(data_Points_)
        nearest_Neighbor = Process_Data_Small().use_NearestNeighbor(
            distances_html, data_Points_
        )
        self.show_HTMLContent(
            data_Center,
            data_Radius,
            data_Kg_M3_Drops,
            nearest_Neighbor,
            dataCenters,
            dataLayout,
            data_Map,
        )

        def show_Data_():
            self.web_view.hide()
            Compare(
                data_Points_,
                data_Center,
                cb_TSP,
                dataCenters,
                data_Kg_M3_Drops,
                dataLayout,
            )

        def show_Data_Cluster_Main():
            remove_layout()
            if dataLayout.count() == 1:
                self.web_view.show()

        def remove_layout():
            item = dataLayout.itemAt(1)
            if item:
                widget_to_delete = item.widget()
                if widget_to_delete:
                    dataLayout.removeWidget(widget_to_delete)
                    widget_to_delete.deleteLater()

        def on_Change_LayOut():
            self.click_count += 1
            if self.click_count % 2 == 1:
                show_Data_()
            else:
                show_Data_Cluster_Main()

    def show_DataKgM(self, dataPoints, data_Matrix):
        data_Kg = []
        array_Matrix = np.array(
            [
                [
                    item[2],
                    item[3],
                    float(item[5]),
                    float(item[6]),
                ]
                for item in data_Matrix
            ]
        )
        for items in dataPoints:
            data = []
            for value in items:
                lat = value[0]
                lon = value[1]
                for data_ in array_Matrix:
                    if lat == data_[2] and lon == data_[3]:
                        data.append(data_)
            data_Kg.append(data)
        self.data_Point_Clean = dataPoints
        return data_Kg

    def show_valueAllPoint(self, dataPoints):
        for data in dataPoints:
            for value in data:
                self.all_Points.append([value[0], value[1]])
        return self.all_Points

    def haversine_distance(self, coord1, coord2):
        R = 6371  # Bán kính của Trái Đất trong kilômét
        lat1, lon1 = np.radians(list(coord1))
        lat2, lon2 = np.radians(list(coord2))

        dlat = lat2 - lat1
        dlon = lon2 - lon1

        a = np.sin(dlat / 2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(dlon / 2) ** 2
        c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))

        distance = R * c
        return distance

    def show_radius_Cluster(self, data_point, data_center):
        radius = []
        for i, group in enumerate(data_point):
            max_distance = 0
            for point in group:
                distance = self.haversine_distance(point, data_center[i])
                if distance > max_distance:
                    max_distance = distance
            radius.append(max_distance)
        radius = np.array(radius) * 1000
        return radius

    def calculator_Distance_Trip(self, data_point):
        try:
            for value in data_point:
                distance_matrix = distance_Matrix.calculate_distances(value)
                self.distance_Matrix.append(distance_matrix)
        except ValueError as e:
            print("Check Log Error Distance", e)
        return self.distance_Matrix

    def show_Tree_Trip(self, data_point, distances, data_Center):
        list_Option = ""
        for index, value in enumerate(data_point):
            sum_kg = 0
            sum_m3 = 0
            for item in value:
                volume = item[0]
                weight = item[1]
                sum_kg += volume
                sum_m3 += weight
            list_Option += f"""

Trip {index+1}:

            KM:{distances[index]} - KG:{sum_kg.quantize(Decimal("1.000"), rounding=ROUND_DOWN)}
            - M3:{sum_m3.quantize(Decimal("1.000"), rounding=ROUND_DOWN)} -
            Drops:{len(self.calculator_Drops(value))} - Orders:{len(value)}

            """
        return list_Option

    def calculator_Drops(self, drops):
        data_Clean = []
        seen_coordinates = set()

        for item in drops:
            volume, weight, latitude, longitude = item
            coordinates = (latitude, longitude)

            if coordinates not in seen_coordinates:
                data_Clean.append(item)
                seen_coordinates.add(coordinates)
        return data_Clean

    def remove_layout(self, dataLayout):
        item = dataLayout.itemAt(0)
        if item:
            widget_to_delete = item.widget()
            if widget_to_delete:
                dataLayout.removeWidget(widget_to_delete)
                widget_to_delete.deleteLater()

    def show_HTMLContent(
        self,
        data_Center,
        data_Radius,
        data_Kg_M3_Drops,
        distances_html,
        dataCenters,
        dataLayout,
        data_Map,
    ):
        html_content = f"""
        <!DOCTYPE html>

                <title>Google Maps</title>
                {css.type_cs()}

                                    Nearest Neighbor

Trip: {len(data_Center)}

                                                    {self.show_Tree_Trip(data_Kg_M3_Drops,distances_html,dataCenters)}

            """
        if len(data_Map) > 4:
            self.web_view.setHtml(data_Map)
            self.remove_layout(dataLayout)
        else:
            self.web_view.setHtml(html_content)
        dataLayout.addWidget(self.web_view)


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

# pylint: disable=trailing-whitespace,bad-whitespace,invalid-name
# pylint: disable=anomalous-backslash-in-string,bad-continuation
# pylint: disable=multiple-statements,redefined-outer-name,global-statement

"""
FILE:  pyThreeD.py
DATE:  06 DEC 2023
AUTH:  G. E. Deschaines
DESC:  Three dimensional (3D) drawing of objects defined as collections
       of polygons.
REFS:

  [1] This Python script was refactored from threeD.c available at:
      https://github.com/gedeschaines/threeD/blob/master/src/threeD.c

Disclaimer:  See DISCLAIMER file.

"""

import sys
import warnings

warnings.simplefilter(action='ignore', category=FutureWarning)

try:
    import matplotlib.pyplot as plt
except ImportError:
    print("* Error: Matplotlib package required.")
    print("         Suggest installing the SciPy stack.")
    sys.exit()

from draw3D import Draw3D

global fig       # Instantiated matplotlib pyplot figure
global draw3D    # Instantiated Draw3D object
global started   # Draw3D.MainLoop method started flag

started = False

def onClick(event):
    """
    Mouse button pressed handler
    """
    global fig
    global draw3D
    global started

    #b = event.button
    #x = event.xdata
    #y = event.ydata
    if not started:
        started = True
        draw3D.MainLoop()
        if draw3D.doneflag:
            fig.clear()
            plt.close(fig)
            sys.exit()
        started = False

if __name__ == "__main__":

    global fig
    global draw3D

    # Set image save flag and rate (FPS).

    img_Save = False  # Save rendered images as PNG files
    img_FPS = 50;     # (semi-colon is hold over from threeD.c)

    # Process command line arguments.

    CaseId = None  # TXYZ.OUT.CaseId

    if len(sys.argv) > 2:
        CaseId   = sys.argv[1]
        img_Save = False if sys.argv[2] == "0" else True
    else:
        print("usage:  python3 pyThreeD.py CaseId [0|1]")
        print("where:  CaseId - read trajectory data from ./out/TXYZ.OUT.CaseId")
        print("        [0|1]  - save rendered image flag (0=False, 1=True)")
        sys.exit()

    File  = "./out/TXYZ.OUT." + CaseId
    title = "pyThreeD - " + File

    # Instantiate a matplotlib pyplot figure.

    #fig = plt.figure(figsize=(6.0,6.0), dpi=100.0)     # 465x462 viewport
    fig = plt.figure(figsize=(8.0,6.0), dpi=100.0)     # 620x462 viewport
    #fig = plt.figure(figsize=(8.0,8.0), dpi=100.0)     # 620x616 viewport
    #fig = plt.figure(figsize=(7.74,7.78), dpi=100.0)   # 600x599 viewport
    #fig = plt.figure(figsize=(10.0,8.0), dpi=100.0)    # 775x616 viewport
    #fig = plt.figure(figsize=(10.32,7.80), dpi=100.0)  # 800x601 viewport

    try:
        fig.set_tight_layout(True)
    except:
        fig.set_layout_engine('tight')

    # Specify plotting layout and parameters.

    ax = fig.add_subplot(111, autoscale_on=False, animated=False)
    ax.set_title(title)
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_aspect('equal')
    bbox = ax.bbox.get_points()
    w = round(bbox[1,0] - bbox[0,0])
    h = round(bbox[1,1] - bbox[0,1])
    #print("w= %d, h= %d" % (w, h))
    ax.set_xlim(0.0, w)
    ax.set_ylim(h, 0.0)
    ax.set_facecolor('#d8dcd6')  # light gray
    fig.canvas.draw()

    # Instantiate a Draw3D object.

    draw3D = Draw3D(txyzFile=File, w=w, h=h, fig=fig, ax=ax,
                    imgSave=img_Save, imgFPS=img_FPS)

    # Load object shape polygon data.

    draw3D.ReadPolyData()

    # Assign mouse button press handler.

    cidbtn = fig.canvas.mpl_connect('button_press_event', onClick)

    # Assign key press event handler.

    cidkey = fig.canvas.mpl_connect('key_press_event', draw3D.onPress)

    # Present instructions, then display figure window for rendering.

    print("With cursor in the displayed Figure, press a mouse button")
    print("to initiate threeD animation. If animation is exited or has")
    print("completed, close figure to terminate this program or press")
    print("a mouse button to restart animation. During threeD program")
    print("animation the following key presses are recognized:\n")

    print("Press T key to toggle field-of-view towards target.")
    print("Press M key to toggle field-of-view towards missile.")
    print("Press H key to toggle field-of-view along missile heading.")
    print("Press Z key to reset zoom to one.")
    print("Press Up Arrow key to increase zoom.")
    print("Press Down Arrow key to decrease zoom.")
    print("Press 0 (zero) key to reset animation step delay to zero.")
    print("Press Left Arrow key to slow animation down by 50 msec increments.")
    print("Press Right Arrow key to speed animation up by 50 msec increments.")
    print("Press Space key to toggle pause/unpause.")
    print("Press X key to exit animation (press a mouse button to restart).")
    print("Press Esc key to exit program.")

    plt.show(block=True)

    # Execution terminated, delete draw3D object and exit.

    del draw3D


import setuptools
with open("README.md", "r") as fh:
    long_description = fh.read()
setuptools.setup(
     name='aiopyql',
     version='PYQLVERSION',
     packages=setuptools.find_packages(include=['aiopyql'], exclude=['build']),
     author="Joshua Jamison",
     author_email="joshjamison1@gmail.com",
     description="A fast and easy-to-use asyncio ORM(Object-relational Mapper) for performing C.R.U.D. ops within RBDMS tables using python",
     long_description=long_description,
   long_description_content_type="text/markdown",
     url="https://github.com/codemation/aiopyql",
     classifiers=[
         "Programming Language :: Python :: 3",
         "License :: OSI Approved :: MIT License",
         "Operating System :: OS Independent",
     ],
     python_requires='>=3.7, <4',
     install_requires=['aiosqlite', 'aiomysql', 'asyncpg', 'cryptography'],
 )


#twitter mentioning network Matrix
#goal is to make it up-to-date everytime
#this code is run by a user

#!/usr/bin/env python
# encoding: utf-8
import time
from selenium import webdriver #solve scroll problem????
from selenium.webdriver.common.keys import Keys
#from selenium.webdriver.support.ui import WebDriverWait
#import selenium.webdriver.support.expected_conditions as EC
#from selenium.webdriver.common.by import By
import csv
driver = webdriver.Chrome() # edit to webdriver.PhantomJS("phantomjs")
                            # to utilize phantomjs instead

#take command line argument of file name
'''
try:
    filename = sys.argv[1]
except IndexError:
    filename = "edges.csv"
'''
fd = open('new_edges.csv', 'wb')
#mentioner is from whome the mention, and mentiong is the person
#being mentioned
def get_total_mention(mentioner, mentioning):
    url = u'https://twitter.com/search?f=tweets&q=from%3A'+mentioner+'%20%40'+mentioning+'&src=typd'
    driver.get(url)
    time.sleep(2)
    body = driver.find_element_by_tag_name('body')

    for _ in range(400):
        body.send_keys(Keys.PAGE_DOWN)

    #    if driver.find_elements_by_class_name('stream-end-inner'):
    #        next
    #    time.sleep(0.2)
        #if driver.find_elements_by_class_name('stream-end-inner')
        #continue
    tweets = driver.find_elements_by_class_name('tweet-text')

    counter = 0
    for tweet in tweets:
        if not tweet:
            continue
        counter += 1
    results = '%s,%s,%s\n' % (mentioner,mentioning,counter)#outputformat gephi edges
    fd.write(results)
with open(var, 'r') as file1:
    l = []
    for usern in file1:
        usern = usern.rstrip()
        l.append(usern)
    for mentioner in l:
        time.sleep(2) #find best time sleep for twitter
        for mentioning in l: #what's newer way to do nested loop?find
            get_total_mention(mentioner, mentioning)
fd.close()


import difflib
import itertools
from typing import Iterable, Tuple

from .parser import Parser

class Differ:
    r'''
    This class can be used to flexibly compare two pieces of text and
    return diff information in a variety of formats.

    Definitions of common terms used:

    - non-newline whitespace: Tabs and spaces
    - newline: Any of the following line endings: ``\n``, ``\r``, or
      ``\r\n``
    - whitespace: A combination of newlines and non-newline whitespace
    - empty line: A line consisting of only whitespace

    '''

    def __init__(self,
                 ignore_case: bool=False,
                 ignore_non_newline_whitespace: bool=False,
                 ignore_non_newline_whitespace_changes: bool=False,
                 ignore_newline_changes: bool=False,
                 ignore_blank_lines: bool=False,
                 ignore_leading_whitespace: bool=False,
                 ignore_trailing_whitespace: bool=False) -> None:
        r'''
        :param ignore_case: Ignore case differences between the two
            texts.
        :param ignore_non_newline_whitespace: Completely ignore
            differences in non-newline whitespace.
        :param ignore_non_newline_whitespace_changes: Treat consecutive
            sequences of non-newline whitespace as equal.
            For example, when this option is True, a single
            space, two spaces, a tab character, and a mix of tabs and
            spaces will be considered equal.
        :param ignore_newline_changes: Treat consecutive sequences of
            newline characters as equal. For example, when this option
            is True, ``\r``, ``\r\r\n``, and ``\n\n`` will all be
            considered equal.
        :param ignore_blank_lines: Ignore lines consisting of only
            whitespace.
        :param ignore_leading_whitespace: Ignore whitespace characters
            at the beginning of lines. Note that this will cause empty
            lines to be treated as the empty string.
        :param ignore_trailing_whitespace: Ignore whitespace characters
            at the end of lines. Note that this will cause empty
            lines to be treated as the empty string.
        '''
        self._parser = Parser(
            ignore_case=ignore_case,
            ignore_non_newline_whitespace=ignore_non_newline_whitespace,
            ignore_non_newline_whitespace_changes=ignore_non_newline_whitespace_changes,
            ignore_newline_changes=ignore_newline_changes,
            ignore_blank_lines=ignore_blank_lines,
            ignore_leading_whitespace=ignore_leading_whitespace,
            ignore_trailing_whitespace=ignore_trailing_whitespace
        )

    def compare(self, first: str, second: str) -> Iterable[Tuple[str, str, str]]:
        '''
        Performs a line-by-line comparision of the strings first and
        second and returns a sequence of ``(tag, left, right)`` tuples
        specifying the differences between the strings.

        ``tag`` can be any of the values of "tag" used in
        https://docs.python.org/3.5/library/difflib.html#difflib.SequenceMatcher.get_opcodes
        and have the same meanings.

        If the two strings are equal, returns an empty iterable.
        '''
        result = tuple()  # type: Iterable[Tuple[str, str, str]]
        parsed_first = self._parser.parse(first)
        parsed_second = self._parser.parse(second)
        matcher = difflib.SequenceMatcher(a=parsed_first, b=parsed_second)

        sequences_equal = True

        for tag, first_start, first_end, second_start, second_end in matcher.get_opcodes():
            if tag != 'equal':
                sequences_equal = False

            pairs = itertools.zip_longest(
                (line.original_text for line in parsed_first[first_start:first_end]),
                (line.original_text for line in parsed_second[second_start:second_end]),
                fillvalue='')
            tuples = [(tag,) + pair for pair in pairs]
            result = itertools.chain(result, tuples)

        if sequences_equal:
            return tuple()

        return result


from django.core.exceptions import ValidationError
from django.db import models
import jsonfield

class Template(models.Model):
    name = models.CharField(max_length=100, unique=True)
    data = models.TextField(default="")
    args = jsonfield.JSONField(
        blank=True,
        default={"measurement": "", "field": ""},
    )
    defaults = jsonfield.JSONField(blank=True, default={"field": "value"})

    def clean(self):
        errors = []
        if "measurement" not in self.args.keys():
            errors.append(
                ValidationError("Arguments must contain measurement")
            )
        if "field" not in self.args.keys():
            errors.append(ValidationError("Arguments must contain field"))
        for key in self.defaults.keys():
            if key not in self.args.keys():
                errors.append(
                    ValidationError(f"Unknown argument in defaults: {key}")
                )
        if len(errors) > 0:
            raise ValidationError(errors)

    def __str__(self):
        return self.name


from detectron2.config import LazyCall as L
from detectron2.solver import WarmupParamScheduler
from fvcore.common.param_scheduler import MultiStepParamScheduler
from ..common.data.cod10k_instance_seg import dataloader
from ..common.models.diffcis_with_label import model

from ..common.train import train
from ..common.optim import AdamW as optimizer

from ..common.data.nc4k_eval import (
    nc4k_eval as _nc4k_eval
)

train.max_iter = 92_188
train.grad_clip = 0.01
train.checkpointer.period = 50

lr_multiplier = L(WarmupParamScheduler)(
    scheduler=L(MultiStepParamScheduler)(
        values=[1.0, 0.1, 0.01],
        # assume 100e with batch-size 64 as original LSJ
        # Equivalent to 100 epochs.
        # 100 ep = 184375 iters * 64 images/iter / 118000 images/ep
        milestones=[163889, 177546],
        num_updates=184375,
    ),
    # for warmup length we adopted COCO LSJ setting
    warmup_length= train.checkpointer.period / 184375,
    warmup_factor=0.067,
)

optimizer.lr = 1e-4
optimizer.weight_decay = 0.05

# dataloader.extra_task = dict(
#     eval_nc4k=_nc4k_eval
# )

def TVA_module(image_features, text_features):

    # weights to restrain influence of obvious classes on others
    prob = image_features[:, :1, :] @ text_features.t()
    prob = (prob * 2).softmax(-1)
    w = prob / prob.mean(-1, keepdim=True)

    # element-wise multiplied features
    b, n_t, n_i, c = image_features.shape[0], text_features.shape[0], image_features.shape[1], image_features.shape[2]
    feats = image_features.reshape(b, n_i, 1, c) * text_features.reshape(1, 1, n_t, c)
    feats *= w.reshape(1, 1, n_t, 1)
    redundant_feats = feats.mean(2, keepdim=True) # along cls dim
    feats = feats - redundant_feats

    # sum the element-wise multiplied features as cosine similarity
    similarity = feats.sum(-1)

    return similarity


# coding=utf-8
import random
import numpy as np
from collections import defaultdict
from environment import Agent, Environment
from planner import RoutePlanner
from simulator import Simulator

class LearningAgent(Agent):
    """ An agent that learns to drive in the smart-cab world

    *** Q-learning agent pseudo-code ***

    1. Initialize Q(s, a) arbitrarily
    2. For each trial t:
        3. Repeat for until deadline or goal is reached:
            4. Update the state s
            5. Choose action a from s using a policy derived from Q (e.g. epsilon-greedy)
            6. Take action a and observe the reward and outcome state s'
            7. Update Q(s, a) := Q(s,a) + α [r + γ max_a′ Q(s′,a′) − Q(s,a)]
    """

    def __init__(self, env):
        """ Initialize the learning agent"""

        super(LearningAgent, self).__init__(env)  # Run constructor of base class
        self.color = 'red'  # Set agent color to red, overriding the default value
        self.planner = RoutePlanner(self.env, self)  # Route planner to get next_waypoint
        self.actions = Environment.valid_actions  # Store all possible actions (improved code readability)

        self.Q = defaultdict(lambda: random.uniform(-.25, .25))  # arbitrary values for all state, action pairs
        self.gamma = 0.35  # Discount factor of max(Qs', a')
        self.epsilon = 0.9  # Probability of doing a random move
        self.alpha = 0.2  # Learning rate
        self.trial = 0  # Trial counter (epsilon decay)
        self.selection = "random"
        self.filter = False

        self.total_reward = 0  # Keep track of total reward for the current trial
        self.mistake_counter = 0  # Keep track of the number of moves with a negative reward
        self.rewards = []  # Historical rewards
        self.mistakes = []  # Historical mistakes
        self.results = []  # Non-successful trials
        self.success = 1

    def set_parameters(self, alpha, epsilon, gamma, selection, f):
        """ Overwrite the parameters with static settings hack """

        self.gamma = gamma  # Discount factor of max(Qs', a')
        self.epsilon = epsilon  # Probability of doing a random move
        self.alpha = alpha
        self.selection = selection
        self.filter = f

    def reset(self, destination=None):
        """ Reset the agent between trails """

        self.planner.route_to(destination)  # Initialize new destination

        self.trial += 1  # For every trial, increment trial counter
        self.rewards.append(self.total_reward)  # Keep track of rewards for each trial
        self.mistakes.append(self.mistake_counter)  # Keep track of mistakes for each trial
        self.total_reward = 0  # Reset reward counter
        self.mistake_counter = 0  # Reset mistake counter
        self.results.append(self.success)
        self.success = 1

    def get_state(self):
        """ Return the current state s of the agent """

        inputs = self.env.sense(self)
        return inputs['light'], inputs['oncoming'], inputs['left'], self.next_waypoint

    def select_action(self, s):

        if self.selection == "random":
            return random.choice(self.actions)

        if self.selection == "epsilon-greedy":
            # select a random move with probability ε
            if random.random() < self.epsilon:
                return random.choice(self.actions)
            else:
                # Shuffle deals with the cases when a draw is returned from np.argmax
                random.shuffle(self.actions)
                # Evaluate all action and pick the one with the highest estimated reward
                return self.actions[np.argmax([self.Q[(s, a_i)] for a_i in self.actions])]

        elif self.selection == "epsilon-decay":
            # select a random move with probability ε controlled by an epsilon-decay function
            if random.random() < np.exp(-self.epsilon * self.trial):
                return random.choice(self.actions)
            # otherwise action = max Q(s', a')
            else:
                # Shuffle deals with the cases when a draw is returned from np.argmax
                random.shuffle(self.actions)
                # Evaluate all action and pick the one with the highest estimated reward
                return self.actions[np.argmax([self.Q[(s, a_i)] for a_i in self.actions])]

    def update(self, t):
        """ Implements the q-learning algorithm """

        # Get next waypoint from the route planner
        self.next_waypoint = self.planner.next_waypoint()

        # Filter out unwanted actions
        if self.filter:
            self.actions = [self.next_waypoint, None]

        # Observe the current state
        s = self.get_state()
        # Select action a according to policy
        a = self.select_action(s)
        # Take action a, observe reward and s'
        reward, s_i = self.env.act(self, a), self.get_state()
        # Calculate maximum attainable reward in the next state (s_i)
        max_a = max([self.Q[(s_i, a_i)] for a_i in self.actions])
        # Update state, action value
        self.Q[(s, a)] += self.alpha * (reward + self.gamma * (max_a - self.Q[(s, a)]))

        # Update total reward
        self.total_reward += reward
        # If remaining time is 0, store as failed attempt
        if self.env.get_deadline(self) == 0:
            self.success = 0
        # Update total mistakes
        if reward < 0:
            self.mistake_counter += 1

        # DEBUG
        # print "deadline = {}, inputs = {}, action = {}, reward = {}".format(
            # self.env.sense(self), self.env.get_deadline(self), a, reward)

    def get_run_data(self):
        return self.results, self.mistakes, self.rewards

def run(alpha, epsilon, gamma, selection, t, f):
    """Run the agent for a finite number of trials."""

    # create environment, add dummy agents
    e = Environment()

    # Create our agent
    a = e.create_agent(LearningAgent)
    # Initialize agent settings
    a.set_parameters(alpha, epsilon, gamma, selection, f)
    # Configure the environment, set agent a as primary agent to track
    e.set_primary_agent(a, enforce_deadline=True)

    # Simulator settings, reduce delay to speed up simulations
    sim = Simulator(e, update_delay=0.00001)
    # Set the number of trials
    sim.run(n_trials=t)

    # Return data on agents performance
    return a.get_run_data()

if __name__ == '__main__':
    run(.2, .5, .1, "epsilon-decay", 100)


import requests
from bs4 import BeautifulSoup as bs

with open('URL_IMPORTANT.txt','r') as f:
	url = f.read()
response = requests.get(url)
soup = bs(response.text, "lxml")

_soup1 = soup.find('div', 'temp fact__temp fact__temp_size_s')
better_soup = (str(_soup1.text) + '°')
print('\n Температура сейчас:', better_soup)

feel = soup.find('div', 'term term_orient_h fact__feels-like')
feel1 = feel.find('span')
print(f' Ощущается как {feel1.text}°\n')

water = soup.find('div', 'maps-widget-nowcast card content__brief')
water2 = water.find('p')
print('',water2.text)

how = soup.find('div', 'link__condition day-anchor i-bem')
print('',how.text)

#print('\n' + str(response))

input('\n Нажмите ENTER для выхода')


"""DRFsite URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include, re_path
from myQuize.views import *
from rest_framework import routers
from rest_framework_simplejwt.views import TokenVerifyView,TokenObtainPairView,TokenRefreshView
from django.views.generic import TemplateView,RedirectView

app_name = 'myQuize'

urlpatterns = [
    path('admin/', admin.site.urls),
    path ('api/v1/Myquize/', MyquizeAPIList.as_view ()),
    path('api/v1/Myquize/<int:pk>/', MyquizeAPIUpdate.as_view()),
    path ('api/v1/Myquizedelete/<int:pk>/', MyquizeAPIDestroy.as_view()),
    path('api/v1/Myquize-auth/',include('rest_framework.urls')),
    path ('api/v1/auth/', include ('djoser.urls')),
    re_path (r'^auth/', include ('djoser.urls.authtoken')),
    path('api/v1/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/v1/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    path('api/v1/token/verify/', TokenVerifyView.as_view(), name='token_verify'),
    # path('accounts/profile/', RedirectView.as_view(url='http://127.0.0.1:8000')),
    path('', TemplateView.as_view(template_name='index.html'), name='index'),
    path('accounts/profile/', RedirectView.as_view(url='/')),
    path('users/', UserCreate.as_view(), name='user-create'),
#
#
#
#
#
# class MyCustomRouter (routers. SimpleRouter):
#     routes = [
#         routers.Route(url=r'^{prefix}$',
#                      mapping={'get': 'list'},
#                      name='{basename}-list',
#                      detail=False,
# #                      initkwargs={'suffix': 'List'}),
#         routers.Route(url=r'^{prefix}/{lookup}$',
#                      mapping={'get': 'retrieve'},
#                      name='{basename}-detail',
#                      detail=True,
#                      initkwargs={'suffix': 'Detail'})]

# router = MyCustomRouter()
# router.register (r"Myquize",MyquizeViewSet,basename='Myquize')

    # path('api/v1/', include(router.urls)),
    # path('api/v1/Myquize/', MyquizeViewSet.as_view({'get': 'list'})),
    # path('api/v1/Myquize/<int:pk>/', MyquizeViewSet.as_view({"put":"update"})),

]


# Lint as: python3
"""Tests for epi_forecast_stat_mech.statistical_models.bic_model."""

from absl.testing import parameterized

from epi_forecast_stat_mech.statistical_models import bic_model
from epi_forecast_stat_mech.statistical_models import linear_model_test

import numpy as np

from absl.testing import absltest

class BicLinearModelTest(linear_model_test.LinearModelTest):
  """We assert the BIC-wrapped model should pass the tests for `LinearModel`."""

  def get_model(self):
    base_model = super().get_model()
    return bic_model.BICModel(base_model=base_model)

class SoftNonzeroTest(parameterized.TestCase):

  @parameterized.parameters(
      dict(x=np.array([1, 2, 0, 0, 3], dtype=np.float32),
           sharpness=20,
           threshold=.1,
           nonzero_count=3),
      dict(x=np.array([[-1000, 200], [0, 0]], dtype=np.float32),
           sharpness=20,
           threshold=.1,
           nonzero_count=2),
      dict(x=np.array([0, 0, 0, 0], dtype=np.float32),
           sharpness=100,
           threshold=.1,
           nonzero_count=0),
      dict(x=np.arange(1000.),
           sharpness=20,
           threshold=.1,
           nonzero_count=999),
      )
  def testSoftNonzeroAccuracy(self, x, sharpness, threshold, nonzero_count):
    soft_nonzero_count = bic_model.soft_nonzero(x, sharpness, threshold).sum()
    atol = .1 * np.size(x)
    np.testing.assert_allclose(nonzero_count, soft_nonzero_count, atol=atol)

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


import copy
import sys
import os

from pathlib import Path

from utils import dump_to_file
from utils import load_file

src_path = sys.argv[1]
dest_path = sys.argv[2]
module = sys.argv[3]
src_name = sys.argv[4]
dest_name = sys.argv[5]

dest_action_group = dest_name.split('.')[1]

def update_actions_group(data, to_be_migrated):
    to_be_updated = []
    current = data['action_groups'][dest_action_group]
    to_be_updated = [x for x in current if x not in to_be_migrated]
    data['action_groups'][dest_action_group] = to_be_updated

    return data

def update_plugin_routing(data, to_be_migrated):
    for name in to_be_migrated:
        if name in data['plugin_routing']['modules']:
            data['plugin_routing']['modules'].pop(name)
        else:
            data['plugin_routing']['modules'][name] = {"redirect": f"{dest_name}.{name}"}

    return data

def add_changelog(to_be_migrated):
    changelog_to = {}
    changelog_from = {}
    changelog_to['major_changes'] = []
    changelog_from['breaking_changes'] = []

    os.makedirs(f"{dest_path}/changelogs/fragments", exist_ok=True)

    for module_name in to_be_migrated:
        _module_name = module_name
        if '_facts' in module_name:
            module_name = module_name.replace("_facts", "_info")
        msg = f"""{_module_name} - The module has been migrated from the ``{src_name}`` collection. Playbooks using the Fully Qualified Collection Name for this module should be updated to use ``{dest_name}.{module_name}``."""
        changelog_to['major_changes'].append(msg)
        changelog_from['breaking_changes'].append(msg)

    dump_to_file(changelog_to, f"{dest_path}/changelogs/fragments/migrate_{module}.yml")
    dump_to_file(changelog_from, f"{src_path}/changelogs/fragments/migrate_{module}.yml")

def ensure_and_dump_meta(data, path):
    dump_to_file(data, f"{path}/meta/runtime.yml")

    runtime_file = Path(f"{path}/meta/runtime.yml")
    no_empty_lines = [l for l in runtime_file.read_text().split("\n") if l != ""]
    content = '\n'.join(no_empty_lines)
    if not content.startswith("---\n"):
        content = f"---\n{content}"
    runtime_file.write_text(content)

def regenerate():
    action_groups_to_be_added = []
    plugin_routing_to_be_added = {}
    com_data = load_file(f"{src_path}/meta/runtime.yml")
    _com_data_cpy = copy.deepcopy(com_data)
    am_data = load_file(f"{dest_path}/meta/runtime.yml")
    _am_data_cpy = copy.deepcopy(am_data)

    for module_name in com_data['action_groups'][dest_action_group]:
        if module in module_name:
            action_groups_to_be_added.append(module_name)

    for key, value in com_data['plugin_routing']['modules'].items():
        if module in value.get('redirect'):
            li = com_data['plugin_routing']['modules'][key]
            li['redirect'] = li['redirect'].replace(src_name, dest_name)
            plugin_routing_to_be_added[key] = li
            _com_data_cpy['plugin_routing']['modules'].pop(key)

    if action_groups_to_be_added:
        _am_data_cpy['action_groups'][dest_action_group].extend(action_groups_to_be_added)
        _am_data_cpy['action_groups'][dest_action_group].sort()
        _com_data_cpy = update_actions_group(com_data, action_groups_to_be_added)

    if plugin_routing_to_be_added:
        _am_data_cpy['plugin_routing']['modules'].update(plugin_routing_to_be_added)
        _am_data_cpy['plugin_routing']['modules']= dict(sorted(_am_data_cpy['plugin_routing']['modules'].items()))
        _am_data_cpy['action_groups'][dest_action_group].sort()

    _com_data_cpy = update_plugin_routing(com_data, action_groups_to_be_added)

    ensure_and_dump_meta(_com_data_cpy, src_path)
    ensure_and_dump_meta(_am_data_cpy, dest_path)

    add_changelog(action_groups_to_be_added)

regenerate()


from crypt import methods
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import status
from rest_framework.serializers import ModelSerializer
from rest_framework.decorators import action
from rareapi.models import RareUser, Subscription

class RareUserView(ViewSet):
    def list(self, request):
        rare_users = RareUser.objects.all()
        serializer = RareUserSerializer(rare_users, many=True)
        return Response(serializer.data)

    def retrieve(self, request, pk):
        author = RareUser.objects.get(pk=pk)
        follower = RareUser.objects.get(user=request.auth.user)
        try:
            Subscription.objects.get(follower=follower, author=author)
            author.is_followed = True
            serializer = RareUserSerializer(author)
            return Response(serializer.data)
        except Subscription.DoesNotExist:
            author.is_followed = False
            serializer = RareUserSerializer(author)
            return Response(serializer.data)

    @action(methods=['get'], detail=False)
    def currentuser(self, request):
        """Gets the current user at http://localhost:8000/rareusers/currentuser"""
        rare_user = RareUser.objects.get(user=request.auth.user)
        serializer = RareUserSerializer(rare_user)
        return Response(serializer.data)

    @action(methods=['post'], detail=True)
    def subscribe(self, request, pk):
        author = RareUser.objects.get(pk=pk)
        follower = RareUser.objects.get(user=request.auth.user)
        serializer = SubscriptionSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save(author=author, follower=follower)

        return Response(serializer.data, status=status.HTTP_201_CREATED)

    @action(methods=['delete'], detail=True)
    def unsubscribe(self, request, pk):
        author = RareUser.objects.get(pk=pk)
        follower = RareUser.objects.get(user=request.auth.user)
        sub_obj = Subscription.objects.get(
            author=author, follower=follower)
        sub_obj.delete()
        return Response(None, status=status.HTTP_204_NO_CONTENT)

class RareUserSerializer(ModelSerializer):
    class Meta:
        model = RareUser
        fields = ('id', "bio", 'is_followed', 'created_on', 'user',
                  'is_followed', 'subscriber_count')
        depth = 1

class SubscriptionSerializer(ModelSerializer):
    class Meta:
        model = Subscription
        fields = ('author', 'follower')
        depth = 1


from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^register$', views.register, name='register'),
    url(r'^login$', views.login, name='login'),
    url(r'^process$', views.process, name='process'),
    url(r'^logout$', views.logout),
    url(r'^makepost$', views.makepost),
    url(r'^secretspage$', views.secretspage ),
    url(r'^like/(?P<sid>\d+)$', views.like),
    url(r'^delete/(?P<id>\d+)$', views.delete),
    # url(r'^.+$', views.any, name='any')

]


import matplotlib.pyplot as plt

input_values = [1, 2, 3, 4, 5]

# Plot 1 - Cubes
cubes = []

for num in input_values:
    cubes.append(num*num*num)

ax1 = plt.subplot(1, 2, 1)
ax1.plot(input_values, cubes, marker='*', ls='dashed', lw='2', c='#326C4C')
plt.title("Cubed Numbers")
plt.xlabel("Input Values")
plt.ylabel("Values Cubed")
plt.grid()

# Plot 2 - Raised to the 2nd Power
pow = []

for num in input_values:
    pow.append(num**2)

ax2 = plt.subplot(1, 2, 2)
plt.style.use("seaborn-paper")
ax2.plot(input_values, pow, color='purple', marker='^')
plt.title("Raised Numbers")
plt.xlabel("Input Values")
plt.ylabel("2nd Power Value")
plt.grid(color='gainsboro')

# Draw graphs
plt.suptitle("Fun with Numbers", c='red', fontfamily="Bookman Old Style", fontsize="16")
plt.show()


from itertools import combinations
from SAT_AI import SAT_AI as MinesweeperAI

import numpy as np

class Minesweeper():
    """
    Minesweeper game representation
    """

    def __init__(self, height=8, width=8, mines=8):

        # Set initial width, height, and number of mines
        self.height = height
        self.width = width
        self.mines = set()

        # Initialize an empty field with no mines
        self.board = []
        for i in range(self.height):
            row = []
            for j in range(self.width):
                row.append(False)
            self.board.append(row)
        self.initial_board = self.board.copy()
        # Add mines randomly

    def get_initial_board(self):
        return self.initial_board

    def __str__(self):
        """
        Prints a text-based representation
        of where mines are located.
        """
        for i in range(self.height):
            print("--" * self.width + "-")
            for j in range(self.width):
                if self.board[i][j]:
                    print("|X", end="")
                else:
                    print("| ", end="")
            print("|")
        print("--" * self.width + "-")

    def set_mines(self, mines):
        for i, j in mines:
            self.board[i][j] = True
        self.mines = mines

    def is_mine(self, cell):
        i, j = cell
        return self.board[i][j]

    def nearby_mines(self, cell):
        """
        Returns the number of mines that are
        within one row and column of a given cell,
        not including the cell itself.
        """

        # Keep count of nearby mines
        count = 0

        # Loop over all cells within one row and column
        for i in range(cell[0] - 1, cell[0] + 2):
            for j in range(cell[1] - 1, cell[1] + 2):

                # Ignore the cell itself
                if (i, j) == cell:
                    continue

                # Update count if cell in bounds and is mine
                if 0 <= i < self.height and 0 <= j < self.width:
                    if self.board[i][j]:
                        count += 1
        return count

def all_boards_iterator(height, width, no_of_mines):
    coords_list = [(i, j) for i in range(height) for j in range(width)]
    for mines_comb in combinations(coords_list, no_of_mines):
        new_board = Minesweeper(height, width, no_of_mines)
        new_board.set_mines(set(mines_comb))
        yield new_board

def append_to_dict(dict, key, value):
    if key not in dict:
        dict[key] = [value]
    else:
        dict[key].append(value)

def unify_dict_values(dict_a, dict_b):
    for k, v in dict_b.items():
        if k in dict_a:
            dict_a[k].extend(v)
        else:
            dict_a[k] = v

def gather_information_for_4x4():
    conf_moves = dict()
    conf_count = dict()
    NO_OF_GAMES_PER_MINES_COMB = 1000
    for no_of_mines in range(1, 16):
        for game in all_boards_iterator(4, 4, no_of_mines):
            for i in range(NO_OF_GAMES_PER_MINES_COMB):
                current_conf_moves = dict()
                print("NEXT GAME")
                ai = MinesweeperAI(height=4, width=4, no_of_mines_in_board=no_of_mines)
                revealed = set()
                lost = False
                won = False
                while not lost:
                    flags = ai.mines.copy()
                    current_configuration = ai.get_configuration()
                    if current_configuration in conf_count:
                        conf_count[current_configuration] += 1
                    else:
                        conf_count[current_configuration] = 1
                    if flags == game.mines:
                        print("WON")
                        won = True
                        break
                    move = ai.make_safe_move()
                    if move is None:
                        move = ai.make_random_move()
                        if move is None:
                            # print("No moves left to make.")
                            lost = True
                        else:
                            pass
                            # print("No known safe moves, AI making random move:", move)
                    else:
                        pass
                        # print("AI making safe move:", move)
                    # Make move and update AI knowledge
                    if move:
                        if game.is_mine(move):
                            lost = True
                        else:

                            append_to_dict(current_conf_moves, current_configuration, move)
                            nearby = game.nearby_mines(move)
                            revealed.add(move)
                            ai.extend_knowledge_base(move, nearby)
                if won:
                    unify_dict_values(conf_moves, current_conf_moves)

    for k, v in conf_moves.items():
        max_value = max(v, key=v.count)
        max_count = v.count(max_value)
        prob = max_count / conf_count[k]
        conf_moves[k] = (max_value, prob)
    np.save('PDB.npy', conf_moves)
    # read_dictionary = np.load('PDB.npy', allow_pickle=True).item()
    # print(read_dictionary.values())  # displays "world"

gather_information_for_4x4()


import time
import functools
def main():
   # Find the sum of all positive integers that cannot be written as the sum of two abundant numbers
   # NOTE: Only evaluate integers under 20162
   # NOTE: every EVEN integer greater than 46 is the sum of two abundant numbers

   # finds abundantNumbers under 20162
   print("Finding Abundant Numbers")
   abundantNumberList = findAbundantNumbers()
   abundantNumberList.sort()

   # creates an odd abundant numbers list and some small evens to lower number of additions
   print("Truncating List")
   oddList = set()
   for number in abundantNumberList:
      if number%2!=0 or number < 46:
         oddList.add(number)
   oddList = list(oddList)
   oddList.sort()

   # finds the possible sums of abundant numbers
   print("Finding Possible Sums")
   possibleSums = set()
   for number2 in oddList:
      for number in abundantNumberList:
         tempSum = number + number2
         if tempSum > 20162: break
         possibleSums.add(tempSum)

   # finds all positive integers that cannot be written as the sum of two abundant numbers
   notSums = set()
   for i in range(1, 20162):
      if not i in possibleSums:
         notSums.add(i)
   notSums = list(notSums)
   notSums.sort()

   # prints sum of all positive integers that cannot be written as the sum of two abundant numbers
   print("Answer:", sum(notSums))

def findAbundantNumbers():
   abundantNumbers = set()
   for i in range(12, 20162):
      if sum(findProperDivisors(i)) > i: abundantNumbers.add(i)
   return list(abundantNumbers)

def findProperDivisors(n):
   divisorList = list(set(functools.reduce(list.__add__,
      ([i, n/i] for i in range(1, int(n**0.5) + 1) if n % i == 0))))

   #sorts and takes off the last divisor (itself)
   divisorList.sort()
   return divisorList[:-1]

if __name__ == '__main__':
   a = time.time()
   main()
   print(time.time() - a)