import os
from typing import Dict

from nordea_analytics import NordeaAnalyticsService
from nordea_analytics.nalib.data_retrieval_client import DataRetrievalServiceClient
from nordea_analytics.nalib.http.open_banking import (
    OpenBankingClientConfiguration,
    OpenBankingHttpClient,
)
from nordea_analytics.nalib.live_keyfigures.open_banking import (
    OpenBankingHttpStreamIterator,
)
from nordea_analytics.shortcuts.proxy import find_proxy  # type: ignore

DEFAULT_URL = "https://open.nordea.com/instrument-analytics/v1/"

def get_nordea_analytics_client(
    client_id: str, client_secret: str, base_url: str = None, use_proxy: bool = False
) -> NordeaAnalyticsService:
    """Shortcut function to create :class:`NordeaAnalyticsService`.

    Remarks:
        The Client ID and Client Secret are generated after the application is created on My apps page.
        The Client ID and Client Secret can be found when clicking the app icon after the app is created.

    Args:
        client_id:
            client id is required.
        client_secret:
            client secret is required.
        base_url:
            (optional) base url of Nordea Analytics Service.
        use_proxy:
            (optional) Search for appropriate proxy server if it set.

    Returns:
        Service client which can be used to retrieve data.
    """
    proxies = {}
    headers: Dict = {}
    base_url = base_url or DEFAULT_URL
    if use_proxy:
        proxies = find_proxy(base_url)

    configuration = OpenBankingClientConfiguration(
        base_url,
        client_id=client_id,
        client_secret=client_secret,
        proxies=proxies,
        headers=headers,
    )
    http_client = OpenBankingHttpClient(configuration)

    data_retrieval_service_client = DataRetrievalServiceClient(
        http_client, OpenBankingHttpStreamIterator(http_client)
    )

    return NordeaAnalyticsService(data_retrieval_service_client)

def get_nordea_analytics_test_client(
    base_url: str = None, use_proxy: bool = False
) -> NordeaAnalyticsService:
    """Create and return test instance of NordeaAnalyticsService."""
    client_id = os.getenv("clientId")
    client_secret = os.getenv("clientSecret")
    if client_id is None or client_secret is None:
        raise ValueError("Test user credentials are not set")

    return get_nordea_analytics_client(client_id, client_secret, base_url, use_proxy)


import setuptools

with open("README.md", "r") as f:
    long_description = f.read()

setuptools.setup(
    name="Poker-Solver-Lewis-Cole",
    version="0.0.1",
    author="Lewis Cole",
    author_email="lcole.mail@gmail.com",
    description="A poker solver package",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://https://github.com/Lewis-Cole/Poker-Solver",
    license="MIT",
    packages=setuptools.find_packages(),
    classifiers=[
        "Programming Language :: Python :: 3",
        "License :: OSI Approved :: MIT License",
        "Operating System :: OS Independent",
    ],
    python_requires=">=3.6",
)


import asyncio
import time

import pandas as pd
import pytest
import numpy as np

from collections import Counter

from ...... import dataframe as md
from ...... import tensor as mt
from ......config import Config
from ......core import TileContext
from ......core.context import get_context
from ......core.graph import TileableGraph, TileableGraphBuilder, ChunkGraphBuilder
from ......core.operand import ShuffleFetchType
from ......lib.aio.isolation import new_isolation, stop_isolation
from ......resource import Resource
from ......serialization import serialize
from ......tests.core import require_ray, mock
from ......utils import lazy_import, get_chunk_params
from .....context import ThreadedServiceContext
from .....subtask import Subtask
from ....analyzer import GraphAnalyzer
from ....core import new_task_id, Task
from ..config import RayExecutionConfig
from ..context import (
    RayExecutionContext,
    RayExecutionWorkerContext,
    RayRemoteObjectManager,
    _RayRemoteObjectContext,
)
from ..executor import (
    execute_subtask,
    OrderedSet,
    RayTaskExecutor,
    RayTaskState,
    _RayChunkMeta,
    _RaySubtaskRuntime,
    _RaySlowSubtaskChecker,
)
from ..fetcher import RayFetcher
from ..shuffle import ShuffleManager

ray = lazy_import("ray")

def _gen_subtask_chunk_graph(t):
    graph = TileableGraph([t.data])
    next(TileableGraphBuilder(graph).build())
    return next(ChunkGraphBuilder(graph, fuse_enabled=False).build())

def _gen_subtask_graph(t):
    tileable_graph = t.build_graph(tile=False)
    chunk_graph = next(ChunkGraphBuilder(tileable_graph).build())
    bands = [(f"address_{i}", "numa-0") for i in range(4)]
    band_resource = dict((band, Resource(num_cpus=1)) for band in bands)
    task = Task("mock_task", "mock_session", tileable_graph)
    analyzer = GraphAnalyzer(
        chunk_graph,
        band_resource,
        task,
        Config(),
        dict(),
        shuffle_fetch_type=ShuffleFetchType.FETCH_BY_INDEX,
    )
    subtask_graph = analyzer.gen_subtask_graph()
    return chunk_graph, subtask_graph

class MockRayTaskExecutor(RayTaskExecutor):
    def __init__(self, *args, **kwargs):
        self._set_attrs = Counter()
        self._monitor_tasks = []
        super().__init__(*args, **kwargs)

    @classmethod
    async def _get_apis(cls, session_id: str, address: str):
        return None, None

    @staticmethod
    def _get_ray_executor():
        # Export remote function once.
        return None

    async def get_available_band_resources(self):
        return {}

    async def execute_subtask_graph(self, *args, **kwargs):
        self._monitor_tasks.clear()
        return await super().execute_subtask_graph(*args, **kwargs)

    async def _update_progress_and_collect_garbage(self, *args, **kwargs):
        # Infinite loop to test monitor task cancel.
        self._monitor_tasks.append(asyncio.current_task())
        return await super()._update_progress_and_collect_garbage(*args, **kwargs)

    def monitor_tasks(self):
        return self._monitor_tasks

    def set_attr_counter(self):
        return self._set_attrs

    def __setattr__(self, key, value):
        super().__setattr__(key, value)
        self._set_attrs[key] += 1

class MockTileContext(TileContext):
    def get_all_progress(self) -> float:
        return 1.0

@require_ray
@pytest.mark.asyncio
@mock.patch("mars.services.task.execution.ray.executor.RayTaskState.create")
@mock.patch("mars.services.task.execution.ray.context.RayExecutionContext.init")
@mock.patch("ray.get")
async def test_ray_executor_create(
    mock_ray_get, mock_execution_context_init, mock_task_state_actor_create
):
    task = Task("mock_task", "mock_session", TileableGraph([]))

    # Create RayTaskState actor as needed by default.
    mock_config = RayExecutionConfig.from_execution_config({"backend": "ray"})
    executor = await MockRayTaskExecutor.create(
        mock_config,
        session_id="mock_session_id",
        address="mock_address",
        task=task,
        tile_context=TileContext(),
    )
    assert isinstance(executor, MockRayTaskExecutor)
    assert mock_task_state_actor_create.call_count == 0
    ctx = get_context()
    assert isinstance(ctx, RayExecutionContext)
    ctx.create_remote_object("abc", lambda: None)
    assert mock_ray_get.call_count == 1
    assert mock_task_state_actor_create.call_count == 1

@require_ray
@pytest.mark.asyncio
async def test_ray_executor_destroy():
    task = Task("mock_task", "mock_session", TileableGraph([]))
    mock_config = RayExecutionConfig.from_execution_config({"backend": "ray"})
    executor = MockRayTaskExecutor(
        config=mock_config,
        task=task,
        tile_context=TileContext(),
        task_context={},
        task_chunks_meta={},
        lifecycle_api=None,
        meta_api=None,
    )
    counter = executor.set_attr_counter()
    assert len(counter) > 0
    keys = executor.__dict__.keys()
    assert counter.keys() >= keys
    counter.clear()
    executor.destroy()
    keys = set(keys) - {"_set_attrs", "_monitor_tasks"}
    assert counter.keys() == keys, "Some keys are not reset in destroy()."
    for k, v in counter.items():
        assert v == 1
    assert await executor.get_progress() == 1.0

@require_ray
@mock.patch("ray.get_runtime_context")
def test_ray_execute_subtask_basic(_):
    raw = np.ones((10, 10))
    raw_expect = raw + 1
    a = mt.ones((10, 10), chunk_size=10)
    b = a + 1

    subtask_id = new_task_id()
    subtask_chunk_graph = _gen_subtask_chunk_graph(b)
    r = execute_subtask(subtask_id, serialize(subtask_chunk_graph), 0, False)
    np.testing.assert_array_equal(r, raw_expect)
    test_get_meta_chunk = subtask_chunk_graph.result_chunks[0]
    r = execute_subtask(subtask_id, serialize(subtask_chunk_graph), 1, False)
    assert len(r) == 2
    meta_dict, r = r
    assert len(meta_dict) == 1
    assert meta_dict[test_get_meta_chunk.key][0] == get_chunk_params(
        test_get_meta_chunk
    )
    np.testing.assert_array_equal(r, raw_expect)

@require_ray
@pytest.mark.asyncio
async def test_ray_fetcher(ray_start_regular_shared2):
    pd_value = pd.DataFrame(
        {
            "col1": [str(i) for i in range(10)],
            "col2": np.random.randint(0, 100, (10,)),
        }
    )
    pd_object_ref = ray.put(pd_value)
    np_value = np.asarray([1, 3, 6, 2, 4])
    np_object_ref = ray.put(np_value)
    # Test RayFetcher to fetch mixed values.
    fetcher = RayFetcher()
    await fetcher.append("pd_key", {"object_refs": [pd_object_ref]})
    await fetcher.append("np_key", {"object_refs": [np_object_ref]})
    await fetcher.append("pd_key", {"object_refs": [pd_object_ref]}, [slice(1, 3, 1)])
    await fetcher.append("np_key", {"object_refs": [np_object_ref]}, [slice(1, 3, 1)])
    results = await fetcher.get()
    pd.testing.assert_frame_equal(results[0], pd_value)
    np.testing.assert_array_equal(results[1], np_value)
    pd.testing.assert_frame_equal(results[2], pd_value.iloc[1:3])
    np.testing.assert_array_equal(results[3], np_value[1:3])

@require_ray
@pytest.mark.asyncio
async def test_ray_remote_object(ray_start_regular_shared2):
    class _TestRemoteObject:
        def __init__(self, i):
            self._i = i

        def value(self):
            return self._i

        def foo(self, a, b):
            return self._i + a + b

        async def bar(self, a, b):
            return self._i * a * b

    # Test RayTaskState reference
    state = RayTaskState.create()
    await state.create_remote_object.remote("aaa", _TestRemoteObject, 123)
    assert await state.call_remote_object.remote("aaa", "value") == 123
    state = RayTaskState.create()
    assert await state.call_remote_object.remote("aaa", "value") == 123

    # Test RayRemoteObjectManager
    name = "abc"
    manager = RayRemoteObjectManager()
    manager.create_remote_object(name, _TestRemoteObject, 2)
    r = await manager.call_remote_object(name, "foo", 3, 4)
    assert r == 9
    r = await manager.call_remote_object(name, "bar", 3, 4)
    assert r == 24
    manager.destroy_remote_object(name)
    with pytest.raises(KeyError):
        await manager.call_remote_object(name, "foo", 3, 4)

    # Test _RayRemoteObjectContext
    context = _RayRemoteObjectContext(lambda: RayTaskState.create())
    context.create_remote_object(name, _TestRemoteObject, 2)
    remote_object = context.get_remote_object(name)
    r = remote_object.foo(3, 4)
    assert r == 9
    r = remote_object.bar(3, 4)
    assert r == 24
    context.destroy_remote_object(name)
    with pytest.raises(KeyError):
        remote_object.foo(3, 4)

    class MyException(Exception):
        pass

    class _ErrorRemoteObject:
        def __init__(self):
            raise MyException()

    with pytest.raises(MyException):
        context.create_remote_object(name, _ErrorRemoteObject)

    handle = RayTaskState.get_handle()
    assert handle is not None

@require_ray
def test_ray_execution_context(ray_start_regular_shared2):
    value = 123
    o = ray.put(value)

    def fake_init(self):
        pass

    async def fake_get_chunks_meta_from_service(
        self, data_keys, fields=None, error="raise"
    ):
        mock_meta = {"meta_1": {fields[0]: 1}, "meta_3": {fields[0]: 3}}
        return [mock_meta[k] for k in data_keys]

    with mock.patch.object(
        ThreadedServiceContext, "__init__", new=fake_init
    ), mock.patch.object(
        RayExecutionContext,
        "_get_chunks_meta_from_service",
        new=fake_get_chunks_meta_from_service,
    ):
        mock_config = RayExecutionConfig.from_execution_config({"backend": "ray"})
        mock_worker_addresses = ["mock_worker_address"]
        isolation = new_isolation("test", threaded=True)
        try:
            context = RayExecutionContext(
                mock_config, {"abc": o}, {}, mock_worker_addresses, lambda: None
            )
            context._loop = isolation.loop
            r = context.get_chunks_result(["abc"])
            assert r == [value]

            r = context.get_worker_addresses()
            assert r == mock_worker_addresses

            r = context.get_chunks_meta(["meta_1"], fields=["memory_size"])
            assert r == [{"memory_size": 1}]

            context._task_chunks_meta["meta_1"] = _RayChunkMeta(memory_size=2)
            r = context.get_chunks_meta(["meta_1", "meta_3"], fields=["memory_size"])
            assert r == [{"memory_size": 2}, {"memory_size": 3}]
        finally:
            stop_isolation("test")

def test_ray_execution_worker_context():
    context = RayExecutionWorkerContext(lambda: None)
    with pytest.raises(NotImplementedError):
        context.set_running_operand_key("mock_session_id", "mock_op_key")
    with pytest.raises(NotImplementedError):
        context.register_custom_log_path(
            "mock_session_id",
            "mock_tileable_op_key",
            "mock_chunk_op_key",
            "mock_worker_address",
            "mock_log_path",
        )

    assert context.set_progress(0.1) is None
    assert context.new_custom_log_dir() is None
    assert context.get_storage_info("mock_address") == {}

@require_ray
@pytest.mark.asyncio
async def test_ray_execution_config(ray_start_regular_shared2):
    t1 = mt.random.randint(10, size=(100, 10), chunk_size=100)
    chunk_graph, subtask_graph = _gen_subtask_graph(t1)

    real_executor = RayTaskExecutor._get_ray_executor()

    class MockExecutor:
        opt = {}

        @classmethod
        def options(cls, *args, **kwargs):
            cls.opt = kwargs
            return real_executor.options(*args, **kwargs)

    task = Task("mock_task", "mock_session", TileableGraph([]))
    mock_config = RayExecutionConfig.from_execution_config(
        {
            "backend": "ray",
            "ray": {
                "monitor_interval_seconds": 0,
                "subtask_max_retries": 4,
                "subtask_num_cpus": 0.8,
                "subtask_memory": 1001,
                "n_cpu": 1,
                "n_worker": 1,
            },
        }
    )
    tile_context = MockTileContext()
    executor = MockRayTaskExecutor(
        config=mock_config,
        task=task,
        tile_context=tile_context,
        task_context={},
        task_chunks_meta={},
        lifecycle_api=None,
        meta_api=None,
    )
    executor._ray_executor = MockExecutor
    async with executor:
        await executor.execute_subtask_graph(
            "mock_stage", subtask_graph, chunk_graph, tile_context
        )

    assert MockExecutor.opt["num_cpus"] == 0.8
    assert MockExecutor.opt["max_retries"] == 4
    assert MockExecutor.opt["memory"] == 1001

@require_ray
@pytest.mark.asyncio
@pytest.mark.parametrize("gc_method", ["submitted", "completed"])
async def test_executor_context_gc(ray_start_regular_shared2, gc_method):
    popped_seq = []

    class MockTaskContext(dict):
        def pop(self, k, d=None):
            popped_seq.append(k)
            return super().pop(k, d)

    t1 = mt.random.randint(10, size=(100, 10), chunk_size=100)
    t2 = mt.random.randint(10, size=(100, 10), chunk_size=50)
    t3 = t2 + t1
    t4 = t3.sum(0)
    chunk_graph, subtask_graph = _gen_subtask_graph(t4)
    task = Task("mock_task", "mock_session", TileableGraph([]), fuse_enabled=True)
    mock_config = RayExecutionConfig.from_execution_config(
        {
            "backend": "ray",
            "ray": {
                "monitor_interval_seconds": 0,
                "log_interval_seconds": 0,
                "subtask_max_retries": 0,
                "n_cpu": 1,
                "n_worker": 1,
                "gc_method": gc_method,
            },
        }
    )
    tile_context = MockTileContext()
    task_context = MockTaskContext()
    executor = MockRayTaskExecutor(
        config=mock_config,
        task=task,
        tile_context=tile_context,
        task_context=task_context,
        task_chunks_meta={},
        lifecycle_api=None,
        meta_api=None,
    )
    executor._ray_executor = RayTaskExecutor._get_ray_executor()

    original_execute_subtask_graph = executor._execute_subtask_graph

    async def _wait_gc_execute_subtask_graph(*args, **kwargs):
        # Mock _execute_subtask_graph to wait the monitor task done.
        await original_execute_subtask_graph(*args, **kwargs)
        await executor.monitor_tasks()[0]

    with mock.patch.object(
        executor, "_execute_subtask_graph", _wait_gc_execute_subtask_graph
    ):
        async with executor:
            await executor.execute_subtask_graph(
                "mock_stage", subtask_graph, chunk_graph, tile_context
            )
            await asyncio.sleep(0)
            assert len(executor.monitor_tasks()) == 1
            assert executor.monitor_tasks()[0].done()

    assert len(task_context) == 1
    assert len(popped_seq) == 6
    subtasks = list(subtask_graph.topological_iter())
    chunk_keys1 = set(
        map(
            lambda c: c.key,
            (
                subtasks[0].chunk_graph.results
                + subtasks[1].chunk_graph.results
                + subtasks[3].chunk_graph.results
            ),
        )
    )
    chunk_keys2 = set(
        map(
            lambda c: c.key,
            (subtasks[2].chunk_graph.results + subtasks[4].chunk_graph.results),
        )
    )
    assert chunk_keys1 == set(popped_seq[0:4])
    assert chunk_keys2 == set(popped_seq[4:])

    task_context.clear()

    original_update_progress_and_collect_garbage = (
        executor._update_progress_and_collect_garbage
    )

    async def infinite_update_progress_and_collect_garbage(*args, **kwargs):
        # Mock _update_progress_and_collect_garbage that never done.
        await original_update_progress_and_collect_garbage(*args, **kwargs)
        while True:
            await asyncio.sleep(0)

    with mock.patch("logging.Logger.info") as log_patch, mock.patch.object(
        executor,
        "_update_progress_and_collect_garbage",
        infinite_update_progress_and_collect_garbage,
    ):
        async with executor:
            await executor.execute_subtask_graph(
                "mock_stage2", subtask_graph, chunk_graph, tile_context
            )
            await asyncio.sleep(0)
            assert len(executor.monitor_tasks()) == 1
            assert executor.monitor_tasks()[0].done()
        assert log_patch.call_count > 0
        args = [c.args[0] for c in log_patch.call_args_list]
        assert any("Submitted [%s/%s]" in a for a in args)
        assert any("Completed [%s/%s]" in a for a in args)

    assert len(task_context) == 1

    task_context.clear()

    # Test the monitor aiotask is done even an exception is raised.
    async def _raise_load_subtask_inputs(*args, **kwargs):
        # Mock _load_subtask_inputs to raise an exception.
        await asyncio.sleep(0)
        1 / 0

    with mock.patch.object(
        executor, "_load_subtask_inputs", _raise_load_subtask_inputs
    ):
        async with executor:
            with pytest.raises(ZeroDivisionError):
                await executor.execute_subtask_graph(
                    "mock_stage3", subtask_graph, chunk_graph, tile_context
                )
            await asyncio.sleep(0)
            assert len(executor.monitor_tasks()) == 1
            assert executor.monitor_tasks()[0].done()

@require_ray
@pytest.mark.asyncio
@pytest.mark.parametrize("gc_method", ["submitted", "completed"])
async def test_execute_shuffle(ray_start_regular_shared2, gc_method):
    chunk_size, n_rows = 10, 50
    df = md.DataFrame(
        pd.DataFrame(np.random.rand(n_rows, 3), columns=list("abc")),
        chunk_size=chunk_size,
    )
    df2 = df.groupby(["a"]).apply(lambda x: x)
    chunk_graph, subtask_graph = _gen_subtask_graph(df2)
    task = Task("mock_task", "mock_session", TileableGraph([]), fuse_enabled=True)

    class MockRayExecutor:
        @staticmethod
        def options(**kwargs):
            num_returns = kwargs["num_returns"]

            class _Wrapper:
                @staticmethod
                def remote(*args):
                    args = [
                        ray.get(a) if isinstance(a, ray.ObjectRef) else a for a in args
                    ]
                    r = execute_subtask(*args)
                    assert len(r) == num_returns
                    return [ray.put(i) for i in r]

            return _Wrapper

    mock_config = RayExecutionConfig.from_execution_config(
        {
            "backend": "ray",
            "ray": {
                "monitor_interval_seconds": 0,
                "subtask_max_retries": 0,
                "n_cpu": 1,
                "n_worker": 1,
                "gc_method": gc_method,
            },
        }
    )
    tile_context = MockTileContext()
    task_context = {}
    executor = MockRayTaskExecutor(
        config=mock_config,
        task=task,
        tile_context=tile_context,
        task_context=task_context,
        task_chunks_meta={},
        lifecycle_api=None,
        meta_api=None,
    )
    executor._ray_executor = MockRayExecutor

    # Test ShuffleManager.remove_object_refs
    sm = ShuffleManager(subtask_graph)
    sm._mapper_output_refs[0].fill(1)
    sm.remove_object_refs(next(iter(sm._reducer_indices.keys())))
    assert pd.isnull(sm._mapper_output_refs[0][:, 0]).all()
    sm._mapper_output_refs[0].fill(1)
    sm.remove_object_refs(next(iter(sm._mapper_indices.keys())))
    assert pd.isnull(sm._mapper_output_refs[0][0]).all()
    with pytest.raises(ValueError):
        sm.remove_object_refs(None)

    original_execute_subtask_graph = executor._execute_subtask_graph

    async def _wait_gc_execute_subtask_graph(
        stage_id, subtask_graph, chunk_graph, monitor_context
    ):
        # Mock _execute_subtask_graph to wait the monitor task done.
        await original_execute_subtask_graph(
            stage_id, subtask_graph, chunk_graph, monitor_context
        )
        await executor.monitor_tasks()[0]
        assert pd.isnull(monitor_context.shuffle_manager._mapper_output_refs[0]).all()

    with mock.patch.object(
        executor, "_execute_subtask_graph", _wait_gc_execute_subtask_graph
    ), mock.patch("ray.get_runtime_context"):
        async with executor:
            await executor.execute_subtask_graph(
                "mock_stage", subtask_graph, chunk_graph, tile_context
            )
        await asyncio.sleep(0)
        assert len(executor.monitor_tasks()) == 1
        assert executor.monitor_tasks()[0].done()

    assert len(task_context) == len(chunk_graph.results)

@require_ray
@pytest.mark.asyncio
async def test_slow_subtask_checker():
    subtasks = [
        Subtask(str(i), logic_key=f"logic_key1", logic_parallelism=5) for i in range(5)
    ]
    for s in subtasks:
        s.runtime = _RaySubtaskRuntime()
    submitted = OrderedSet()
    completed = OrderedSet()
    now = time.time()
    checker = _RaySlowSubtaskChecker(5, submitted, completed)
    updater = checker.update()
    for s in subtasks:
        submitted.add(s)
    for _ in updater:
        break
    assert all(s.runtime.start_time >= now for s in subtasks)
    await asyncio.sleep(0.01)
    assert not any(checker.is_slow(s) for s in subtasks)
    completed.add(subtasks[0])
    completed.add(subtasks[1])
    for _ in updater:
        break
    await asyncio.sleep(0.01)
    completed.add(subtasks[2])
    assert not any(checker.is_slow(s) for s in subtasks[3:])
    completed.add(subtasks[3])
    for _ in updater:
        break
    assert not checker.is_slow(subtasks[4])
    await asyncio.sleep(0.1)
    assert checker.is_slow(subtasks[4])

@require_ray
@pytest.mark.asyncio
async def test_execute_slow_task(ray_start_regular_shared2):
    t1 = mt.random.randint(10, size=(100, 10), chunk_size=10)
    t2 = mt.random.randint(10, size=(100, 10), chunk_size=30)
    t3 = t2 + t1
    t4 = t3.sum(0)
    chunk_graph, subtask_graph = _gen_subtask_graph(t4)
    task = Task("mock_task", "mock_session", TileableGraph([]), fuse_enabled=True)
    mock_config = RayExecutionConfig.from_execution_config(
        {
            "backend": "ray",
            "ray": {
                "monitor_interval_seconds": 0,
                "log_interval_seconds": 0,
                "check_slow_subtasks_iqr_ratio": 1.5,
                "check_slow_subtasks_interval_seconds": 0,
                "subtask_max_retries": 0,
                "n_cpu": 1,
                "n_worker": 1,
            },
        }
    )
    tile_context = MockTileContext()
    executor = MockRayTaskExecutor(
        config=mock_config,
        task=task,
        tile_context=tile_context,
        task_context={},
        task_chunks_meta={},
        lifecycle_api=None,
        meta_api=None,
    )
    slow_subtask_id = list(subtask_graph)[-1].subtask_id

    def mock_execute_subtask(subtask_id, *args):
        if subtask_id == slow_subtask_id:
            time.sleep(2)
        return execute_subtask(subtask_id, *args)

    executor._ray_executor = ray.remote(mock_execute_subtask)

    with mock.patch("logging.Logger.info") as log_patch:
        async with executor:
            await executor.execute_subtask_graph(
                "mock_stage2", subtask_graph, chunk_graph, tile_context
            )
            await asyncio.sleep(0)
            assert len(executor.monitor_tasks()) == 1
            assert executor.monitor_tasks()[0].done()
        assert log_patch.call_count > 0
        slow_ray_object_refs = set()
        for c in log_patch.call_args_list:
            if c.args[0] == "Slow tasks(%s): %s":
                count, object_refs = c.args[1:]
                assert count >= 1
                slow_ray_object_refs.update(object_refs)
        assert len(slow_ray_object_refs) >= 1


#!/usr/bin/python3
"""lists State objects from the database hbtn_0e_6_usa that contains a"""
from model_state import Base, State
from sqlalchemy import create_engine, func
from sqlalchemy.orm import sessionmaker
from sys import argv

if __name__ == "__main__":
    engine = create_engine(
        "mysql://{}:{}@localhost:3306/{}".format(
            argv[1], argv[2], argv[3]
        )
    )
    session = sessionmaker(bind=engine)()
    nu = State(name='Louisiana')
    session.add(nu)
    session.commit()
    print(nu.id)


import requests
import json
import os
import time
import eventlet
from bs4 import BeautifulSoup
table = {} # 解码字典
table['w'] = 'a'
table['k'] = 'b'
table['v'] = 'c'
table['1'] = 'd'
table['j'] = 'e'
table['u'] = 'f'
table['2'] = 'g'
table['i'] = 'h'
table['t'] = 'i'
table['3'] = 'j'
table['h'] = 'k'
table['s'] = 'l'
table['4'] = 'm'
table['g'] = 'n'
table['5'] = 'o'
table['r'] = 'p'
table['q'] = 'q'
table['6'] = 'r'
table['f'] = 's'
table['p'] = 't'
table['7'] = 'u'
table['e'] = 'v'
table['o'] = 'w'
table['8'] = '1'
table['d'] = '2'
table['n'] = '3'
table['9'] = '4'
table['c'] = '5'
table['m'] = '6'
table['0'] = '7'
table['b'] = '8'
table['l'] = '9'
table['a'] = '0'
def Decode(url):  # 对百度加密的objRUL进行解码，
    ans = ''
    l = len(url)
    i = 0
    while i < l:
        if url[i] == '_':
            if url[i:i+6] == '_z2C$q':
                ans += ':'
                i += 6
            elif url[i:i+6] == '_z&e3B':
                ans += '.'
                i += 6
            else:
                ans += url[i]
                i += 1
        elif url[i] == 'A':
            if url[i:i+6] == 'AzdH3F':
                ans += '/'
                i += 6
            else:
                ans += url[i]
                i += 1
        else:
            try:
                ans += table[url[i]]
                i += 1
            except:
                ans += url[i]
                i += 1
    return ans
if __name__ == '__main__':
    # base_url：后台请求request URL：访问之后是JSON数据，保存即将加载的图片，其中每张图片的URL在json中的objURL
    base_url ='http://image.baidu.com/search/acjson?tn=resultjson_com&ipn=rj&ct=201326592&is=&fp=result&queryWord=piao+bao+ying&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=0&ic=&hd=&latest=&copyright=&word=piao+bao+ying&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&rn=30&'
    os.makedirs('./img/', exist_ok=True)
    eventlet.monkey_patch()  # 激活 eventlet模块
    print(f'come here')
    num = 1
    for j in range(5):
        print(f'正在爬取第{j}组数据.......')
        suf_url = 'pn=' + str(j*30)
        html = json.loads(requests.get(base_url + suf_url).text) # 将JSON数据转换为dict格式
        for item in html['data']:
            try:
                url = Decode(item['objURL'])
                print(url)
                with eventlet.Timeout(2, False):  # 部分图片加载失败，所以限时2s,超过直接跳过
                    img = requests.get(url)
                    with open(f'./img/img{num}.png', 'wb') as f:
                        f.write(img.content)
                        num += 1
            except:
                pass


from flask import Flask, request, render_template, send_file, url_for, jsonify, flash, redirect
from werkzeug.utils import secure_filename
import os

# imported custom modules
from Modules import merge_pdfs, watermark_pdfs, image_editor, split_pdf, file_compression, perform_ocr, crop_image, file_conversion, feedback_support

app = Flask(__name__)
app.secret_key = 'Aditya@123    '

# create directories if they don't exist
if not os.path.exists('uploads'):
    os.makedirs('uploads')
if not os.path.exists('downloads'):
    os.makedirs('downloads')

@app.route('/')
def main():
    return render_template('main.html')

# link to function section from navbar
@app.route('/functions')
def functions():
    return render_template('main.html')

# merge pdf route
@app.route('/merge', methods=['GET', 'POST'])
def merge_pdfs_route():
    if request.method == 'POST':
        pdf_files = request.files.getlist('pdfs')
        merger = merge_pdfs.merge_pdfs(pdf_files)

        try:
            merged_filename = 'merged_pdf.pdf'
            merged_path = os.path.join('downloads', merged_filename)
            merger.write(merged_path)
            merger.close()

            # Flash a success message
            flash("PDFs merged successfully!", 'success')

            # Open a window to save the merged PDF file
            return send_file(merged_path, as_attachment=True)
        except Exception as e:
            # Flash an error message
            flash(f"Error: {str(e)}", 'error')
            return redirect(url_for('merge_pdfs_route'))

    return render_template('merge.html', merged_pdf=None)

# Watermark PDFs route
@app.route('/watermark', methods=['POST', 'GET'])
def watermark_pdfs_route():
    if request.method == 'POST':
        watermark_file = request.files['watermark']
        input_pdf = request.files['input_pdf']

        if watermark_file and input_pdf:
            watermark_path = 'uploads/' + watermark_file.filename
            input_pdf_path = 'uploads/' + input_pdf.filename

            watermark_file.save(watermark_path)
            input_pdf.save(input_pdf_path)

            output_filename = f'watermarked_{input_pdf.filename}'
            output_path = os.path.join('downloads', output_filename)

            try:
                watermark_pdfs.apply_watermark(
                    input_pdf_path, watermark_path, output_path)

                # Flash a success message
                flash("PDFs watermarked successfully!", 'success')

                # Open a window to save the watermarked PDF file
                return send_file(output_path, as_attachment=True)

            except Exception as e:
                # Flash an error message if there's an exception
                flash(f"Error: {str(e)}", 'error')

    return render_template('watermark.html', watermark_pdfs=None)

# PDF Splitting route
@app.route('/split_pdf', methods=['POST', 'GET'])
def pdf_split_route():
    if request.method == 'POST':
        if 'pdf_file' in request.files and 'page_range' in request.form:
            pdf_file = request.files['pdf_file']
            page_range = request.form['page_range']

            if pdf_file and page_range:
                try:
                    # Save the uploaded PDF file
                    filename = secure_filename(pdf_file.filename)
                    pdf_path = os.path.join('uploads', filename)
                    pdf_file.save(pdf_path)

                    # Calling the split_pdf function
                    output_path = split_pdf.split_pdf(pdf_path, page_range)

                    if output_path:
                        # Flash a success message
                        flash("PDF split successfully!", 'success')

                        # Open a window to save the split PDF file
                        return send_file(output_path, as_attachment=True)
                    else:
                        # Flash an error message for invalid page range
                        flash("Invalid page range", 'error')
                        return redirect(url_for('pdf_split_route'))
                except Exception as e:
                    # Flash an error message if there's an exception
                    flash(f"Error: {str(e)}", 'error')
                    return redirect(url_for('pdf_split_route'))

    return render_template('split_pdf.html')

# File Compression route
@app.route('/compress', methods=['GET', 'POST'])
def compress_file():
    if request.method == 'POST':
        input_file = request.files['input_file']
        compression_level = request.form['compression_level']

        if input_file:
            # Save the uploaded file to the 'uploads' directory
            filename = secure_filename(input_file.filename)
            file_path = os.path.join('uploads', filename)
            input_file.save(file_path)

            # Call the file_compression function to compress the file
            success, compressed_file_path = file_compression.compress(
                file_path, compression_level)

            if success:
                # Flash a success message and provide the compressed file for download
                flash("File compressed successfully!", 'success')
                return send_file(compressed_file_path, as_attachment=True)

            # If compression fails, flash an error message
            flash("File compression failed. Please try again.", 'error')

    return render_template('compress.html')

# Image Editor route
@app.route('/image_editor', methods=['POST', 'GET'])
def image_editor_route():
    if request.method == 'POST':
        if 'image_file' in request.files:
            image_file = request.files['image_file']
            if image_file.filename != '':
                input_image_path = os.path.join('uploads', image_file.filename)
                image_file.save(input_image_path)

                operation = request.form['operation']
                parameters = {}

                if operation == "resize":
                    parameters['size'] = (800, 600)
                elif operation == "rotate":
                    # Default rotation degree 90 degrees
                    parameters['degrees'] = 90
                elif operation == "sharpen":
                    pass
                elif operation == "brightness":
                    parameters['factor'] = 1.5
                elif operation == "contrast":
                    parameters['factor'] = 1.5
                elif operation == "grayscale":
                    pass
                elif operation == "horizontal_flip":
                    pass
                elif operation == "vertical_flip":
                    pass
                elif operation == "sepia_filter":
                    pass
                elif operation == "grayscale_with_custom_threshold":
                    parameters['threshold'] = 128
                elif operation == "invert_colors":
                    pass
                else:
                    return "Invalid operation. Supported operations: resize, rotate, sharpen, brightness, contrast, grayscale, horizontal_flip, vertical_flip, sepia_filter, custom_threshold, invert_colors"

                edited_image_path = image_editor.edit_image(
                    input_image_path, operation, parameters)

                # Flash a success message
                flash("Image editing successful!", 'success')

                # After applying edits to the image, open a window to save it
                return send_file(edited_image_path, as_attachment=True)

    return render_template('image_editor.html')

# OCR route (Optical Character Recognition)
@app.route('/perform_ocr', methods=['GET', 'POST'])
def perform_ocr_route():
    if request.method == 'POST':
        if 'image_file' in request.files:
            image_file = request.files['image_file']
            if image_file.filename != '':
                # Save the uploaded image
                image_path = os.path.join('uploads', image_file.filename)
                image_file.save(image_path)

                language = request.form.get('language', 'eng')
                threshold = int(request.form.get('threshold', 128))
                segmentation_mode = int(
                    request.form.get('segmentation_mode', 6))

                # Perform OCR and store the result
                ocr_result = perform_ocr.perform_ocr(
                    image_path, language, threshold, segmentation_mode)

                return render_template('ocr.html', ocr_result=ocr_result)

    return render_template('ocr.html', ocr_result=None)

# Image Cropping route
@app.route('/crop_image', methods=['POST', 'GET'])
def crop_image_route():
    if 'image_file' in request.files:
        input_file = request.files['image_file']
        if input_file.filename != '':
            try:
                # Save the uploaded image
                filename = secure_filename(input_file.filename)
                input_path = os.path.join('uploads', filename)
                input_file.save(input_path)

                x = int(request.form.get('x', 0))
                y = int(request.form.get('y', 0))
                width = int(request.form.get('width', 0))
                height = int(request.form.get('height', 0))

                coordinates = (x, y, x + width, y + height)

                # Modify the output path using the uploaded file's name
                output_path = os.path.join('uploads', 'cropped_' + filename)

                if crop_image.crop_image(input_path, output_path, coordinates):
                    # Flash a success message
                    flash("Image cropping successful!", 'success')
                    return send_file(output_path, as_attachment=True)
                else:
                    # Flash an error message for failed cropping
                    flash("Image cropping failed.", 'error')
            except Exception as e:
                # Flash an error message if there's an exception
                flash(f"Error: {str(e)}", 'error')

    return render_template('crop_image.html')

# File Conversion route
@app.route('/convert_file', methods=['POST', 'GET'])
def convert_file_route():
    if 'input_file' in request.files:
        input_file = request.files['input_file']
        if input_file.filename != '':
            try:
                # Save the uploaded file with a secure filename in the 'uploads' folder
                filename = secure_filename(input_file.filename)
                input_path = os.path.join('uploads', filename)
                input_file.save(input_path)

                # Get input and output formats
                input_format = request.form.get('input_format', 'png')
                output_format = request.form.get('output_format', 'png')

                # Check if the conversion is supported
                if (input_format, output_format) in file_conversion.convert_file:
                    # Generate output filename
                    output_filename = f'converted_file.{output_format}'

                    # Output file path in the 'uploads' folder
                    output_path = os.path.join('uploads', output_filename)

                    # Call the conversion function using the dictionary
                    conversion_function = file_conversion.convert_file[(
                        input_format, output_format)]
                    success = conversion_function(input_path, output_path)

                    if success:
                        # Flash a success message
                        flash("File conversion successful!", 'success')

                        # Open a window to save the converted file
                        return send_file(output_path, as_attachment=True)
                    else:
                        # Flash an error message if the conversion fails
                        flash("File conversion failed", 'error')
                else:
                    flash("Unsupported conversion: {} to {}".format(
                        input_format, output_format), 'error')
            except Exception as e:
                # Flash an error message if there's an exception
                flash(f"Error: {str(e)}", 'error')

    return render_template('conversion.html', ocr_result=None)

# Feedback Route
@app.route('/feedback', methods=['GET', 'POST'])
def feedback_route():
    if request.method == 'POST':
        feedback = request.form['feedback']
        feedback_support.store_feedback(feedback)
        flash("Feedback submitted successfully!", "success")
        return redirect(url_for('feedback_route'))

    return render_template('feedback_form.html')

# feedback submit

@app.route('/submit_feedback', methods=['POST'])
def submit_feedback():
    if request.method == 'POST':
        feedback = request.form['feedback']
        feedback_support.store_feedback(feedback)
        flash("Feedback submitted successfully!", "success")
        return redirect(url_for('feedback_route'))
    else:
        return redirect(url_for('feedback_route'))

# 404 Error Page
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

if __name__ == '__main__':
    app.run(debug=True)


from __future__ import division
from vector3 import Vector3
from physics_object import PhysicsObject

class ParticleEmitter(object):

    def __init__(self,
                 num_of_particles,
                 position,
                 lifetime,
                 velocity=Vector3(0, -100, 0),
                 acceleration=Vector3(0, 50, 0),
                 x_angle=100,
                 z_angle=100,
                 spawn_rate=5):
        self.num_of_particles = num_of_particles
        self.position = position
        self.velocity = velocity
        self.acceleration = acceleration
        self.x_angle = x_angle
        self.z_angle = z_angle
        self.lifetime = lifetime
        self.spawn_rate = spawn_rate

        self.particles = []
        for _ in range(num_of_particles):
            particle = PhysicsParticle(lifetime,
                                       velocity=velocity,
                                       acceleration=acceleration,
                                       x_angle=x_angle,
                                       z_angle=z_angle,
                                       position=position)
            self.particles.append(particle)

    def update(self, time_step):
        sphereDetail(5)
        for particle in self.particles:
            particle.update(time_step)
            if not particle.alive:
                self.particles.remove(particle)

        for _ in range(self.spawn_rate):
            particle = PhysicsParticle(self.lifetime,
                                       velocity=self.velocity,
                                       acceleration=self.acceleration,
                                       x_angle=self.x_angle,
                                       z_angle=self.z_angle,
                                       position=self.position)
            self.particles.append(particle)
        sphereDetail(30)

class ParticleExplosion(object):

    def __init__(self,
                 position,
                 lifetime,
                 velocity_factor=50,
                 acceleration=Vector3(0, 50, 0),
                 x_angle=100,
                 z_angle=100,
                 spawn_rate=5,
                 steps=20):
        self.position = position
        self.velocity_factor = velocity_factor
        self.acceleration = acceleration
        self.x_angle = x_angle
        self.z_angle = z_angle
        self.lifetime = lifetime
        self.spawn_rate = spawn_rate
        self.steps = steps

        self.directions = ParticleExplosion.get_explosion_directions(steps)
        self.particles = []
        for direction in self.directions:
            particle = PhysicsParticle(lifetime,
                                       add_noise=False,
                                       acceleration=acceleration)
            particle.velocity = direction*random(velocity_factor/2, velocity_factor)
            particle.position = self.position
            self.particles.append(particle)

    def update(self, time_step):
        sphereDetail(5)
        for particle in self.particles:
            particle.update(time_step)
            if not particle.alive:
                self.particles.remove(particle)

        sphereDetail(30)

    @staticmethod
    def get_explosion_directions(steps=10):
        """Find the direction particles will have
        using equation of a sphere. The particles
        will move outwards from center.
        steps: number of particles around the
        circumfrenece

        sphere => x^2 + y^2 + z^2 = R
        """
        directions = []
        y_step = 2 / steps
        # Move y from -1..1 and find x,z going in a circle for each
        for i in range(1, steps):
            y = y_step * i - 1
            r = 1 - y * y
            thetha_step = 360 / steps
            for j in range(steps):
                x = cos(radians(thetha_step * j)) * r
                z = sin(radians(thetha_step * j)) * r
                directions.append(Vector3(x, y, z))
        # We dont' want multiple particles on the top and bottom
        directions.append(Vector3(0, 1, 0))
        directions.append(Vector3(0, -1, 0))
        return directions

class PhysicsParticle(PhysicsObject):

    def __init__(self,
                 lifetime,
                 velocity=Vector3(),
                 acceleration=Vector3(),
                 position=Vector3(),
                 x_angle=100,
                 z_angle=100,
                 add_noise=True,
                     radius=3):
        super(PhysicsParticle, self).__init__()
        self.lifetime = lifetime
        self.time_remaining = lifetime
        self.velocity = velocity
        self.acceleration = acceleration
        self.x_angle = x_angle
        self.z_angle = z_angle
        self.radius = radius
        self.color = color(random(255), random(255), random(255))
        if add_noise:
            self.add_noise()

    def add_noise(self):
        axis = [0, 0, 0]
        limit_angles = [self.x_angle, self.velocity.y / 2, self.z_angle]
        for i in range(3):
            seed = random(1)
            r = noise(seed)
            axis[i] = limit_angles[i] * r - limit_angles[i] / 2

        self.velocity = Vector3(axis[0], axis[1], axis[2]) + self.velocity
        acceleration_limit = 10
        self.acceleration = self.acceleration + \
            Vector3(random(-acceleration_limit, acceleration_limit),
                    random(-acceleration_limit, acceleration_limit),
                    random(-acceleration_limit, acceleration_limit))

    @property
    def alive(self):
        return self.time_remaining > 0

    def update(self, time_step):
        super(PhysicsParticle, self).update(time_step)
        self.time_remaining -= time_step
        self.display()

    def display(self):
        pushMatrix()
        noStroke()
        fill(self.color,
             self.time_remaining / self.lifetime * 255 if self.alive else 0)
        translate(self.position.x,
                  self.position.y,
                  self.position.z)
        sphere(self.radius)
        popMatrix()


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

# lists

# In[1]:

list1=[1,2,3,4,"pineapple","grapes","mango",33.33,3.14,0.000000067]

# In[2]:

list1[0]="coconut"

# In[3]:

print(list1)

# tuple

# In[4]:

t1=(1,2,3,4,5,6,7,8,9)

# In[5]:

t1[0]

# In[6]:

t1[5]

# In[7]:

t1[3]=55
#it shows error because tuples are unmutable

# dictionary

# In[8]:

my_dict = {'key1':'value1','key2':'value2','key3':'value3','key4':'value4'}

# In[9]:

del my_dict['key4']

# In[10]:

print(my_dict)

# In[11]:

deleted=my_dict.pop('key1')

# In[12]:

print(deleted)

# In[13]:

print(my_dict)


# Downloading Hungarian Wordnet from original source
import requests
import os
from importlib_resources import files

class WordNetDownloader:
    def __init__(self, url_wordnet="https://rgai.inf.u-szeged.hu/sites/rgai.inf.u-szeged.hu/files/HuWN.zip",
                 wordnet_zip_path=files("resources") / "HuWN.zip",
                 wordnet_xml_path=files("resources") / "HuWN_final4.xml"):
        self.wordnet_zip_file = wordnet_zip_path
        self.url_wordnet = url_wordnet
        self.wordnet_xml_path = wordnet_xml_path
        self.download_needed = False

    def check_existing_file(self):
        if not os.path.isfile(self.wordnet_xml_path):
            print("Wordnet xml file missing, downloading from original source.")
            self.download_needed = True
        else:
            self.download_needed = False

    def download(self):
        try:
            # downloading from original source
            data = requests.get(self.url_wordnet, allow_redirects=True)
            with open(self.wordnet_zip_file, 'wb') as file:
                file.write(data.content)
        except ValueError:
            print("Download failed.")

    def unzip(self):
        # unzipping
        os.system('unzip {} -d {}'.format(self.wordnet_zip_file, files("resources")))

    def cleanup(self):
        # deleting zip file
        os.remove(self.wordnet_zip_file)

    def run(self):
        self.check_existing_file()
        if self.download_needed:
            self.download()
            self.unzip()
            self.cleanup()

if __name__ == "__main__":
    downloader = WordNetDownloader()
    downloader.run()


import sys
import math

def mymod(n: int, m: int) -> int:
    return n - math.floor(n / m) * m

mymod(20, 7)

with open(sys.argv[1], 'r') as test_cases:
    for test in test_cases:
        args = test.split(",")
        n = int(args[0])
        m = int(args[1])
        print(mymod(n, m))


import argparse
import asyncio
import json
from datetime import datetime

from sqlalchemy.orm import Session

from pptx_clarifier.db import engine
from pptx_clarifier.db.models import Upload
from pptx_clarifier.pptx_explainer import initial_prompt, explainer_logger as logger
from pptx_clarifier.pptx_explainer.openai_interactor import interact
from pptx_clarifier.pptx_explainer.presentation_parser import open_presentation, parse_slide

def create_slide_prompt(index, slide):
    """
    Prepares a slide prompt for the AI.

    Args:
        index (int): Index of the slide.
        slide (slide): Slide to be parsed.

    Returns:
        str: The prompt for the AI.
    """
    # Prepare the message input for the current slide
    parsed_slide = parse_slide(slide)
    slide_prompt = json.dumps({f"Slide-{index}": parsed_slide})

    # Return the message to the AI
    return slide_prompt

async def process_presentation(upload, session):
    """
    Opens a presentation and parses its slides to OpenAI chat prompts.
    Each prompt is sent to the AI asynchronously.

    Args:
        upload: Path to the presentation file.
        session: The database session.

    Returns:
        list: List of all responses from the AI.
    """
    messages = [{"role": "system", "content": initial_prompt}]

    # Open the presentation
    presentation = open_presentation(upload.get_upload_path())
    responses = {}
    upload.status = "pending"
    session.commit()

    # Parse each slide to prompt and send it to the AI asynchronously
    for index, slide in enumerate(presentation.slides, start=1):
        try:
            prompt = create_slide_prompt(index, slide)

            if prompt is None:
                responses[f"Slide {index}"] = "No text to clarify"

            else:
                res = await interact(prompt, messages)
                responses[f"Slide {index}"] = res

        except ConnectionError as connection_error:
            logger.error(connection_error)
            responses[f"Slide {index}"] = f"Could not clarify slide {index}"

    return responses

async def clarify(upload, session):
    """
    Generates explanations for a PowerPoint presentation and saves it to a JSON file.

    Args:
        upload : The uploaded presentation file object.
        session: The database session.

    Raises:
        PathError: If the path does not exist.
        ValueError: If the path is not a pptx file.
        Exception: If an error occurs while parsing the presentation.
    """
    logger.info(f'Clarifying {upload.filename}')
    explanations = await process_presentation(upload, session)

    with open(str(upload.get_output_path()), "w") as file:
        json.dump(explanations, file)

    logger.info(f'Finished clarifying {upload.filename}')

if __name__ == "__main__":
    # Create an argument parser
    parser = argparse.ArgumentParser(description='Script to clarify pptx files.')

    # Add an argument for the file path
    parser.add_argument('file_path', nargs=1, type=str, help='Path to the pptx file')

    # Parse the command-line arguments
    args = parser.parse_args()

    # Extract the file path from the arguments
    file_path = args.file_path[0]

    # Check if the file path argument is missing
    if not file_path:
        parser.error('Please provide a path to the pptx file.')

    try:
        upload_file = Upload(upload_time=datetime.now(), filename=file_path, status="processing")
        # Call the function to process the file
        asyncio.run(clarify(upload_file))
    except Exception as e:
        print(e)


import requests, os, datetime, pandas as pd

def downLoadAndCopy(inputLink, outputLink):
    r = requests.get(inputLink)
    with open(outputLink, 'wb') as f:
        f.write(r.content)

'''

The following code downloads the bhavcopy for derivatives and equity
as well as the list of banned securities from NSE server.
Required inputs
1) Date as string input in 'ddmmyyyy' format,
2) holidays and no holidays dates stored in csv files under the column header 'Date'
3) The path to the directory where output from the program will be stored.

Please see below block to check user input variables.

'''

############################# User inputs here ###################################
# Enter the date for download in 'ddmmyyyy' format
date = '19122019'

# Enter the path for output
outputLink = r'D:\Work\NSEDownloader\Output'

# Path to the 'holidays file'
holidayListPath = r"D:\Work\NSEDownloader\Holidaylist_NSE.csv"

# Path to the 'no holidays file'
noHolidayListPath = r"D:\Work\NSEDownloader\NoHoliday_NSE.csv"

##################################################################################
# Assertions about files
assert os.path.isfile(holidayListPath), "The following path for the holidays list file is incorrect.\n" + holidayListPath
assert os.path.isfile(noHolidayListPath), "The following path for the no holidays list file is incorrect.\n" + noHolidayListPath

# Convert string to datetime object
dateMod = datetime.datetime.strptime(date, "%d%m%Y")

# Read the 'holidays file' and 'no holidays file' and convert them to required string format
holidays = pd.read_csv(holidayListPath, encoding='ISO-8859-1')['Date'].dropna()
noHolidays = pd.read_csv(noHolidayListPath, encoding='ISO-8859-1')['Date'].dropna()

holidays = holidays.map(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d"))
noHolidays = noHolidays.map(lambda x: datetime.datetime.strptime(x, "%Y-%m-%d"))

holidays = holidays.map(lambda x: datetime.datetime.strftime(x, "%d%m%Y"))
noHolidays = noHolidays.map(lambda x: datetime.datetime.strftime(x, "%d%m%Y"))

# Check if date is in list of holidays
if date in list(holidays):
    raise Exception("The given date corresponds to a holiday.")

# Check if date is a weekend and date is a 'no holiday day' for the markets
if (datetime.datetime.strftime(dateMod, "%w") in ['0','6']):
    if date not in list(noHolidays):
        raise Exception("The given date corresponds to a weekend.")

# Downloading for banned securities
foBanFileName = 'fo_secban_' + date + '.csv'
foBanLink = 'https://www.nseindia.com/archives/fo/sec_ban/'

downLoadAndCopy(foBanLink + foBanFileName, os.path.join(outputLink, foBanFileName))

# Downloading historical data for equities
equityFileName = 'cm' + datetime.datetime.strftime(dateMod, '%d%b%Y').upper() + 'bhav.csv.zip'
equityFileLink = 'https://www.nseindia.com/content/historical/EQUITIES/' + datetime.datetime.strftime(dateMod, "%Y") + '/' + datetime.datetime.strftime(dateMod, "%b").upper()  + '/' + equityFileName

downLoadAndCopy(equityFileLink, os.path.join(outputLink, equityFileName))

# Downloading historical data for derivatives
derivFileName = 'fo' + datetime.datetime.strftime(dateMod, '%d%b%Y').upper() + 'bhav.csv.zip'
derivFileLink = 'https://www.nseindia.com/content/historical/DERIVATIVES/' + datetime.datetime.strftime(dateMod, "%Y") + '/' + datetime.datetime.strftime(dateMod, "%b").upper()  + '/' + derivFileName

downLoadAndCopy(derivFileLink, os.path.join(outputLink, derivFileName))


from vapoursynth import core, YUV420P8
import awsmfunc as awf

heads = {}

hattoushin = core.std.BlankClip(width=1024, height=768, length=6000, fpsnum=15, color=[255, 255, 255])

fc = len(hattoushin)

head_start = -1

hattoushin_id = 12

head_first = True

for frame in range(fc):
    if head_start >= frame:
        continue

    head_end = frame
    if hattoushin_id not in heads:
        heads[hattoushin_id] = ""
    if head_first:
        head_end = 2
    head_first = False
    heads[hattoushin_id] += f"{frame} "
    head_start = head_end
    hattoushin_id += 1
    if hattoushin_id > 12:
        hattoushin_id = 1

for rng, frames in heads.items():
    hattoushin = awf.ReplaceFrames(hattoushin, core.std.AssumeFPS(core.imwri.Read(f"吹っ切れた/furifuri/fuirfuir-{rng:02}.png")*fc, fpsnum=15), frames)

hattoushin = core.std.AssumeFPS(hattoushin, fpsnum=15)

hattoushin = hattoushin.resize.Point(format=YUV420P8, matrix_s="709")[3:1353]

hattoushin.set_output()


#A program which stores data into pickles and then can be unpickled later

import pickle
def save():
    shyam = {'key': 'shyam', 'name': 'shyam khatiwada', 'age': 30}
    ram = {'key': 'ram', 'name': 'ram baral', 'age': 27}

    db={}
    db['shyam']=shyam
    db['ram']=ram

    file=open('xyz.txt','ab')
    pickle.dump(db,file)
    file.close()
def load():
    file = open('xyz.txt', 'rb')
    db=pickle.load(file)
    for keys in db:
        print(keys,':',db[keys])
        file.close()
save()
load()


#!/usr/bin/env python

# Listing 6
# cacluate similarity between pairs of SMILES files and report
# the most similar training set molecule for each test set molecule

import sys,string
from rdkit import Chem
from rdkit import DataStructs
from rdkit.Chem.Fingerprints import FingerprintMols

# build a list of fingerprints from an input file
def buildFingerprintList(fileName):
    suppl = Chem.SmilesMolSupplier(fileName,titleLine=False)
    fpList = []
    for idx,mol in enumerate(suppl):
        print >> sys.stderr,"\rGenerating fingerprints for %s " % (fileName),idx+1,
        sys.stderr.flush()
        fpList.append([mol.GetProp("_Name"),FingerprintMols.FingerprintMol(mol)])
    print >> sys.stderr,"\rGenerating fingerprints for %s " % (fileName),idx+1
    return fpList

# for each molecule in queryFpLIst, find and report the most similar molecule in refFpList
def findMostSimilar(refFpList,queryFpList):
    outList = []
    for idx,[name,fp] in enumerate(queryFpList):
        print >> sys.stderr,"\rCalculating similarity ",idx,
        sys.stderr.flush()
        simList = [[x[0],DataStructs.FingerprintSimilarity(fp,x[1])] for x in refFpList]
        simList.sort(lambda a,b: cmp(b[1],a[1]))
        outList.append([name,simList[0]])
    print >> sys.stderr,"\rCalculating similarity ",idx+1
    return outList

# setup training and test sets
trainingFiles = ["huuskonen_train.smi"]
testFiles = ["huuskonen_test.smi","jcim.smi","pubchem.smi"]
trainDict = {}
testDict = {}

# open the output file
ofs = open("similarity.txt","w")

# generate fingerprints
for fileName in trainingFiles:
    trainDict[fileName] = buildFingerprintList(fileName)
for fileName in testFiles:
    testDict[fileName] = buildFingerprintList(fileName)

# write the output
print >> ofs,"Query Reference Similarity Dataset"
for trainFileName,trainFpList in trainDict.iteritems():
    for testFileName,testFpList in testDict.iteritems():
        print >> sys.stderr,"Processing %s" % testFileName
        simList = findMostSimilar(trainFpList,testFpList)
        for query,[ref,sim] in simList:
            print >> ofs,query,ref,"%.2f" % (sim),testFileName.split(".")[0].upper()

print >> sys.stderr,"Results have been written to similarity.txt"


import numpy

from .. import MLPBaseTrainer, MLPBackPropTrainer, CrossEntropyLoss, SquareError
from ...machine import HyperbolicTangentActivation, LogisticActivation, IdentityActivation, MLP

class PythonBackProp(MLPBaseTrainer):
  """A simple version of the vanilla BackProp algorithm written in Python
  """

  def __init__(self, batch_size, cost, machine, train_biases,
      learning_rate=0.1, momentum=0.0):

    super(PythonBackProp, self).__init__(batch_size, cost, machine, train_biases)
    self.previous_derivatives = [numpy.zeros(k.shape, dtype=float) for k in machine.weights]
    self.previous_bias_derivatives = [numpy.zeros(k.shape, dtype=float) for k in machine.biases]

    self.learning_rate = learning_rate
    self.momentum = momentum

  def train(self, machine, input, target):

    # Run dataset through
    prev_cost = self.cost(machine, input, target)
    self.backward_step(machine, input, target)

    # Updates weights and biases
    new_weights = list(machine.weights)
    for k,W in enumerate(new_weights):
      new_weights[k] = W - (((1-self.momentum)*self.learning_rate*self.derivatives[k]) + (self.momentum*self.previous_derivatives[k]))
    self.previous_derivatives = [self.learning_rate*k for k in self.derivatives]
    machine.weights = new_weights

    if self.train_biases:
      new_biases = list(machine.biases)
      for k,B in enumerate(new_biases):
        new_biases[k] = B - (((1-self.momentum)*self.learning_rate*self.bias_derivatives[k]) + (self.momentum*self.previous_bias_derivatives[k]))
      self.previous_bias_derivatives = [self.learning_rate*k for k in self.bias_derivatives]
      machine.biases = new_biases

    return prev_cost, self.cost(machine, input, target)

def check_training(machine, cost, bias_training, batch_size, learning_rate,
    momentum):

  python_machine = MLP(machine)

  X = numpy.random.rand(batch_size, machine.weights[0].shape[0])
  T = numpy.zeros((batch_size, machine.weights[-1].shape[1]))

  python_trainer = PythonBackProp(batch_size, cost, machine, bias_training,
      learning_rate, momentum)
  cxx_trainer = MLPBackPropTrainer(batch_size, cost, machine, bias_training)
  cxx_trainer.learning_rate = learning_rate
  cxx_trainer.momentum = momentum

  # checks previous state matches
  for k,D in enumerate(cxx_trainer.previous_derivatives):
    assert numpy.allclose(D, python_trainer.previous_derivatives[k])
  for k,D in enumerate(cxx_trainer.previous_bias_derivatives):
    assert numpy.allclose(D, python_trainer.previous_bias_derivatives[k])
  for k,W in enumerate(machine.weights):
    assert numpy.allclose(W, python_machine.weights[k])
  for k,B in enumerate(machine.biases):
    assert numpy.allclose(B, python_machine.biases[k])
  assert numpy.alltrue(machine.input_subtract == python_machine.input_subtract)
  assert numpy.alltrue(machine.input_divide == python_machine.input_divide)

  prev_cost, cost = python_trainer.train(python_machine, X, T)
  assert cost <= prev_cost #this should always be true for a fixed dataset
  cxx_trainer.train(machine, X, T)

  # checks each component of machine and trainer, make sure they match
  for k,D in enumerate(cxx_trainer.derivatives):
    assert numpy.allclose(D, python_trainer.derivatives[k])
  for k,D in enumerate(cxx_trainer.bias_derivatives):
    assert numpy.allclose(D, python_trainer.bias_derivatives[k])
  for k,W in enumerate(machine.weights):
    assert numpy.allclose(W, python_machine.weights[k])
  for k,B in enumerate(machine.biases):
    assert numpy.allclose(B, python_machine.biases[k])
  assert numpy.alltrue(machine.input_subtract == python_machine.input_subtract)
  assert numpy.alltrue(machine.input_divide == python_machine.input_divide)

def test_2in_1out_nobias():

  machine = MLP((2, 1))
  machine.randomize()
  machine.hidden_activation = LogisticActivation()
  machine.output_activation = LogisticActivation()
  machine.biases = 0

  BATCH_SIZE = 10
  cost = CrossEntropyLoss(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, False, BATCH_SIZE, 0.1, 0.0)

def test_1in_2out_nobias():

  machine = MLP((1, 2))
  machine.randomize()
  machine.hidden_activation = LogisticActivation()
  machine.output_activation = LogisticActivation()
  machine.biases = 0

  BATCH_SIZE = 10
  cost = CrossEntropyLoss(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, False, BATCH_SIZE, 0.1, 0.0)

def test_2in_3_1out_nobias():

  machine = MLP((2, 3, 1))
  machine.randomize()
  machine.hidden_activation = HyperbolicTangentActivation()
  machine.output_activation = HyperbolicTangentActivation()
  machine.biases = 0

  BATCH_SIZE = 10
  cost = SquareError(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, False, BATCH_SIZE, 0.1, 0.0)

def test_100in_10_10_5out_nobias():

  machine = MLP((100, 10, 10, 5))
  machine.randomize()
  machine.hidden_activation = HyperbolicTangentActivation()
  machine.output_activation = HyperbolicTangentActivation()
  machine.biases = 0

  BATCH_SIZE = 10
  cost = SquareError(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, False, BATCH_SIZE, 0.1, 0.0)

def test_2in_3_1out():

  machine = MLP((2, 3, 1))
  machine.randomize()
  machine.hidden_activation = HyperbolicTangentActivation()
  machine.output_activation = HyperbolicTangentActivation()

  BATCH_SIZE = 10
  cost = SquareError(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, True, BATCH_SIZE, 0.1, 0.0)

def test_20in_10_5_3out():

  machine = MLP((20, 10, 5, 3))
  machine.randomize()
  machine.hidden_activation = HyperbolicTangentActivation()
  machine.output_activation = HyperbolicTangentActivation()

  BATCH_SIZE = 10
  cost = SquareError(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, True, BATCH_SIZE, 0.1, 0.0)

def test_20in_10_5_3out_with_momentum():

  machine = MLP((20, 10, 5, 3))
  machine.randomize()
  machine.hidden_activation = HyperbolicTangentActivation()
  machine.output_activation = HyperbolicTangentActivation()

  BATCH_SIZE = 10
  cost = SquareError(machine.output_activation)

  for k in range(10):
    check_training(machine, cost, True, BATCH_SIZE, 0.1, 0.1)


# Add the functions in this file
import json
import math
def load_journal(filename):
    f = open(filename)
    s=f.read()
    content = json.loads(s)
    f.close()
    return content

def compute_phi(filename,event):
    d=load_journal(filename)
    a=[0,0,0,0,0,0,0,0]
    for i in d:
        if(event in i['events']):
            a[4]+=1
            if(i['squirrel']):
                a[6]+=1
                a[0]+=1
            else :
                a[7]+=1
                a[2]+=1
        else:
            a[5]+=1
            if (i['squirrel']):
                a[6]+=1
                a[3]+=1
            else:
                a[7]+=1
                a[1]+=1
    val=(a[0] * a[1] - a[2] * a[3]) / math.sqrt(a[4] * a[5] * a[6] * a[7])
    return val

def compute_correlations(filename):
    data={}
    d=load_journal(filename)
    for i in d:
        for j in i['events']:
            if j not in data:
                data[j]=compute_phi(filename,j)
    return data
def diagnose(filename):
    data=compute_correlations(filename)
    maxval = max(data, key = data.get)
    minval = min(data, key = data.get)
    return maxval,minval


from my_tests.project import Caretaker
from my_tests.project import Cheetah
from my_tests.project import Keeper
from my_tests.project import Lion
from my_tests.project import Tiger
from my_tests.project import Vet

class Zoo:
    def __init__(self, name, budget, animal_capacity, workers_capacity):
        self.name = name
        self.__budget = budget
        self.__animal_capacity = animal_capacity
        self.__workers_capacity = workers_capacity
        self.animals = []
        self.workers = []

    def add_animal(self, animal, price):
        if price > self.__budget:
            return "Not enough budget"
        if len(self.animals) == self.__animal_capacity:
            return "Not enough space for animal"
        self.__budget -= price
        self.animals.append(animal)

        return f"{animal.name} the {animal.__class__.__name__} added to the zoo"

    def hire_worker(self, worker):
        if len(self.workers) == self.__workers_capacity:
            return "Not enough space for worker"
        self.workers.append(worker)

        return f"{worker.name} the {worker.__class__.__name__} hired successfully"

    def fire_worker(self, worker_name):
        for worker in self.workers:
            if worker.name == worker_name:
                self.workers.remove(worker)
                return f"{worker_name} fired successfully"

        return f"There is no {worker_name} in the zoo"

    def pay_workers(self):
        all_workers_salary = 0
        for worker in self.workers:
            all_workers_salary += worker.salary

        if all_workers_salary > self.__budget:
            return "You have no budget to pay your workers. They are unhappy"

        self.__budget -= all_workers_salary
        return f"You payed your workers. They are happy. Budget left: {self.__budget}"

    def tend_animals(self):
        animal_care_spending = 0

        for animal in self.animals:
            animal_care_spending += animal.money_for_care

        if animal_care_spending > self.__budget:
            return "You have no budget to tend the wild_farm. They are unhappy."

        self.__budget -= animal_care_spending
        return f"You tended all the wild_farm. They are happy. Budget left: {self.__budget}"

    def profit(self, amount):
        self.__budget += amount

    def animals_status(self):
        result = f"You have {len(self.animals)} wild_farm\n"

        lions = [repr(a) for a in self.animals if isinstance(a, Lion)]
        result += f"----- {len(lions)} Lions:\n" + '\n'.join(lions) + '\n'

        tigers = [repr(a) for a in self.animals if isinstance(a, Tiger)]
        result += f"----- {len(tigers)} Tigers:\n" + '\n'.join(tigers) + '\n'

        cheetahs = [repr(a) for a in self.animals if isinstance(a, Cheetah)]
        result += f"----- {len(cheetahs)} Cheetahs:\n" + '\n'.join(cheetahs) + '\n'

        return result.strip()

    def workers_status(self):
        result = f"You have {len(self.workers)} workers\n"

        keepers = [repr(a) for a in self.workers if isinstance(a, Keeper)]
        result += f"----- {len(keepers)} Keepers:\n" + '\n'.join(keepers) + '\n'

        caretakers = [repr(a) for a in self.workers if isinstance(a, Caretaker)]
        result += f"----- {len(caretakers)} Caretakers:\n" + '\n'.join(caretakers) + '\n'

        vets = [repr(a) for a in self.workers if isinstance(a, Vet)]
        result += f"----- {len(vets)} Vets:\n" + '\n'.join(vets) + '\n'

        return result.strip()


import time
import sys
import math
import machine
import uasyncio as asyncio

# A generic debounce coroutine that monitors multiple pins.
# Derived classes can overlaod pin_value_did_change to be notified
# when the state of a pin changes
class PinMonitor:
    # pin_ids: A dictionary that matches pin_names to pin_ids. The pins
    #          in the dictionary will be configured as inputs. When a pin
    #          changes state its name will be passed to pin_value_did_change.
    #          If any pin_id is None then the pin will not be configured.
    def __init__(self, pin_ids, pull = machine.Pin.PULL_UP):
        # Filter out any pins that have no id
        self.pin_ids = {pin_name:pin_id for pin_name,pin_id in pin_ids.items() if pin_id is not None}
        self.pull = pull

    def pin_value_did_change(self, pin_name, pin_state, new_value, previous_duration):
        pass

    # Repeats a function until a pin changes state. The repeat interval will start long
    # and decrease with each repeat.
    def repeat_action_until(self, pin_name, action, min_time = 100, max_time = 1000, ramp_ticks = 10):
        asyncio.create_task(self._repeat_action_until(self.pins[pin_name].event, action, min_time, max_time, ramp_ticks))

    async def _repeat_action_until(self, event, action, min_time, max_time, ramp_ticks):
        i = 0
        time_range = max_time - min_time
        # A sigmoid function that will be close to 1 when i = 0 and close to 0
        # when i = ramp_ticks. This will ramp the button interval down smoothly
        sigmoid = lambda i, ramp_ticks: 0.5 - math.atan((i - ramp_ticks/2)*7.5/ramp_ticks)/math.pi
        while True:
            try:
                # Fire the action
                action()

                delay = int(min_time + time_range * sigmoid(i, ramp_ticks))
                i = i + 1
                # Delay until either the event is triggered or the delay times out
                await asyncio.wait_for_ms(event.wait(), delay)
                # The pin has changed state, so the repeat is over
                return
            except asyncio.TimeoutError:
                # A timeout without a pin state change means the button is still down
                pass

    # Waits for a pin to change state.
    async def await_pin(self, pin_name, timeout):
        await asyncio.wait_for_ms(self.pins[pin_name].event.wait(), timeout)

    async def _run(self, bounce_time, poll_interval):
        # Map the pin ids to configured pin instances
        pins = { pin_name: PinState(pin_id, self.pull) for (pin_name, pin_id) in self.pin_ids.items()}
        self.pins = pins

        self.running = True
        while self.running:
            try:
                for (pin_name, pin_state) in pins.items():
                    value = pin_state.update(bounce_time)
                    if value is not None:
                        self.pin_value_did_change(pin_name, value[0], value[1])

                await asyncio.sleep_ms(poll_interval)
            except Exception as e:
                print("Error processing pin change.")
                sys.print_exception(e)

    def run(self, bounce_time = 20, poll_interval = 1):
        self.run_task = asyncio.create_task(self._run(bounce_time, poll_interval))

    def stop(self):
        self.running = False

class PinState:
    def __init__(self, pin_id, pull):
        self.pin = machine.Pin(pin_id, machine.Pin.IN, pull)
        self.value = self.pin.value()
        self.counter = 0
        self.previous_state_change_time = time.ticks_ms()
        self.event = asyncio.Event()

    def update(self, bounce_time):
        # Read the current state of the pin
        current_value = self.pin.value()

        # If the current value is different than the stored value increment the counter,
        # otherwise reset it
        pin_count = self.counter + 1 if current_value != self.value else 0

        if pin_count >= bounce_time:
            # The pin has been stable at this new value for the bounce interval
            # Switch the pin status and call the callback
            self.value = current_value
            self.counter = 0

            now = time.ticks_ms()
            previous_duration = time.ticks_diff(now, self.previous_state_change_time)
            self.previous_state_change_time = now
            self.event.set()
            self.event.clear()

            return (current_value, previous_duration)
        else:
            self.counter = pin_count

        return None


import jax
import jax.numpy as jnp
import numpy as np
import logging

from ase.neighborlist import primitive_neighbor_list
from functools import partial
from typing import Dict
from tqdm import tqdm

from mlff.geometric.metric import coordinates_to_distance_matrix, coordinates_to_distance_matrix_mic
from mlff.utils import PrimitiveNeighbors

def get_indices(R: np.ndarray, z: np.ndarray, r_cut: float, cell: np.ndarray = None, mic: bool = False):
    """
        For the `n_data` data frames, return index lists for centering and neighboring atoms given some cutoff radius
        `r_cut` for each structure. As atoms may leave or enter the neighborhood for a given atom within the dataset,
        one can have different lengths for the index lists, even for same structures. Thus, the index lists are padded
        wrt the length `n_pairs_max` of the longest index list observed over all frames in the coordinates `R` across
        all structures. Note, that this is suboptimal if one has a wide range number of atoms in the same dataset. The
        coordinates `R` and the atomic types `z` can also be already padded (see `mlff.src.padding.padding`) and are
        assumed to be padded with `0` if padded. Index values are padded with `-1`.

        Args:
            R (Array): Atomic coordinates, shape: (n_data,n,3)
            z (Array): Atomic types, shape: (n_data, n)
            r_cut (float): Cutoff distance
            cell (Array): Unit cell with lattice vectors along rows (ASE default), shape: (n_data,3,3)
            mic (bool): Minimal image convention for periodic boundary conditions

        Returns: Tuple of centering and neighboring indices, shape: Tuple[(n_pairs_max), (n_pairs_max)]

        """
    if mic is True:
        raise DeprecationWarning('`get_indices()` with `mic=True` is deprecated in favor of `get_pbc_neighbors()`.')

    n = R.shape[-2]
    n_data = R.shape[0]
    idx = np.indices((n, n))

    if mic:
        cell_lengths = np.linalg.norm(cell, axis=-1).reshape(-1)  # shape: (n_data*3)
        if r_cut < 0.5 * min(cell_lengths):
            distance_fn = lambda r, c: coordinates_to_distance_matrix_mic(r, c)
        else:
            raise NotImplementedError(f'Minimal image convention currently only implemented for '
                                      f'r_cut < 0.5*min(cell_lengths), but r_cut={r_cut} and 0.5*min(cell_lengths) = '
                                      f'{0.5 * min(cell_lengths)}. Consider using `get_pbc_indices` which uses ASE under '
                                      f'the hood. However, the latter takes ~15 times longer so maybe reduce r_cut.')
    else:
        distance_fn = lambda r, _: coordinates_to_distance_matrix(r)
        cell = np.zeros(len(R))

    @jax.jit
    def neigh_filter(_z, _R, _c):
        Dij = distance_fn(_R, _c).squeeze(axis=-1)  # shape: (n,n)
        msk_ij = (jnp.einsum('i, j -> ij', _z, _z) != 0).astype(np.int16)  # shape: (n,n)
        Dij_x_msk_ij = Dij * msk_ij  # shape: (n,n)
        return jnp.where((Dij_x_msk_ij <= r_cut) & (Dij_x_msk_ij > 0), True, False)

    def get_idx(i):
        idx_ = idx[:, neigh_filter(z[i], R[i], cell[i])]  # shape: (2,n_pairs)
        # pad_idx = np.pad(idx_, ((0, 0), (0, int(pad_length[i]))), mode='constant', constant_values=((0, 0), (0, -1)))
        # shape: (2,n_pair+pad_length)
        return idx_

    logging.info('Generate neighborhood lists for {} geometries: '.format(n_data))
    idxs = list(map(get_idx, tqdm(range(n_data))))  # [(2,*), ..., (2,*)]  * is number of pairs per point

    n_pairs = [u.shape[-1] for u in idxs]
    n_pairs_max = max(n_pairs)
    pad_length = [n_pairs_max - u for u in n_pairs]

    def pad_idxs(t):
        u, l = t
        return np.pad(u, ((0, 0), (0, int(l))), mode='constant', constant_values=((0, 0), (0, -1)))

    pad_idx_i, pad_idx_j = map(np.squeeze,
                               np.split(np.array(list(map(pad_idxs, zip(idxs, pad_length)))),
                                        indices_or_sections=2,
                                        axis=-2))

    return {'idx_i': pad_idx_i, 'idx_j': pad_idx_j}

def pad_index_list(idx, n_pair_max, pad_value=-1):
    n_pair = idx.shape[-1]

    pad_length = n_pair_max - n_pair
    assert pad_length >= 0

    pad = partial(np.pad, pad_width=(0, pad_length), mode='constant',
                  constant_values=(0, pad_value))
    pad_idx = pad(idx)
    return pad_idx

def pad_shift(_shift, n_pair_max, pad_value=0):
    n_pair = _shift.shape[0]

    pad_length = n_pair_max - n_pair
    assert pad_length >= 0

    pad = partial(np.pad, pad_width=((0, pad_length), (0, 0)), mode='constant',
                  constant_values=((0, pad_value), (0, 0)))
    pad_s = pad(_shift)
    return pad_s

def get_pbc_neighbors(pos, node_mask, pbc, cell, cutoff) -> Dict:
    """
    Calculate the neighbors in the graph if periodic boundary conditions (PBC) are present. Under the hood, this
    function uses the ase.primitive_neighbors function from ASE to calculate the central index `idx_i`, the neighboring
    indices `idx_j` as well as shifts vectors `shifts`.

    Args:
        pos (Array): Positions that, if structures of different sizes are present, are already padded to the same
            length n, shape: (B,n,3)
        node_mask (Array): Mask that indicates which nodes are true nodes and which are only present due to padding,
            shape: (B,n)
        cell (Array): Cells are assumed to be row-wise (ASE default), shape: (B,3,3)
        pbc (Array): Directions of periodic boundary conditions, shape: (B,3)
        cutoff (float): Cutoff used to calculate the neighbors

    Returns: Dictionary, with central, neighboring indices as well as the shift vectors.

    """

    # Cells must be transposed in the following, since mlff neighbor list assume cells to be column wise to make it
    # consistent with GLP and jax_md neighbor list

    # TODO: check for 2*cutoff < min(||lattice_vector||_2)

    def estimate_capacity_multiplier():
        return 1.1 * node_mask.sum(-1).max() / node_mask.sum(-1).min()

    allocate_fn, update_fn = pbc_neighbor_list(cell=cell[0].T,
                                               cutoff=cutoff,
                                               capacity_multiplier=1.)

    neighbors = allocate_fn(pos=pos[0], node_mask=node_mask[0], pbc=pbc[0], new_cell=cell[0].T)

    tmp_idx_i_list = []
    tmp_idx_j_list = []
    tmp_shifts_list = []

    idx_i_list = []
    idx_j_list = []
    shifts_list = []

    print('Construct neighbors using minimal image convention ...')
    for (R, msk, pb, ce) in tqdm(zip(pos, node_mask, pbc, cell)):
        neighbors = update_fn(pos=R, node_mask=msk, pbc=pb, primitive_neighbors=neighbors, new_cell=ce.T)

        # check for overflow in the neighbor list
        if neighbors.overflow:
            # print('Overflow detected. Re-allocating neighbor list!')
            # re-allocate the neighborhood list
            # allocate_fn, update_fn = pbc_neighbor_list(cell=ce.T, cutoff=cutoff, capacity_multiplier=1.)
            neighbors = allocate_fn(pos=R, node_mask=msk, pbc=pb, new_cell=ce.T)

            # save the primitive neighbors calculated so far
            idx_i_list += [np.stack(tmp_idx_i_list, axis=0)]
            idx_j_list += [np.stack(tmp_idx_j_list, axis=0)]
            shifts_list += [np.stack(tmp_shifts_list, axis=0)]

            # clear the temporary lists
            tmp_idx_i_list = []
            tmp_idx_j_list = []
            tmp_shifts_list = []

        # append neighbors and shifts
        tmp_idx_i_list += [neighbors.idx_i]
        tmp_idx_j_list += [neighbors.idx_j]
        tmp_shifts_list += [neighbors.shifts]

    # append the remaining temporary lists
    idx_i_list += [np.stack(tmp_idx_i_list, axis=0)]
    idx_j_list += [np.stack(tmp_idx_j_list, axis=0)]
    shifts_list += [np.stack(tmp_shifts_list, axis=0)]

    n_edges_max = max([x.shape[-1] for x in idx_j_list])

    idx_i_list = [np.array(list(map(partial(pad_index_list, n_pair_max=n_edges_max), x))) for x in idx_i_list]
    idx_j_list = [np.array(list(map(partial(pad_index_list, n_pair_max=n_edges_max), x))) for x in idx_j_list]
    shifts_list = [np.array(list(map(partial(pad_shift, n_pair_max=n_edges_max), x))) for x in shifts_list]
    print('... done!')
    return {'idx_i': np.concatenate(idx_i_list, axis=0),
            'idx_j': np.concatenate(idx_j_list, axis=0),
            'shifts': np.concatenate(shifts_list, axis=0)}

def pbc_neighbor_list(cell, cutoff, skin: float = None, capacity_multiplier: float = 1.25):
    """

    Args:
        cell (Array): Lattice vectors, are assumed to be column-wise!! Differs from ASE default
        cutoff ():
        skin ():
        capacity_multiplier ():

    Returns:

    """

    def allocate_fn(pos, pbc, new_cell=None, node_mask=None):
        if new_cell is None:
            new_cell = cell

        if node_mask is None:
            node_mask = np.ones(pos.shape[0], dtype=pos.dtype).astype(bool)

        ce_ase = new_cell.T  # switch to ASE default, lattice vectors are now row-wise

        idx_i, idx_j, shifts = primitive_neighbor_list(quantities='ijS',
                                                       pbc=pbc,
                                                       cell=ce_ase,
                                                       positions=pos[node_mask],
                                                       cutoff=cutoff,
                                                       numbers=None,
                                                       self_interaction=False,
                                                       use_scaled_positions=False,
                                                       max_nbins=1000000.0)

        n_edges_max = int(np.ceil(capacity_multiplier * len(idx_i)))
        return PrimitiveNeighbors(idx_i=pad_index_list(idx_i, n_pair_max=n_edges_max),
                                  idx_j=pad_index_list(idx_j, n_pair_max=n_edges_max),
                                  shifts=pad_shift(shifts, n_pair_max=n_edges_max),
                                  overflow=False
                                  )

    def update_fn(pos, pbc, primitive_neighbors: PrimitiveNeighbors, new_cell, node_mask=None):
        """

        Args:
            pos (Array): Atomic positions, shape: (n,3)
            pbc (Array):
            primitive_neighbors (PrimitiveNeighbors)
            new_cell (Array): Unit cell, lattice vectors are assumed to be column-wise (transpose of ASE default),
                shape: (3,3)
            node_mask (Array):

        Returns:

        """
        if node_mask is None:
            node_mask = np.ones(pos.shape[0], dtype=pos.dtype).astype(bool)

        n_edges_max = len(primitive_neighbors.idx_i)

        ce_ase = new_cell.T  # switch to ASE default, lattice vectors are now row-wise

        idx_i, idx_j, shifts = primitive_neighbor_list(quantities='ijS',
                                                       pbc=pbc,
                                                       cell=ce_ase,
                                                       positions=pos[node_mask],
                                                       cutoff=cutoff,
                                                       numbers=None,
                                                       self_interaction=False,
                                                       use_scaled_positions=False,
                                                       max_nbins=1000000.0)

        if len(idx_i) > n_edges_max:
            overflow = True
        else:
            overflow = False
            idx_i = pad_index_list(idx_i, n_pair_max=n_edges_max)
            idx_j = pad_index_list(idx_j, n_pair_max=n_edges_max)
            shifts = pad_shift(shifts, n_pair_max=n_edges_max)

        return PrimitiveNeighbors(idx_i=idx_i, idx_j=idx_j, shifts=shifts, overflow=overflow)

    # note that the update function still returns correct neighbors and shifts, even when overflow is present.
    return allocate_fn, update_fn


import boto3
from boto3.dynamodb.conditions import Key, Attr

def lambda_handler(event, context):

    menu_id = event['menu_id']

    dynamodb = boto3.resource('dynamodb')

    table = dynamodb.Table('Menus')

    table.delete_item(
        Key={
            'menuId': menu_id
        }
    )


import serial
import time
import constants
import random
import chess
import chess.pgn
import chess.svg
import pyttsx3
# import opencv
import threading
# import voice
from command import Command
from collections import deque
from stockfish import Stockfish
from game import Game
from graph import Graph, Node
from enum import Enum
from connection import SerialConnection

class GameMode(Enum):
    VS_AI = 1
    DATABASE = 2
    SANDBOX = 3

gameMode = GameMode.DATABASE
NARRATION = False
move_str = ""
def main():
    players = dict()

    if gameMode == GameMode.VS_AI:
        game = Game()
        stockfish = Stockfish('./stockfish_15_linux_x64_ssse')
        userMove = ''
        userName = ''
            # ------------------------------------------------
        # move_str = input("Black(0) or White(1)?: ")
        move_str = "1"
        # if userName == '':
        #     userName = input("Your Name: ")
        userName = "Dhwani"
        move = None
        # sayStr = userName + ", You've choosen to play white. You are ready to make your move once you hear the beep sound."

        if move_str == '1':

            players['White'] = userName
            players['Black'] = 'Stockfish'
            while True:
                cell1, cell2 = opencv.getUserMoveFromCamera(game.board)
                # print(cell1,cell2)
                move1 = chess.Move.from_uci(cell1+cell2)
                move2 = chess.Move.from_uci(cell2+cell1)
                if move1 in game.board.legal_moves:
                    move = move1
                elif move2 in game.board.legal_moves:
                    move = move2
                else:
                    pass
                    # print("Illegal move", cell1,cell2)
                    # voice.say("Its an illegal move. Try again.")
                if move in game.board.legal_moves:
                    fromPosition = game.graph.chessPositions[chess.SQUARE_NAMES[move.from_square]]
                    toPosition = game.graph.chessPositions[chess.SQUARE_NAMES[move.to_square]]
                    game.updatePiecePosition(fromPosition,toPosition)
                    game.makeMove(move)
                    # movePiecesInBoard(game,move)
                    break
                else:
                    # voice.say("error")
                    pass
        while not game.board.is_game_over():
            stockfish.set_fen_position(game.board.fen())
            move = chess.Move.from_uci(stockfish.get_best_move())
            is_capture = game.board.is_capture(move)

            # print(game.board)
            # if is_capture:
            #     voice.say("This is a capture move")

            movePiecesInBoard(game,move,players)

           # game.makeMove(move)
            is_check = game.board.is_check()
            is_checkmate = game.board.is_checkmate()
            # print(is_check, is_checkmate)
            # if is_checkmate:
            #     voice.say("Checkmate!!")
            # elif is_check:
            #     voice.say("Check!")

            fromSquare = chess.SQUARE_NAMES[move.from_square]
            toSquare = chess.SQUARE_NAMES[move.to_square]

            while not game.board.is_game_over():
                cell1, cell2 = opencv.getUserMoveFromCamera(game.board)
                move1 = chess.Move.from_uci(cell1+cell2)
                move2 = chess.Move.from_uci(cell2+cell1)
                if move1 in game.board.legal_moves:
                    move = move1
                elif move2 in game.board.legal_moves:
                    move = move2
                else:
                    pass
                is_captureMove = game.board.is_capture(move)

                fs = game.graph.chessPositions[chess.SQUARE_NAMES[move.from_square]]
                ts = game.graph.chessPositions[chess.SQUARE_NAMES[move.to_square]]
                if move in game.board.legal_moves:
                    clr = game.board.turn
                    is_qsCastling = game.board.is_queenside_castling(move)
                    is_ksCastling = game.board.is_kingside_castling(move)
                    game.makeMove(move)

                    if is_captureMove:
                        game.updateCaptureMove(fs,ts)
                    elif is_qsCastling:
                        game.updatePiecePosition(fs,ts)
                        game.updateCastling("qs",clr)
                    elif is_ksCastling:
                        game.updatePiecePosition(fs,ts)
                        game.updateCastling("ks",clr)
                    else:
                        game.updatePiecePosition(fs,ts)

                    break
        voice.say("Thanks for playing. Resetting board.")
        game.resetBoard()

    elif gameMode == GameMode.DATABASE:
        pgn = open('Carlsen.pgn')
        while pgn:
            gamePGN = chess.pgn.read_game(pgn)
            game = Game()
            blackPlayer = gamePGN.headers['Black'].split(',')[0]
            whitePlayer = gamePGN.headers['White'].split(',')[0]
            if NARRATION:
                voice.say("Lets look at a game between " + \
                    gamePGN.headers['White'] + " playing white and " + \
                    gamePGN.headers['Black'] + " playing black in " + \
                    gamePGN.headers['Date'] + " at " + gamePGN.headers['Site'])
            moveCount = 0

            player = whitePlayer
            for move in gamePGN.mainline_moves():
                movePiecesInBoard(game,move, gamePGN.headers)
                moveCount += 1
                game.graph.updateGraph(game.board)
            if NARRATION:
                voice.say("That was the final move in the game.")
            game.resetBoard()
            game.probeBoard()
    elif gameMode == GameMode.SANDBOX:
        game = Game()

        while True:
            txt = input()
            print(txt)
            movePiecesInBoard(game,chess.Move.from_uci(txt),None)
            if txt == '1':
                break;

def movePiecesInBoard(game, move, headers=None):
        if NARRATION:
            piecesName = dict()
            piecesName['P'] = 'Pawn'
            piecesName['N'] = 'Knight'
            piecesName['B'] = 'Bishop'
            piecesName['R'] = 'Rook'
            piecesName['Q'] = 'Queen'
            piecesName['K'] = 'King'

        is_capture = game.board.is_capture(move)
        is_kingside_castling = game.board.is_kingside_castling(move)
        is_queenside_castling = game.board.is_queenside_castling(move)
        thisTurn = game.board.turn
        player = ''
        movingCellName = chess.SQUARE_NAMES[move.from_square]
        movingToCellName = chess.SQUARE_NAMES[move.to_square]
        if headers:
            if thisTurn == chess.BLACK:
                player = headers['Black'].split(',')[0]
            else:
                player = headers['White'].split(',')[0]
        else:
            player = move_str
        if NARRATION:
            thingToSay = ''
            if is_capture:
                movingPieceName = piecesName[str.upper((game.board.piece_at(move.from_square)).symbol())]
                movingToPieceName = piecesName[str.upper((game.board.piece_at(move.to_square)).symbol())]
                thingToSay = player + "'s" + movingPieceName + " at " + movingCellName + " captures " + movingToPieceName + " at " + movingToCellName
            elif is_kingside_castling:
                thingToSay = player + " castles kingside"
            elif is_queenside_castling:
                thingToSay = player + " castles queenside"
            else:
                movingPieceName = piecesName[str.upper((game.board.piece_at(move.from_square)).symbol())]
                if movingPieceName == "Pawn":
                    thingToSay = player + " advances " + movingCellName +" pawn to " + movingToCellName
                else:
                    thingToSay = player + " moves " + movingPieceName + " to " + movingToCellName
            threading.Thread(target=voice.say, args=(thingToSay,)).start()

        fromSquare = chess.SQUARE_NAMES[move.from_square]
        toSquare = chess.SQUARE_NAMES[move.to_square]
        game.makeMove(move)

        if is_capture:
            game.captureMove(game.graph.chessPositions[fromSquare], game.graph.chessPositions[toSquare])
        else:
            game.movePieceFromTo(game.graph.chessPositions[fromSquare],game.graph.chessPositions[toSquare])
        if is_kingside_castling:
            if thisTurn == chess.BLACK:
                game.movePieceFromTo(game.graph.chessPositions['h8'], game.graph.chessPositions['f8'])
            elif thisTurn == chess.WHITE:
                game.movePieceFromTo(game.graph.chessPositions['h1'], game.graph.chessPositions['f1'])
        if is_queenside_castling:
            if thisTurn == chess.BLACK:
                game.movePieceFromTo(game.graph.chessPositions['a8'], game.graph.chessPositions['d8'])
            elif thisTurn == chess.WHITE:
                game.movePieceFromTo(game.graph.chessPositions['a1'], game.graph.chessPositions['d1'])

if __name__ == "__main__":
    main()