from __future__ import annotations

import json
import time
import uuid
import email
import inspect
import logging
import platform
import warnings
import email.utils
from types import TracebackType
from random import random
from typing import (
    TYPE_CHECKING,
    Any,
    Dict,
    Type,
    Union,
    Generic,
    Mapping,
    TypeVar,
    Iterable,
    Iterator,
    Optional,
    Generator,
    AsyncIterator,
    cast,
    overload,
)
from functools import lru_cache
from typing_extensions import Literal, get_args, get_origin

import anyio
import httpx
import distro
import pydantic
from httpx import URL, Limits
from pydantic import PrivateAttr

from . import _exceptions
from ._qs import Querystring
from ._types import (
    NOT_GIVEN,
    Body,
    Omit,
    Query,
    ModelT,
    Headers,
    Timeout,
    NoneType,
    NotGiven,
    ResponseT,
    Transport,
    AnyMapping,
    ProxiesTypes,
    RequestFiles,
    AsyncTransport,
    RequestOptions,
    UnknownResponse,
    ModelBuilderProtocol,
)
from ._utils import is_dict, is_given, is_mapping
from ._compat import model_copy, model_dump
from ._models import (
    BaseModel,
    GenericModel,
    FinalRequestOptions,
    validate_type,
    construct_type,
)
from ._streaming import Stream, AsyncStream
from ._exceptions import (
    APIStatusError,
    APITimeoutError,
    APIConnectionError,
    APIResponseValidationError,
)

log: logging.Logger = logging.getLogger(__name__)

# TODO: make base page type vars covariant
SyncPageT = TypeVar("SyncPageT", bound="BaseSyncPage[Any]")
AsyncPageT = TypeVar("AsyncPageT", bound="BaseAsyncPage[Any]")

_T = TypeVar("_T")
_T_co = TypeVar("_T_co", covariant=True)

_StreamT = TypeVar("_StreamT", bound=Stream[Any])
_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any])

if TYPE_CHECKING:
    from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
else:
    try:
        from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
    except ImportError:
        # taken from https://github.com/encode/httpx/blob/3ba5fe0d7ac70222590e759c31442b1cab263791/httpx/_config.py#L366
        HTTPX_DEFAULT_TIMEOUT = Timeout(5.0)

# default timeout is 10 minutes
DEFAULT_TIMEOUT = Timeout(timeout=600.0, connect=5.0)
DEFAULT_MAX_RETRIES = 2
DEFAULT_LIMITS = Limits(max_connections=100, max_keepalive_connections=20)

class MissingStreamClassError(TypeError):
    def __init__(self) -> None:
        super().__init__(
            "The `stream` argument was set to `True` but the `stream_cls` argument was not given. See `anthropic._streaming` for reference",
        )

class PageInfo:
    """Stores the necesary information to build the request to retrieve the next page.

    Either `url` or `params` must be set.
    """

    url: URL | NotGiven
    params: Query | NotGiven

    @overload
    def __init__(
        self,
        *,
        url: URL,
    ) -> None:
        ...

    @overload
    def __init__(
        self,
        *,
        params: Query,
    ) -> None:
        ...

    def __init__(
        self,
        *,
        url: URL | NotGiven = NOT_GIVEN,
        params: Query | NotGiven = NOT_GIVEN,
    ) -> None:
        self.url = url
        self.params = params

class BasePage(GenericModel, Generic[ModelT]):
    """
    Defines the core interface for pagination.

    Type Args:
        ModelT: The pydantic model that represents an item in the response.

    Methods:
        has_next_page(): Check if there is another page available
        next_page_info(): Get the necesary information to make a request for the next page
    """

    _options: FinalRequestOptions = PrivateAttr()
    _model: Type[ModelT] = PrivateAttr()

    def has_next_page(self) -> bool:
        items = self._get_page_items()
        if not items:
            return False
        return self.next_page_info() is not None

    def next_page_info(self) -> Optional[PageInfo]:
        ...

    def _get_page_items(self) -> Iterable[ModelT]:  # type: ignore[empty-body]
        ...

    def _params_from_url(self, url: URL) -> httpx.QueryParams:
        # TODO: do we have to preprocess params here?
        return httpx.QueryParams(cast(Any, self._options.params)).merge(url.params)

    def _info_to_options(self, info: PageInfo) -> FinalRequestOptions:
        options = model_copy(self._options)

        if not isinstance(info.params, NotGiven):
            options.params = {**options.params, **info.params}
            return options

        if not isinstance(info.url, NotGiven):
            params = self._params_from_url(info.url)
            url = info.url.copy_with(params=params)
            options.params = dict(url.params)
            options.url = str(url)
            return options

        raise ValueError("Unexpected PageInfo state")

class BaseSyncPage(BasePage[ModelT], Generic[ModelT]):
    _client: SyncAPIClient = pydantic.PrivateAttr()

    def _set_private_attributes(
        self,
        client: SyncAPIClient,
        model: Type[ModelT],
        options: FinalRequestOptions,
    ) -> None:
        self._model = model
        self._client = client
        self._options = options

    # Pydantic uses a custom `__iter__` method to support casting BaseModels
    # to dictionaries. e.g. dict(model).
    # As we want to support `for item in page`, this is inherently incompatible
    # with the default pydantic behaviour. It is not possible to support both
    # use cases at once. Fortunately, this is not a big deal as all other pydantic
    # methods should continue to work as expected as there is an alternative method
    # to cast a model to a dictionary, model.dict(), which is used internally
    # by pydantic.
    def __iter__(self) -> Iterator[ModelT]:  # type: ignore
        for page in self.iter_pages():
            for item in page._get_page_items():
                yield item

    def iter_pages(self: SyncPageT) -> Iterator[SyncPageT]:
        page = self
        while True:
            yield page
            if page.has_next_page():
                page = page.get_next_page()
            else:
                return

    def get_next_page(self: SyncPageT) -> SyncPageT:
        info = self.next_page_info()
        if not info:
            raise RuntimeError(
                "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`."
            )

        options = self._info_to_options(info)
        return self._client._request_api_list(self._model, page=self.__class__, options=options)

class AsyncPaginator(Generic[ModelT, AsyncPageT]):
    def __init__(
        self,
        client: AsyncAPIClient,
        options: FinalRequestOptions,
        page_cls: Type[AsyncPageT],
        model: Type[ModelT],
    ) -> None:
        self._model = model
        self._client = client
        self._options = options
        self._page_cls = page_cls

    def __await__(self) -> Generator[Any, None, AsyncPageT]:
        return self._get_page().__await__()

    async def _get_page(self) -> AsyncPageT:
        page = await self._client.request(self._page_cls, self._options)
        page._set_private_attributes(  # pyright: ignore[reportPrivateUsage]
            model=self._model,
            options=self._options,
            client=self._client,
        )
        return page

    async def __aiter__(self) -> AsyncIterator[ModelT]:
        # https://github.com/microsoft/pyright/issues/3464
        page = cast(
            AsyncPageT,
            await self,  # type: ignore
        )
        async for item in page:
            yield item

class BaseAsyncPage(BasePage[ModelT], Generic[ModelT]):
    _client: AsyncAPIClient = pydantic.PrivateAttr()

    def _set_private_attributes(
        self,
        model: Type[ModelT],
        client: AsyncAPIClient,
        options: FinalRequestOptions,
    ) -> None:
        self._model = model
        self._client = client
        self._options = options

    async def __aiter__(self) -> AsyncIterator[ModelT]:
        async for page in self.iter_pages():
            for item in page._get_page_items():
                yield item

    async def iter_pages(self: AsyncPageT) -> AsyncIterator[AsyncPageT]:
        page = self
        while True:
            yield page
            if page.has_next_page():
                page = await page.get_next_page()
            else:
                return

    async def get_next_page(self: AsyncPageT) -> AsyncPageT:
        info = self.next_page_info()
        if not info:
            raise RuntimeError(
                "No next page expected; please check `.has_next_page()` before calling `.get_next_page()`."
            )

        options = self._info_to_options(info)
        return await self._client._request_api_list(self._model, page=self.__class__, options=options)

_HttpxClientT = TypeVar("_HttpxClientT", bound=Union[httpx.Client, httpx.AsyncClient])

class BaseClient(Generic[_HttpxClientT]):
    _client: _HttpxClientT
    _version: str
    _base_url: URL
    max_retries: int
    timeout: Union[float, Timeout, None]
    _limits: httpx.Limits
    _proxies: ProxiesTypes | None
    _transport: Transport | AsyncTransport | None
    _strict_response_validation: bool
    _idempotency_header: str | None

    def __init__(
        self,
        *,
        version: str,
        base_url: str | URL,
        _strict_response_validation: bool,
        max_retries: int = DEFAULT_MAX_RETRIES,
        timeout: float | Timeout | None = DEFAULT_TIMEOUT,
        limits: httpx.Limits,
        transport: Transport | AsyncTransport | None,
        proxies: ProxiesTypes | None,
        custom_headers: Mapping[str, str] | None = None,
        custom_query: Mapping[str, object] | None = None,
    ) -> None:
        self._version = version
        self._base_url = self._enforce_trailing_slash(URL(base_url))
        self.max_retries = max_retries
        self.timeout = timeout
        self._limits = limits
        self._proxies = proxies
        self._transport = transport
        self._custom_headers = custom_headers or {}
        self._custom_query = custom_query or {}
        self._strict_response_validation = _strict_response_validation
        self._idempotency_header = None

    def _enforce_trailing_slash(self, url: URL) -> URL:
        if url.raw_path.endswith(b"/"):
            return url
        return url.copy_with(raw_path=url.raw_path + b"/")

    def _make_status_error_from_response(
        self,
        response: httpx.Response,
    ) -> APIStatusError:
        err_text = response.text.strip()
        body = err_text

        try:
            body = json.loads(err_text)
            err_msg = f"Error code: {response.status_code} - {body}"
        except Exception:
            err_msg = err_text or f"Error code: {response.status_code}"

        return self._make_status_error(err_msg, body=body, response=response)

    def _make_status_error(
        self,
        err_msg: str,
        *,
        body: object,
        response: httpx.Response,
    ) -> _exceptions.APIStatusError:
        raise NotImplementedError()

    def _remaining_retries(
        self,
        remaining_retries: Optional[int],
        options: FinalRequestOptions,
    ) -> int:
        return remaining_retries if remaining_retries is not None else options.get_max_retries(self.max_retries)

    def _build_headers(self, options: FinalRequestOptions) -> httpx.Headers:
        custom_headers = options.headers or {}
        headers_dict = _merge_mappings(self.default_headers, custom_headers)
        self._validate_headers(headers_dict, custom_headers)

        headers = httpx.Headers(headers_dict)

        idempotency_header = self._idempotency_header
        if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
            if not options.idempotency_key:
                options.idempotency_key = self._idempotency_key()

            headers[idempotency_header] = options.idempotency_key

        return headers

    def _prepare_request(
        self,
        request: httpx.Request,  # noqa: ARG002
    ) -> None:
        """This method is used as a callback for mutating the `Request` object
        after it has been constructed.

        This is useful for cases where you want to add certain headers based off of
        the request properties, e.g. `url`, `method` etc.
        """
        return None

    def _prepare_url(self, url: str) -> URL:
        """
        Merge a URL argument together with any 'base_url' on the client,
        to create the URL used for the outgoing request.
        """
        # Copied from httpx's `_merge_url` method.
        merge_url = URL(url)
        if merge_url.is_relative_url:
            merge_raw_path = self.base_url.raw_path + merge_url.raw_path.lstrip(b"/")
            return self.base_url.copy_with(raw_path=merge_raw_path)

        return merge_url

    def _build_request(
        self,
        options: FinalRequestOptions,
    ) -> httpx.Request:
        if log.isEnabledFor(logging.DEBUG):
            log.debug("Request options: %s", model_dump(options, exclude_unset=True))

        kwargs: dict[str, Any] = {}

        json_data = options.json_data
        if options.extra_json is not None:
            if json_data is None:
                json_data = cast(Body, options.extra_json)
            elif is_mapping(json_data):
                json_data = _merge_mappings(json_data, options.extra_json)
            else:
                raise RuntimeError(f"Unexpected JSON data type, {type(json_data)}, cannot merge with `extra_body`")

        headers = self._build_headers(options)
        params = _merge_mappings(self._custom_query, options.params)

        # If the given Content-Type header is multipart/form-data then it
        # has to be removed so that httpx can generate the header with
        # additional information for us as it has to be in this form
        # for the server to be able to correctly parse the request:
        # multipart/form-data; boundary=---abc--
        if headers.get("Content-Type") == "multipart/form-data":
            headers.pop("Content-Type")

            # As we are now sending multipart/form-data instead of application/json
            # we need to tell httpx to use it, https://www.python-httpx.org/advanced/#multipart-file-encoding
            if json_data:
                if not is_dict(json_data):
                    raise TypeError(
                        f"Expected query input to be a dictionary for multipart requests but got {type(json_data)} instead."
                    )
                kwargs["data"] = self._serialize_multipartform(json_data)

        # TODO: report this error to httpx
        request = self._client.build_request(  # pyright: ignore[reportUnknownMemberType]
            headers=headers,
            timeout=self.timeout if isinstance(options.timeout, NotGiven) else options.timeout,
            method=options.method,
            url=self._prepare_url(options.url),
            # the `Query` type that we use is incompatible with qs'
            # `Params` type as it needs to be typed as `Mapping[str, object]`
            # so that passing a `TypedDict` doesn't cause an error.
            # https://github.com/microsoft/pyright/issues/3526#event-6715453066
            params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
            json=json_data,
            files=options.files,
            **kwargs,
        )
        self._prepare_request(request)
        return request

    def _serialize_multipartform(self, data: Mapping[object, object]) -> dict[str, object]:
        items = self.qs.stringify_items(
            # TODO: type ignore is required as stringify_items is well typed but we can't be
            # well typed without heavy validation.
            data,  # type: ignore
            array_format="brackets",
        )
        serialized: dict[str, object] = {}
        for key, value in items:
            if key in serialized:
                raise ValueError(f"Duplicate key encountered: {key}; This behaviour is not supported")
            serialized[key] = value
        return serialized

    def _extract_stream_chunk_type(self, stream_cls: type) -> type:
        args = get_args(stream_cls)
        if not args:
            raise TypeError(
                f"Expected stream_cls to have been given a generic type argument, e.g. Stream[Foo] but received {stream_cls}",
            )
        return cast(type, args[0])

    def _process_response(
        self,
        *,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,  # noqa: ARG002
        response: httpx.Response,
    ) -> ResponseT:
        if cast_to is NoneType:
            return cast(ResponseT, None)

        if cast_to == str:
            return cast(ResponseT, response.text)

        origin = get_origin(cast_to) or cast_to

        if inspect.isclass(origin) and issubclass(origin, httpx.Response):
            # Because of the invariance of our ResponseT TypeVar, users can subclass httpx.Response
            # and pass that class to our request functions. We cannot change the variance to be either
            # covariant or contravariant as that makes our usage of ResponseT illegal. We could construct
            # the response class ourselves but that is something that should be supported directly in httpx
            # as it would be easy to incorrectly construct the Response object due to the multitude of arguments.
            if cast_to != httpx.Response:
                raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`")
            return cast(ResponseT, response)

        # The check here is necessary as we are subverting the the type system
        # with casts as the relationship between TypeVars and Types are very strict
        # which means we must return *exactly* what was input or transform it in a
        # way that retains the TypeVar state. As we cannot do that in this function
        # then we have to resort to using `cast`. At the time of writing, we know this
        # to be safe as we have handled all the types that could be bound to the
        # `ResponseT` TypeVar, however if that TypeVar is ever updated in the future, then
        # this function would become unsafe but a type checker would not report an error.
        if (
            cast_to is not UnknownResponse
            and not origin is list
            and not origin is dict
            and not origin is Union
            and not issubclass(origin, BaseModel)
        ):
            raise RuntimeError(
                f"Invalid state, expected {cast_to} to be a subclass type of {BaseModel}, {dict}, {list} or {Union}."
            )

        # split is required to handle cases where additional information is included
        # in the response, e.g. application/json; charset=utf-8
        content_type, *_ = response.headers.get("content-type").split(";")
        if content_type != "application/json":
            if self._strict_response_validation:
                raise APIResponseValidationError(
                    response=response,
                    message=f"Expected Content-Type response header to be `application/json` but received `{content_type}` instead.",
                    body=response.text,
                )

            # If the API responds with content that isn't JSON then we just return
            # the (decoded) text without performing any parsing so that you can still
            # handle the response however you need to.
            return response.text  # type: ignore

        data = response.json()

        try:
            return self._process_response_data(data=data, cast_to=cast_to, response=response)
        except pydantic.ValidationError as err:
            raise APIResponseValidationError(response=response, body=data) from err

    def _process_response_data(
        self,
        *,
        data: object,
        cast_to: type[ResponseT],
        response: httpx.Response,
    ) -> ResponseT:
        if data is None:
            return cast(ResponseT, None)

        if cast_to is UnknownResponse:
            return cast(ResponseT, data)

        if inspect.isclass(cast_to) and issubclass(cast_to, ModelBuilderProtocol):
            return cast(ResponseT, cast_to.build(response=response, data=data))

        if self._strict_response_validation:
            return cast(ResponseT, validate_type(type_=cast_to, value=data))

        return cast(ResponseT, construct_type(type_=cast_to, value=data))

    @property
    def qs(self) -> Querystring:
        return Querystring()

    @property
    def custom_auth(self) -> httpx.Auth | None:
        return None

    @property
    def auth_headers(self) -> dict[str, str]:
        return {}

    @property
    def default_headers(self) -> dict[str, str | Omit]:
        return {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "User-Agent": self.user_agent,
            **self.platform_headers(),
            **self.auth_headers,
            **self._custom_headers,
        }

    def _validate_headers(
        self,
        headers: Headers,  # noqa: ARG002
        custom_headers: Headers,  # noqa: ARG002
    ) -> None:
        """Validate the given default headers and custom headers.

        Does nothing by default.
        """
        return

    @property
    def user_agent(self) -> str:
        return f"{self.__class__.__name__}/Python {self._version}"

    @property
    def base_url(self) -> URL:
        return self._base_url

    @base_url.setter
    def base_url(self, url: URL | str) -> None:
        self._client.base_url = url if isinstance(url, URL) else URL(url)

    @lru_cache(maxsize=None)
    def platform_headers(self) -> Dict[str, str]:
        return {
            "X-Stainless-Lang": "python",
            "X-Stainless-Package-Version": self._version,
            "X-Stainless-OS": str(get_platform()),
            "X-Stainless-Arch": str(get_architecture()),
            "X-Stainless-Runtime": platform.python_implementation(),
            "X-Stainless-Runtime-Version": platform.python_version(),
        }

    def _calculate_retry_timeout(
        self,
        remaining_retries: int,
        options: FinalRequestOptions,
        response_headers: Optional[httpx.Headers] = None,
    ) -> float:
        max_retries = options.get_max_retries(self.max_retries)
        try:
            # About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
            #
            # <http-date>". See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After#syntax for
            # details.
            if response_headers is not None:
                retry_header = response_headers.get("retry-after")
                try:
                    retry_after = int(retry_header)
                except Exception:
                    retry_date_tuple = email.utils.parsedate_tz(retry_header)
                    if retry_date_tuple is None:
                        retry_after = -1
                    else:
                        retry_date = email.utils.mktime_tz(retry_date_tuple)
                        retry_after = int(retry_date - time.time())
            else:
                retry_after = -1

        except Exception:
            retry_after = -1

        # If the API asks us to wait a certain amount of time (and it's a reasonable amount), just do what it says.
        if 0 < retry_after <= 60:
            return retry_after

        initial_retry_delay = 0.5
        max_retry_delay = 2.0
        nb_retries = max_retries - remaining_retries

        # Apply exponential backoff, but not more than the max.
        sleep_seconds = min(initial_retry_delay * pow(nb_retries - 1, 2), max_retry_delay)

        # Apply some jitter, plus-or-minus half a second.
        jitter = random() - 0.5
        timeout = sleep_seconds + jitter
        return timeout if timeout >= 0 else 0

    def _should_retry(self, response: httpx.Response) -> bool:
        # Note: this is not a standard header
        should_retry_header = response.headers.get("x-should-retry")

        # If the server explicitly says whether or not to retry, obey.
        if should_retry_header == "true":
            return True
        if should_retry_header == "false":
            return False

        # Retry on request timeouts.
        if response.status_code == 408:
            return True

        # Retry on lock timeouts.
        if response.status_code == 409:
            return True

        # Retry on rate limits.
        if response.status_code == 429:
            return True

        # Retry internal errors.
        if response.status_code >= 500:
            return True

        return False

    def _idempotency_key(self) -> str:
        return f"stainless-python-retry-{uuid.uuid4()}"

class SyncAPIClient(BaseClient[httpx.Client]):
    _client: httpx.Client
    _has_custom_http_client: bool
    _default_stream_cls: type[Stream[Any]] | None = None

    def __init__(
        self,
        *,
        version: str,
        base_url: str | URL,
        max_retries: int = DEFAULT_MAX_RETRIES,
        timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
        transport: Transport | None = None,
        proxies: ProxiesTypes | None = None,
        limits: Limits | None = None,
        http_client: httpx.Client | None = None,
        custom_headers: Mapping[str, str] | None = None,
        custom_query: Mapping[str, object] | None = None,
        _strict_response_validation: bool,
    ) -> None:
        if limits is not None:
            warnings.warn(
                "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`")
        else:
            limits = DEFAULT_LIMITS

        if transport is not None:
            warnings.warn(
                "The `transport` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `transport`")

        if proxies is not None:
            warnings.warn(
                "The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")

        if not is_given(timeout):
            # if the user passed in a custom http client with a non-default
            # timeout set then we use that timeout.
            #
            # note: there is an edge case here where the user passes in a client
            # where they've explicitly set the timeout to match the default timeout
            # as this check is structural, meaning that we'll think they didn't
            # pass in a timeout and will ignore it
            if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT:
                timeout = http_client.timeout
            else:
                timeout = DEFAULT_TIMEOUT

        super().__init__(
            version=version,
            limits=limits,
            # cast to a valid type because mypy doesn't understand our type narrowing
            timeout=cast(Timeout, timeout),
            proxies=proxies,
            base_url=base_url,
            transport=transport,
            max_retries=max_retries,
            custom_query=custom_query,
            custom_headers=custom_headers,
            _strict_response_validation=_strict_response_validation,
        )
        self._client = http_client or httpx.Client(
            base_url=base_url,
            # cast to a valid type because mypy doesn't understand our type narrowing
            timeout=cast(Timeout, timeout),
            proxies=proxies,
            transport=transport,
            limits=limits,
        )
        self._has_custom_http_client = bool(http_client)

    def is_closed(self) -> bool:
        return self._client.is_closed

    def close(self) -> None:
        """Close the underlying HTTPX client.

        The client will *not* be usable after this.
        """
        # If an error is thrown while constructing a client, self._client
        # may not be present
        if hasattr(self, "_client"):
            self._client.close()

    def __enter__(self: _T) -> _T:
        return self

    def __exit__(
        self,
        exc_type: type[BaseException] | None,
        exc: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        self.close()

    @overload
    def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        remaining_retries: Optional[int] = None,
        *,
        stream: Literal[True],
        stream_cls: Type[_StreamT],
    ) -> _StreamT:
        ...

    @overload
    def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        remaining_retries: Optional[int] = None,
        *,
        stream: Literal[False] = False,
    ) -> ResponseT:
        ...

    @overload
    def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        remaining_retries: Optional[int] = None,
        *,
        stream: bool = False,
        stream_cls: Type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        ...

    def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        remaining_retries: Optional[int] = None,
        *,
        stream: bool = False,
        stream_cls: type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        return self._request(
            cast_to=cast_to,
            options=options,
            stream=stream,
            stream_cls=stream_cls,
            remaining_retries=remaining_retries,
        )

    def _request(
        self,
        *,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        remaining_retries: int | None,
        stream: bool,
        stream_cls: type[_StreamT] | None,
    ) -> ResponseT | _StreamT:
        retries = self._remaining_retries(remaining_retries, options)
        request = self._build_request(options)

        try:
            response = self._client.send(request, auth=self.custom_auth, stream=stream)
            log.debug(
                'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
            )
            response.raise_for_status()
        except httpx.HTTPStatusError as err:  # thrown on 4xx and 5xx status code
            if retries > 0 and self._should_retry(err.response):
                return self._retry_request(
                    options,
                    cast_to,
                    retries,
                    err.response.headers,
                    stream=stream,
                    stream_cls=stream_cls,
                )

            # If the response is streamed then we need to explicitly read the response
            # to completion before attempting to access the response text.
            err.response.read()
            raise self._make_status_error_from_response(err.response) from None
        except httpx.TimeoutException as err:
            if retries > 0:
                return self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
            raise APITimeoutError(request=request) from err
        except Exception as err:
            if retries > 0:
                return self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
            raise APIConnectionError(request=request) from err

        if stream:
            if stream_cls:
                return stream_cls(cast_to=self._extract_stream_chunk_type(stream_cls), response=response, client=self)

            stream_cls = cast("type[_StreamT] | None", self._default_stream_cls)
            if stream_cls is None:
                raise MissingStreamClassError()
            return stream_cls(cast_to=cast_to, response=response, client=self)

        return self._process_response(cast_to=cast_to, options=options, response=response)

    def _retry_request(
        self,
        options: FinalRequestOptions,
        cast_to: Type[ResponseT],
        remaining_retries: int,
        response_headers: Optional[httpx.Headers] = None,
        *,
        stream: bool,
        stream_cls: type[_StreamT] | None,
    ) -> ResponseT | _StreamT:
        remaining = remaining_retries - 1
        timeout = self._calculate_retry_timeout(remaining, options, response_headers)
        log.info("Retrying request to %s in %f seconds", options.url, timeout)

        # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
        # different thread if necessary.
        time.sleep(timeout)

        return self._request(
            options=options,
            cast_to=cast_to,
            remaining_retries=remaining,
            stream=stream,
            stream_cls=stream_cls,
        )

    def _request_api_list(
        self,
        model: Type[ModelT],
        page: Type[SyncPageT],
        options: FinalRequestOptions,
    ) -> SyncPageT:
        resp = self.request(page, options, stream=False)
        resp._set_private_attributes(  # pyright: ignore[reportPrivateUsage]
            client=self,
            model=model,
            options=options,
        )
        return resp

    @overload
    def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: Literal[False] = False,
    ) -> ResponseT:
        ...

    @overload
    def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: Literal[True],
        stream_cls: type[_StreamT],
    ) -> _StreamT:
        ...

    @overload
    def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: bool,
        stream_cls: type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        ...

    def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: bool = False,
        stream_cls: type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        opts = FinalRequestOptions.construct(method="get", url=path, **options)
        # cast is required because mypy complains about returning Any even though
        # it understands the type variables
        return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

    @overload
    def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
        files: RequestFiles | None = None,
        stream: Literal[False] = False,
    ) -> ResponseT:
        ...

    @overload
    def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
        files: RequestFiles | None = None,
        stream: Literal[True],
        stream_cls: type[_StreamT],
    ) -> _StreamT:
        ...

    @overload
    def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
        files: RequestFiles | None = None,
        stream: bool,
        stream_cls: type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        ...

    def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
        files: RequestFiles | None = None,
        stream: bool = False,
        stream_cls: type[_StreamT] | None = None,
    ) -> ResponseT | _StreamT:
        opts = FinalRequestOptions.construct(method="post", url=path, json_data=body, files=files, **options)
        return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))

    def patch(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
        return self.request(cast_to, opts)

    def put(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="put", url=path, json_data=body, files=files, **options)
        return self.request(cast_to, opts)

    def delete(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options)
        return self.request(cast_to, opts)

    def get_api_list(
        self,
        path: str,
        *,
        model: Type[ModelT],
        page: Type[SyncPageT],
        body: Body | None = None,
        options: RequestOptions = {},
        method: str = "get",
    ) -> SyncPageT:
        opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options)
        return self._request_api_list(model, page, opts)

class AsyncAPIClient(BaseClient[httpx.AsyncClient]):
    _client: httpx.AsyncClient
    _has_custom_http_client: bool
    _default_stream_cls: type[AsyncStream[Any]] | None = None

    def __init__(
        self,
        *,
        version: str,
        base_url: str | URL,
        _strict_response_validation: bool,
        max_retries: int = DEFAULT_MAX_RETRIES,
        timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
        transport: AsyncTransport | None = None,
        proxies: ProxiesTypes | None = None,
        limits: Limits | None = None,
        http_client: httpx.AsyncClient | None = None,
        custom_headers: Mapping[str, str] | None = None,
        custom_query: Mapping[str, object] | None = None,
    ) -> None:
        if limits is not None:
            warnings.warn(
                "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`")
        else:
            limits = DEFAULT_LIMITS

        if transport is not None:
            warnings.warn(
                "The `transport` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `transport`")

        if proxies is not None:
            warnings.warn(
                "The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
                category=DeprecationWarning,
                stacklevel=3,
            )
            if http_client is not None:
                raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")

        if not is_given(timeout):
            # if the user passed in a custom http client with a non-default
            # timeout set then we use that timeout.
            #
            # note: there is an edge case here where the user passes in a client
            # where they've explicitly set the timeout to match the default timeout
            # as this check is structural, meaning that we'll think they didn't
            # pass in a timeout and will ignore it
            if http_client and http_client.timeout != HTTPX_DEFAULT_TIMEOUT:
                timeout = http_client.timeout
            else:
                timeout = DEFAULT_TIMEOUT

        super().__init__(
            version=version,
            base_url=base_url,
            limits=limits,
            # cast to a valid type because mypy doesn't understand our type narrowing
            timeout=cast(Timeout, timeout),
            proxies=proxies,
            transport=transport,
            max_retries=max_retries,
            custom_query=custom_query,
            custom_headers=custom_headers,
            _strict_response_validation=_strict_response_validation,
        )
        self._client = http_client or httpx.AsyncClient(
            base_url=base_url,
            # cast to a valid type because mypy doesn't understand our type narrowing
            timeout=cast(Timeout, timeout),
            proxies=proxies,
            transport=transport,
            limits=limits,
        )
        self._has_custom_http_client = bool(http_client)

    def is_closed(self) -> bool:
        return self._client.is_closed

    async def close(self) -> None:
        """Close the underlying HTTPX client.

        The client will *not* be usable after this.
        """
        await self._client.aclose()

    async def __aenter__(self: _T) -> _T:
        return self

    async def __aexit__(
        self,
        exc_type: type[BaseException] | None,
        exc: BaseException | None,
        exc_tb: TracebackType | None,
    ) -> None:
        await self.close()

    @overload
    async def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        *,
        stream: Literal[False] = False,
        remaining_retries: Optional[int] = None,
    ) -> ResponseT:
        ...

    @overload
    async def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        *,
        stream: Literal[True],
        stream_cls: type[_AsyncStreamT],
        remaining_retries: Optional[int] = None,
    ) -> _AsyncStreamT:
        ...

    @overload
    async def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        *,
        stream: bool,
        stream_cls: type[_AsyncStreamT] | None = None,
        remaining_retries: Optional[int] = None,
    ) -> ResponseT | _AsyncStreamT:
        ...

    async def request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        *,
        stream: bool = False,
        stream_cls: type[_AsyncStreamT] | None = None,
        remaining_retries: Optional[int] = None,
    ) -> ResponseT | _AsyncStreamT:
        return await self._request(
            cast_to=cast_to,
            options=options,
            stream=stream,
            stream_cls=stream_cls,
            remaining_retries=remaining_retries,
        )

    async def _request(
        self,
        cast_to: Type[ResponseT],
        options: FinalRequestOptions,
        *,
        stream: bool,
        stream_cls: type[_AsyncStreamT] | None,
        remaining_retries: int | None,
    ) -> ResponseT | _AsyncStreamT:
        retries = self._remaining_retries(remaining_retries, options)
        request = self._build_request(options)

        try:
            response = await self._client.send(request, auth=self.custom_auth, stream=stream)
            log.debug(
                'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
            )
            response.raise_for_status()
        except httpx.HTTPStatusError as err:  # thrown on 4xx and 5xx status code
            if retries > 0 and self._should_retry(err.response):
                return await self._retry_request(
                    options,
                    cast_to,
                    retries,
                    err.response.headers,
                    stream=stream,
                    stream_cls=stream_cls,
                )

            # If the response is streamed then we need to explicitly read the response
            # to completion before attempting to access the response text.
            await err.response.aread()
            raise self._make_status_error_from_response(err.response) from None
        except httpx.ConnectTimeout as err:
            if retries > 0:
                return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
            raise APITimeoutError(request=request) from err
        except httpx.ReadTimeout as err:
            # We explicitly do not retry on ReadTimeout errors as this means
            # that the server processing the request has taken 60 seconds
            # (our default timeout). This likely indicates that something
            # is not working as expected on the server side.
            raise
        except httpx.TimeoutException as err:
            if retries > 0:
                return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
            raise APITimeoutError(request=request) from err
        except Exception as err:
            if retries > 0:
                return await self._retry_request(options, cast_to, retries, stream=stream, stream_cls=stream_cls)
            raise APIConnectionError(request=request) from err

        if stream:
            if stream_cls:
                return stream_cls(cast_to=self._extract_stream_chunk_type(stream_cls), response=response, client=self)

            stream_cls = cast("type[_AsyncStreamT] | None", self._default_stream_cls)
            if stream_cls is None:
                raise MissingStreamClassError()
            return stream_cls(cast_to=cast_to, response=response, client=self)

        return self._process_response(cast_to=cast_to, options=options, response=response)

    async def _retry_request(
        self,
        options: FinalRequestOptions,
        cast_to: Type[ResponseT],
        remaining_retries: int,
        response_headers: Optional[httpx.Headers] = None,
        *,
        stream: bool,
        stream_cls: type[_AsyncStreamT] | None,
    ) -> ResponseT | _AsyncStreamT:
        remaining = remaining_retries - 1
        timeout = self._calculate_retry_timeout(remaining, options, response_headers)
        log.info("Retrying request to %s in %f seconds", options.url, timeout)

        await anyio.sleep(timeout)

        return await self._request(
            options=options,
            cast_to=cast_to,
            remaining_retries=remaining,
            stream=stream,
            stream_cls=stream_cls,
        )

    def _request_api_list(
        self,
        model: Type[ModelT],
        page: Type[AsyncPageT],
        options: FinalRequestOptions,
    ) -> AsyncPaginator[ModelT, AsyncPageT]:
        return AsyncPaginator(client=self, options=options, page_cls=page, model=model)

    @overload
    async def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: Literal[False] = False,
    ) -> ResponseT:
        ...

    @overload
    async def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: Literal[True],
        stream_cls: type[_AsyncStreamT],
    ) -> _AsyncStreamT:
        ...

    @overload
    async def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: bool,
        stream_cls: type[_AsyncStreamT] | None = None,
    ) -> ResponseT | _AsyncStreamT:
        ...

    async def get(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        options: RequestOptions = {},
        stream: bool = False,
        stream_cls: type[_AsyncStreamT] | None = None,
    ) -> ResponseT | _AsyncStreamT:
        opts = FinalRequestOptions.construct(method="get", url=path, **options)
        return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)

    @overload
    async def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
        stream: Literal[False] = False,
    ) -> ResponseT:
        ...

    @overload
    async def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
        stream: Literal[True],
        stream_cls: type[_AsyncStreamT],
    ) -> _AsyncStreamT:
        ...

    @overload
    async def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
        stream: bool,
        stream_cls: type[_AsyncStreamT] | None = None,
    ) -> ResponseT | _AsyncStreamT:
        ...

    async def post(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
        stream: bool = False,
        stream_cls: type[_AsyncStreamT] | None = None,
    ) -> ResponseT | _AsyncStreamT:
        opts = FinalRequestOptions.construct(method="post", url=path, json_data=body, files=files, **options)
        return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)

    async def patch(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
        return await self.request(cast_to, opts)

    async def put(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        files: RequestFiles | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="put", url=path, json_data=body, files=files, **options)
        return await self.request(cast_to, opts)

    async def delete(
        self,
        path: str,
        *,
        cast_to: Type[ResponseT],
        body: Body | None = None,
        options: RequestOptions = {},
    ) -> ResponseT:
        opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options)
        return await self.request(cast_to, opts)

    def get_api_list(
        self,
        path: str,
        *,
        # TODO: support paginating `str`
        model: Type[ModelT],
        page: Type[AsyncPageT],
        body: Body | None = None,
        options: RequestOptions = {},
        method: str = "get",
    ) -> AsyncPaginator[ModelT, AsyncPageT]:
        opts = FinalRequestOptions.construct(method=method, url=path, json_data=body, **options)
        return self._request_api_list(model, page, opts)

def make_request_options(
    *,
    query: Query | None = None,
    extra_headers: Headers | None = None,
    extra_query: Query | None = None,
    extra_body: Body | None = None,
    idempotency_key: str | None = None,
    timeout: float | None | NotGiven = NOT_GIVEN,
) -> RequestOptions:
    """Create a dict of type RequestOptions without keys of NotGiven values."""
    options: RequestOptions = {}
    if extra_headers is not None:
        options["headers"] = extra_headers

    if extra_body is not None:
        options["extra_json"] = cast(AnyMapping, extra_body)

    if query is not None:
        options["params"] = query

    if extra_query is not None:
        options["params"] = {**options.get("params", {}), **extra_query}

    if not isinstance(timeout, NotGiven):
        options["timeout"] = timeout

    if idempotency_key is not None:
        options["idempotency_key"] = idempotency_key

    return options

class OtherPlatform:
    def __init__(self, name: str) -> None:
        self.name = name

    def __str__(self) -> str:
        return f"Other:{self.name}"

Platform = Union[
    OtherPlatform,
    Literal[
        "MacOS",
        "Linux",
        "Windows",
        "FreeBSD",
        "OpenBSD",
        "iOS",
        "Android",
        "Unknown",
    ],
]

def get_platform() -> Platform:
    system = platform.system().lower()
    platform_name = platform.platform().lower()
    if "iphone" in platform_name or "ipad" in platform_name:
        # Tested using Python3IDE on an iPhone 11 and Pythonista on an iPad 7
        # system is Darwin and platform_name is a string like:
        # - Darwin-21.6.0-iPhone12,1-64bit
        # - Darwin-21.6.0-iPad7,11-64bit
        return "iOS"

    if system == "darwin":
        return "MacOS"

    if system == "windows":
        return "Windows"

    if "android" in platform_name:
        # Tested using Pydroid 3
        # system is Linux and platform_name is a string like 'Linux-5.10.81-android12-9-00001-geba40aecb3b7-ab8534902-aarch64-with-libc'
        return "Android"

    if system == "linux":
        # https://distro.readthedocs.io/en/latest/#distro.id
        distro_id = distro.id()
        if distro_id == "freebsd":
            return "FreeBSD"

        if distro_id == "openbsd":
            return "OpenBSD"

        return "Linux"

    if platform_name:
        return OtherPlatform(platform_name)

    return "Unknown"

class OtherArch:
    def __init__(self, name: str) -> None:
        self.name = name

    def __str__(self) -> str:
        return f"other:{self.name}"

Arch = Union[OtherArch, Literal["x32", "x64", "arm", "arm64", "unknown"]]

def get_architecture() -> Arch:
    python_bitness, _ = platform.architecture()
    machine = platform.machine().lower()
    if machine in ("arm64", "aarch64"):
        return "arm64"

    # TODO: untested
    if machine == "arm":
        return "arm"

    if machine == "x86_64":
        return "x64"

    # TODO: untested
    if python_bitness == "32bit":
        return "x32"

    if machine:
        return OtherArch(machine)

    return "unknown"

def _merge_mappings(
    obj1: Mapping[_T_co, Union[_T, Omit]],
    obj2: Mapping[_T_co, Union[_T, Omit]],
) -> Dict[_T_co, _T]:
    """Merge two mappings of the same type, removing any values that are instances of `Omit`.

    In cases with duplicate keys the second mapping takes precedence.
    """
    merged = {**obj1, **obj2}
    return {key: value for key, value in merged.items() if not isinstance(value, Omit)}


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

import tensorflow as tf

def lrelu(inputs, slope=0.0):
    return tf.maximum(inputs, slope * inputs)

def conv2d(input,name,num_in,num_out):
    with tf.variable_scope(name):
        weights = tf.get_variable('weights',shape=[3,3,num_in,num_out],dtype=tf.float32)
        biases = tf.get_variable('biases',shape=[num_out],dtype=tf.float32)
        return tf.nn.conv2d(input,weights,strides=[1,1,1,1],padding='SAME') + biases

def VGG16_Feature_Extractor(input,reuse=None):
    with tf.variable_scope('vgg_16',reuse=reuse):
        with tf.variable_scope('conv1'):
            conv1_1 = conv2d(input,'conv1_1',3,64)
            relu1_1 = lrelu(conv1_1)
            conv1_2 = conv2d(relu1_1,'conv1_2',64,64)
            relu1_2 = lrelu(conv1_2)

        maxpool1 = tf.nn.max_pool(relu1_2,[1,2,2,1],[1,2,2,1],'VALID')

        with tf.variable_scope('conv2'):
            conv2_1 = conv2d(maxpool1,'conv2_1',64,128)
            relu2_1 = lrelu(conv2_1)
            conv2_2 = conv2d(relu2_1,'conv2_2',128,128)
            relu2_2 = lrelu(conv2_2)

        maxpool2 = tf.nn.max_pool(relu2_2,[1,2,2,1],[1,2,2,1],'VALID')

        with tf.variable_scope('conv3'):
            conv3_1 = conv2d(maxpool2,'conv3_1',128,256)
            relu3_1 = lrelu(conv3_1)
            conv3_2 = conv2d(relu3_1,'conv3_2',256,256)
            relu3_2 = lrelu(conv3_2)
            conv3_3 = conv2d(relu3_2,'conv3_3',256,256)
            relu3_3 = lrelu(conv3_3)

        maxpool3 = tf.nn.max_pool(relu3_3,[1,2,2,1],[1,2,2,1],'VALID')

        with tf.variable_scope('conv4'):
            conv4_1 = conv2d(maxpool3,'conv4_1',256,512)
            relu4_1 = lrelu(conv4_1)
            conv4_2 = conv2d(relu4_1,'conv4_2',512,512)
            relu4_2 = lrelu(conv4_2)
            conv4_3 = conv2d(relu4_2,'conv4_3',512,512)
            relu4_3 = lrelu(conv4_3)

        maxpool4 = tf.nn.max_pool(relu4_3,[1,2,2,1],[1,2,2,1],'VALID')

        with tf.variable_scope('conv5'):
            conv5_1 = conv2d(maxpool4,'conv5_1',512,512)
            relu5_1 = lrelu(conv5_1)
            conv5_2 = conv2d(relu5_1,'conv5_2',512,512)
            relu5_2 = lrelu(conv5_2)

    return conv1_2, conv2_2, conv3_2, conv4_2, conv5_2


# importing modules ploting and animation
import matplotlib.pyplot as p
import matplotlib.animation as a

# animation that takes its input from a dictionary
planets = {
    "0": ["3", "ko", 15],
    "1": ["4", "go", 5],
    "2": ["3", "ro", 6],
    "3": ["2", "yo", 8],
    "4": ["5", "bo", 10],
    "5": ["6", "ro", 20]
}
fig, ax = p.subplots()
max_frames = 6

def animate(frame):
    global ax
    global max_frames
    ax.set_xlim(0, 10)
    ax.set_ylim(-1, 7)
    w = planets.get(str(frame))
    value1 = int(w[0])
    #print(value1)
    attr = w[1]
    attr_clear = "wo"
    value2 = int(w[2])
    #print(value2)
    ax.plot(frame, value1, attr, markersize = value2)

    print("Frame:", frame, "value1", w)

def run():
    global fig
    simple_animation = a.FuncAnimation(fig, animate, frames=max_frames, interval=250)
    p.show()

run()


import os

import zeep

def test_hello_world():
    path = os.path.join(
        os.path.dirname(os.path.realpath(__file__)), "hello_world_recursive.wsdl"
    )
    client = zeep.Client(path)
    client.wsdl.dump()


import requests
import tkinter as tk
from datetime import datetime

def trackBitcoin():
    url = "https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD,JPY,EUR"
    response = requests.get(url).json()
    USDPrice = response["USD"]
    EURPrice = response["EUR"]
    JPPrice = response["JPY"]
    time = datetime.now().strftime("%H:%M:%S %d/%m/%y")

    labelUSDPrice.config(text = "$" + str(USDPrice))
    labelEURPrice.config(text = "€" + str(EURPrice))
    labelJPPrice.config(text = "¥" + str(JPPrice))
    labelTime.config(text = "TIME: " + time)

    window.after(1000, trackBitcoin)

window = tk.Tk()
window.geometry("300x350")
window.title("Bitcoin Tracker")

f1 = ("poppins", 24, "bold")
f2 = ("poppins", 22, "bold")
f3 = ("poppins", 18, "normal")

label = tk.Label(window, text = "BITCOIN PRICE", font = f1)
label.pack(pady = 20)

labelUSDPrice = tk.Label(window, font = f2)
labelUSDPrice.pack(pady = 0)

labelEURPrice = tk.Label(window, font = f2)
labelEURPrice.pack(pady=0)

labelJPPrice = tk.Label(window, font = f2)
labelJPPrice.pack(pady = 0)

labelTime = tk.Label(window, font = f3)
labelTime.pack(pady = 10)

trackBitcoin()

window.mainloop()


import socket
import threading
import time

class Client:
    def __init__(self, username, port, address="0.0.0.0", buffer=4096) -> None:
        self.__socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.__serverAddress = '0.0.0.0'
        self.__serverPort = 9001
        self.__username = username
        self.__address = address
        self.__port = int(port)
        self.__buffer = buffer
        self.__lastSenttime = time.time()
        self.__connection = True

    def start(self):
        print("Starting up on {} ".format(self.__serverPort))
        print(self.__address, self.__port)
        self.__socket.bind((self.__address, self.__port))
        thread_send = threading.Thread(target=self.send)
        thread_recive = threading.Thread(target = self.recive)
        thread_checkConnectiontime = threading.Thread(target=self.checkTime)

        try:
            while self.__connection:
                thread_checkConnectiontime.start()
                thread_send.start()
                thread_send.join()
                thread_checkConnectiontime.join()
                thread_recive.start()
                thread_recive.join()

        except KeyboardInterrupt as e:
            print("keyboardInterrrupt called!" + str(e))

        except OSError as e:
            print("OS Error ! " + str(e))

        finally:
            self.close()

    def send(self):
        while self.__connection:
            try:
                message = input("Input message your messsage : ")
                if message == "exit":
                    break

                if not message:
                    print("No message please input again\n")
                    continue

                bMessage = bytes(self.__username + " : " + message, "utf-8")

                # サーバーへデータを送信
                sent = self.__socket.sendto(bMessage, (self.__serverAddress, self.__serverPort))
                print('send {} bytes'.format(sent))
                self.__lastSenttime = time.time()

            except KeyboardInterrupt as e:
                print("keyboardInterrrupt called!" + str(e))
                break

    def recive(self):
        try:
            while self.__connection:
                print("Waiting to recive....")
                data, server = self.__socket.recvfrom(self.__buffer)
                print("Recived {!r}".format(data))
            print("接続が切れました。")
        except KeyboardInterrupt as e:
            print("keyboard interuppted !!!", str(e))
        except OSError as e:
            print("OS Error ! " + str(e))
        # finally:
        #     self.__socket.close()

    def checkTime(self):
        try:
            while True:
                currenttime = time.time()
                if currenttime - self.__lastSenttime  > 600:
                    self.__connection = False
                    break
                time.sleep(1)
        except TimeoutError as e:
            print("セッション有効期限が切れました。")
            print(e)
        finally:
            print("時間切れです。通信を終了します。")
            self.close()

    def close(self):
        print("Closing socket")
        self.__socket.close()

def main():
    # client_address = input("input your client address : ")
    port = input("input port address : ")
    username = input("Input your userName : ")
    client = Client(username, port)
    client.start()

if __name__ == "__main__":
    main()


""" Script to split a set of source target by the identified language into
respective files """
import argparse
from collections import defaultdict
import os
import spacy
from spacy_cld import LanguageDetector

def load_pairs(src_file, dst_file):
    """ Load sources and targets """
    sources, targets = [], []
    with open(src_file, 'r') as src_fh:
        for line in src_fh:
            sources.append(line.strip())
    with open(dst_file, 'r') as dst_fh:
        for line in dst_fh:
            targets.append(line.strip())
    return sources, targets

def write_lang_pair(paths, sents):
    """ Write a couple of :code:`sents` to :code:`paths` """
    assert len(paths) == len(sents)
    for fname, sentence in zip(paths, sents):
        with open(fname, 'a') as fpath:
            print(sentence, file=fpath)

def lang_path(lang, fname):
    """ Sneak the language identifier into the filename """
    fname, __ext = os.path.splitext(fname)
    return '.'.join([fname, lang])

def main(sources_path, targets_path):
    """ Identify language of (sources, targets) pairs and write them back in
    new files """
    nlp = spacy.load('en')
    language_detector = LanguageDetector()
    nlp.add_pipe(language_detector)

    sources, targets = load_pairs(sources_path, targets_path)
    assert len(sources) == len(targets)
    print("Processing", len(sources), "pairs...")
    lang_counts = defaultdict(int)

    for src_sent, tgt_sent in zip(sources, targets):
        combined_sent = ' '.join([src_sent, tgt_sent])
        doc = nlp(combined_sent)
        for lang in doc._.languages:
            lang_counts[lang] += 1
            lang_sources_path = lang_path(lang, sources_path)
            lang_targets_path = lang_path(lang, targets_path)
            write_lang_pair((lang_sources_path, lang_targets_path),
                            (src_sent, tgt_sent))

    for lang, count in lang_counts.items():
        print("{}: {}".format(lang, count))

    print("Total resulting pairs:", sum(lang_counts.values()))

if __name__ == '__main__':
    PARSER = argparse.ArgumentParser()

    PARSER.add_argument('-s', '--sources', type=str, default='tmp/sources.txt')
    PARSER.add_argument('-t', '--targets', type=str, default='tmp/targets.txt')

    ARGS = PARSER.parse_args()

    main(ARGS.sources, ARGS.targets)


# -*- coding: utf-8 -*-
"""
Created on Wed Sep 27 01:37:58 2023

@author: USER
"""
"""
報童問題之最佳訂貨量_進階版
c = int(input()) # 成本
r = int(input()) # 售價
N = 8 需求數
q = int(input()) # 訂貨量
s = int(input()) # 殘值
"""
# import math

c = int(input()) # 成本
r = int(input()) # 售價
N = int(input()) # 需求數
s = int(input()) # 殘值

# buyC = []
# for i in range(9):
#     buyC.append(float(input()))

buyC = []
profit = p = 0
while int(profit) != 1:
    p = float(input())
    buyC.append(p)
    profit += p

# c = 2
# r = 10
# N = 8
# s = 1
# buyC = [0.06,0.15,0.22,0.22,0.17,0.1,0.05,0.02,0.01]
# buyC = [0.78, 0.08, 0.1, 0.01, 0.02, 0.0, 0.0, 0.0, 0.1]

Df = Q = 0 # 最終利潤
for q in range(1, N+1):
    # if q == 0:
    #     print(Df)
    # else:
    i=D=0
    while i < q:
        D = D + (r * i - c * q + s * (q - i))*buyC[i]
        i += 1
    D = D + (r * q - c * q + s * (q - i))*(1 - sum(buyC[:q]))

    if Df < D:
        Df = D
        Q = q
print(Q, int(Df))


from unittest.mock import Mock

from nio.block.base import Block
from nio.block.context import BlockContext
from nio.block.terminals import DEFAULT_TERMINAL
from nio.router.base import BlockRouter
from nio.router.context import RouterContext
from nio.service.base import BlockExecution
from nio.signal.base import Signal
from nio.testing.test_case import NIOTestCase

class SenderBlock(Block):

    def __init__(self):
        super().__init__()
        self.id = self.__class__.__name__.lower()

    def process_signals(self, signals, input_id=DEFAULT_TERMINAL):
        self.notify_signals(signals)

class ReceiverBlock(Block):

    def __init__(self):
        super().__init__()
        self.id = self.__class__.__name__.lower()
        self.signal_cache = None

    def process_signals(self, signals, input_id=DEFAULT_TERMINAL):
        self.signal_cache = signals

    def reset_signals(self):
        self.signal_cache = None

class BlockExecutionTest(BlockExecution):

    def __init__(self, id, receivers):
        self.id = id
        self.receivers = receivers

class TestBaseDiagnostics(NIOTestCase):

    def test_diagnostics(self):
        """ Checking that router delivers signals and diagnostics """

        instance_id = "instance1"
        service_id = "service1_id"
        service_name = "service1"

        block_router = BlockRouter()
        context = BlockContext(block_router, dict())

        # create blocks
        sender_block = SenderBlock()
        sender_block.configure(context)
        receiver_block = ReceiverBlock()
        receiver_block.configure(context)

        # create context initialization data
        receiver_block_id = receiver_block.id()
        sender_block_id = sender_block.id()
        blocks = {receiver_block_id: receiver_block,
                  sender_block_id:sender_block}
        execution = [BlockExecutionTest(id=sender_block.id(),
                                        receivers=[receiver_block.id()])]

        signal_handler = Mock()
        router_context = RouterContext(execution, blocks,
                                       {
                                           "clone_signals": True,
                                           "diagnostics": True
                                       },
                                       mgmt_signal_handler=signal_handler,
                                       instance_id=instance_id,
                                       service_id=service_id,
                                       service_name=service_name)

        block_router.do_configure(router_context)
        block_router.do_start()

        common_attribute = "common attribute"
        common_value = "common value"
        signals = [Signal({common_attribute: common_value})]

        # make sure nothing has been delivered
        self.assertIsNone(receiver_block.signal_cache)

        self.assertEqual(
            len(block_router._diagnostic_manager._blocks_data), 0)
        sender_block.process_signals(signals)

        # make sure signals made it and diagnostic_manager.on_signal_delivery
        # was invoked
        self.assertIsNotNone(receiver_block.signal_cache)
        self.assertEqual(
            len(block_router._diagnostic_manager._blocks_data), 1)

        # make signal handler to be invoked
        block_router._diagnostic_manager._send_diagnostic()
        signal = signal_handler.call_args[0][0]
        self.assertEqual(signal.type, "RouterDiagnostic")
        self.assertEqual(signal.instance_id, instance_id)
        self.assertEqual(signal.service_id, service_id)
        self.assertEqual(signal.service, service_name)
        self.assertEqual(len(signal.blocks_data), 1)
        block_data = signal.blocks_data[0]
        self.assertEqual(block_data["source_type"], sender_block.type())
        self.assertEqual(block_data["source"], sender_block.id())
        self.assertEqual(block_data["target_type"], receiver_block.type())
        self.assertEqual(block_data["target"], receiver_block.id())
        self.assertEqual(block_data["count"], 1)
        # assert data was cleared after a diagnostic delivery
        self.assertEqual(
            len(block_router._diagnostic_manager._blocks_data), 0)

        block_router.do_stop()

        # setup now a router without diagnostics
        router_context = RouterContext(execution, blocks,
                                       {
                                           "clone_signals": True,
                                           "diagnostics": False
                                       },
                                       mgmt_signal_handler=signal_handler)
        block_router.do_configure(router_context)
        block_router.do_start()
        receiver_block.reset_signals()
        sender_block.process_signals(signals)
        self.assertIsNotNone(receiver_block.signal_cache)
        # assert that diagnostics were not delivered
        self.assertEqual(
            len(block_router._diagnostic_manager._blocks_data), 0)

        block_router.do_stop()


#! /usr/bin/env python3

from PIL import Image

s = Image.open("16_mozart.gif")
w, h = s.size
t = Image.new(s.mode, (2*w, h))
pink = 195
pxs = list(s.getdata())
for row in range(h):
    ls = pxs[row*w:(row+1)*w]
    start = w - ls.index(pink)
    for x, px in enumerate(ls, start=start):
        t.putpixel((x, row), px)

t.show()  # romance


import bd_pedidos as bp
def consulta():
    tipos_consultas = ['1) - Checkup geral', '2) - Ultrassongrafia', '3) - Exame de sangue', '4) - Exame de fezes', '5) - Exame de urina', '6) - Exame oftalmológico', '7) - Exame odontológico', '8) - Temeperatura corporal', '9) - Endoscopia']

    print('°---------------------------°')
    for item in tipos_consultas:
        print('|', item)
    print('°---------------------------°')

def tosa():
    turnos = ['1) - Manhã','2) - Tarde']
    horarios_manha = ['6:00', '8:00', '10:00']
    horarios_tarde = ['13:00', '15:00', '17:00']
    VALOR = '50.00'
    print(turnos[0])
    print(turnos[1])

    op = int(input("Selecione o turno >>> "))

    if op == 1:
        print(turnos[0])
        for h in horarios_manha:
            print('\n',h, 'Horas')
        print("Valor da Tosa: R$50.00")
        escolha = input('Confirmar Tosa [S]- Sim / [N] - Não >>> ')
        if escolha == 'S' or 's':
            dia = input("Diga-nos o dia que deseja (seg a sex)>>> ")
            horario = input("Informe o  horario >>> ")
            bp.pedido_tosa(dia, horario, VALOR)

    elif op == 2:
        print(turnos[1])
        for h in horarios_tarde:
            print(h, 'Horas\n')
        print("Valor da Tosa: R$50.00\n")
        escolha = input("Confirmar Tosa [S]- Sim / [N] - Não")
        if escolha == 'S' or 's':
            dia = input("Diga-nos o dia que deseja (seg a sex)>>> ")
            horario = input("Informe o  horario >>> ")
            bp.pedido_tosa(dia, horario, VALOR)

def banho ():
    horarios_manha = ["1) - 8:00", "2) - 10:00", "3) - 12:00"]
    horarios_tarde = ["1) - 14:00", "2) - 16:00", "3) - 18:00"]
    print('''\nTurnos:
    1) - Manhã
    2) - Tarde\n''')

    turno = int(input('Selecione um turno >>> '))

    if (turno == 1):
        print("\n____________\n")
        for hora in horarios_manha:
            print("|", hora)
        print("\n____________\n")

    elif (turno == 2):
        print("\n____________\n")
        for hora in horarios_tarde:
            print("|", hora)
        print("\n____________\n")


import argparse
import sys
from pathlib import Path
import json

import torch
from torch.utils.data import DataLoader
from torch.nn.utils.rnn import pad_sequence
import torch.optim as optim
from transformers import AutoTokenizer, AutoModel
from tqdm import tqdm

from dataset import ShinraData, NerDataset, ner_collate_fn
from model import BertForMultilabelNER, create_pooler_matrix

import os

def ner_for_shinradata(model, tokenizer, shinra_dataset, device):
    processed_data = shinra_dataset.ner_inputs
    dataset = NerDataset(processed_data, tokenizer)
    total_preds, _ = predict(model, dataset, device, sent_wise=True)

    shinra_dataset.add_nes_from_iob(total_preds)

    return shinra_dataset

def predict(model, dataset, device, sent_wise=False):
    model.eval()
    dataloader = DataLoader(dataset, batch_size=8, collate_fn=ner_collate_fn)

    total_preds = []
    total_trues = []
    with torch.no_grad():
        for step, inputs in enumerate(dataloader):
            input_ids = inputs["tokens"]
            word_idxs = inputs["word_idxs"]

            labels = inputs["labels"]

            input_ids = pad_sequence([torch.tensor(t) for t in input_ids], padding_value=0, batch_first=True).to(device)
            attention_mask = input_ids > 0
            pooling_matrix = create_pooler_matrix(input_ids, word_idxs, pool_type="head").to(device)

            preds = model.predict(
                input_ids=input_ids,
                attention_mask=attention_mask,
                word_idxs=word_idxs,
                pooling_matrix=pooling_matrix
            )

            total_preds.append(preds)
            # test dataの場合truesは使わないので適当にpredsを入れる
            total_trues.append(labels if labels[0] is not None else preds)

    attr_num = len(total_preds[0])
    total_preds = [[pred for preds in total_preds for pred in preds[attr]] for attr in range(attr_num)]
    total_trues = [[true for trues in total_trues for true in trues[attr]] for attr in range(attr_num)]

    if sent_wise:
        total_preds = [[total_preds[attr][idx] for attr in range(attr_num)] for idx in range(len(total_preds[0]))]
        total_trues = [[total_trues[attr][idx] for attr in range(attr_num)] for idx in range(len(total_trues[0]))]

    return total_preds, total_trues

def parse_arg():
    parser = argparse.ArgumentParser()

    parser.add_argument("--input_path", type=str, help="Specify input path in SHINRA2020")
    parser.add_argument("--model_path", type=str, help="Specify attribute_list path in SHINRA2020")
    parser.add_argument("--output_path", type=str, help="Specify attribute_list path in SHINRA2020")
    parser.add_argument("--cuda", type=int, help="Specify attribute_list path in SHINRA2020")
    args = parser.parse_args()

    return args

if __name__ == "__main__":
    args = parse_arg()

    if torch.cuda.is_available():
        device = f"cuda:{args.cuda}"
    else:
        device = "cpu"

    bert = AutoModel.from_pretrained("cl-tohoku/bert-base-japanese")
    tokenizer = AutoTokenizer.from_pretrained("cl-tohoku/bert-base-japanese")

    input_path = Path(args.input_path)
    assert (input_path / "attributes.txt").exists()
    with open(input_path / "attributes.txt", "r") as f:
        attributes = [attr for attr in f.read().split("\n") if attr != '']

    model = BertForMultilabelNER(bert, len(attributes))
    model.load_state_dict(torch.load(args.model_path))
    model.to(device)

    # dataset = [ShinraData(), ....]
    dataset = ShinraData.from_shinra2020_format(Path(args.input_path))
    # dataset = [d for idx, d in enumerate(dataset) if idx < 20 and d.nes is not None]

    def _post_processing(d):
        d['page_id'] = int(d['page_id'])
        for k1 in ['text_offset', 'html_offset', 'token_offset']:
            for k2 in ['start', 'end']:
                for k3 in ['offset', 'line_id']:
                    try:
                        d[k1][k2][k3] = int(d[k1][k2][k3])
                    except KeyError:
                        pass
        return d

    # dataset = [ner_for_shinradata(model, tokenizer, d, device) for d in dataset]
    with open(args.output_path, "w") as f:
        for data in dataset:
            if data.nes is None:
                processed_data = ner_for_shinradata(model, tokenizer, data, device)
                lst = []
                for ne in processed_data.nes:
                    lst.append(json.dumps(_post_processing(ne), ensure_ascii=False))
                if len(lst) == 0:
                    continue
                f.write("\n".join(lst))
                f.write("\n")


# -*- coding: utf-8 -*-
"""
lesson4_nn.py

Tama skripti sisaltaa luennon4 harjoituksia
- lahimman naapurin analyysit

Created on Tue Nov 13 15:11:30 2018

@author: Suvi Hatunen
"""
from shapely.geometry import Point, MultiPoint
from shapely.ops import nearest_points
import geopandas as gpd

def nearest(row, geom_union, df2, geom1_col='geometry', geom2_col='geometry', ser_column=None):
    #alla fiksu tapa kommentoida def-funktioita, nakyy helpissa
    """
    Finds the closest point from the set of points.

    Parameters
    ----------

    geom_union: shapely.MultiPoint

    """
    # Find geometry that is closest
    nearest = df2[geom2_col] == nearest_points(row[geom1_col], geom_union)[1]

    # get the corresponding value from df2
    value = df2[nearest][ser_column].get_values()[0]

    return value

# datapolut
fp1 = 'L4_data/PKS_suuralue.kml'
fp2 = 'L4_data/addresses.shp'

# driverin aktivointi
gpd.io.file.fiona.drvsupport.supported_drivers['KML'] = 'rw'

# luetaan datat
polys = gpd.read_file(fp1, driver='KML')
src_points = gpd.read_file(fp2)

# unary union
unary_union = src_points.unary_union

# calculate centroif for the polygons
polys['centroid'] = polys.centroid

# find the nearest station for each polygon centroid
polys['nearest_id'] = polys.apply(nearest, geom_union=unary_union, df2=src_points, geom1_col='centroid', ser_column='id', axis=1)


class Product(object):
    def __init__(self, price, item_name, weight, status="for sale"):
        self.price =price
        self.item_name = item_name
        self.weight = weight
        self.status = "for sale"

    def sell(self):
        self.status = "Sold"
        return self

    def add_tax(self, tax):
        self.price += self.price*tax
        return self

    def return_something(self, reason):
        if reason == 'defective':
            self.status = 'defective'
            self.price = 0
        elif reason == 'new':
            self.status = 'for sale'
        else :
            self.status = 'used'
            self.price = 0.8*self.price
        return self

    def display_info(self):
        print("Item name %s and have price %s ,weight :%s and status is %s" % (self.item_name,
                                                                               self.price,
                                                                               self.weight,
                                                                               self.status))


s = 'one4seveneight'

def solution(s):
    # s : 문자열 배열

    answer = []

    dict_list = ['zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']  # index - 숫자 변환 리스트
    stack = []  # 한 글자씩 확인

    for i in range(len(s)):
        c = s[i]  # 문자열에서 한 글자 가져오기
        answer.append(c) if c.isdigit() else stack.append(c)  # 숫자면 정답에 추가, 문자면 스택에 추가
        stack_join = ''.join(stack)  # 스택 단어 합치기

        if stack_join in dict_list:  # dict_list에 stack_join이 존재하면
            answer.append(str(dict_list.index(stack_join)))  # index(숫자)로 정답에 추가
            stack = []  # 스택 비우기

    return int(''.join(answer))

answer = solution(s)
print(answer)


import gradio as gr
import numpy as np
from ultralytics import YOLO
import cv2
import matplotlib.pyplot as plt
from omegaconf import OmegaConf
import torch
import os

import sys
sys.path.append('lama-with-refiner')
from saicinpainting.evaluation.refinement import refine_predict
from saicinpainting.training.trainers import load_checkpoint

def get_inpaint_model():
    predict_config = OmegaConf.load('lama-with-refiner/configs/prediction/default.yaml')
    predict_config.model.path = 'big-lama'
    predict_config.refiner.gpu_ids = '0'

    device = torch.device(predict_config.device)
    train_config_path = os.path.join(predict_config.model.path, 'config.yaml')

    train_config = OmegaConf.load(train_config_path)
    train_config.training_model.predict_only = True
    train_config.visualizer.kind = 'noop'

    checkpoint_path = os.path.join(predict_config.model.path,
                                   'models',
                                   predict_config.model.checkpoint)

    model = load_checkpoint(train_config, checkpoint_path, strict=False, map_location='cpu')
    model.freeze()
    model.to(device)
    return model,predict_config

model = YOLO("yolov8x-seg.pt")
inpaint_model,predict_config = get_inpaint_model()

import gradio as gr
import numpy as np
from ultralytics import YOLO
import cv2
import matplotlib.pyplot as plt
from omegaconf import OmegaConf
import torch
import os
from scipy.ndimage import gaussian_filter

import sys
sys.path.append('lama-with-refiner')
from saicinpainting.evaluation.refinement import refine_predict
from saicinpainting.training.trainers import load_checkpoint

id2objects = {}
segment_masks = None

def process_classes(results):
    global segment_masks
    global id2objects
    segment_masks = None
    segment_masks = results[0].masks.data.cpu().numpy()
    classes = results[0].boxes.cls.cpu().numpy()
    conf = results[0].boxes.conf.cpu().numpy()
    dec = results[0].names
    id2objects = {}

    for i in range(len(classes)):
        id2objects[i] = dec[classes[i]], round(conf[i], 2)
    s = ""
    for k, v in id2objects.items():
        s += f"{k}: {v[0]} {str(v[1])}\n"
    return s

def get_mask(img):
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    cv2.imwrite("test_img.jpg", img)

    results = model.predict(img)

    img = results[0].plot({'line_width': False, 'boxes': False, 'conf': True, 'labels': True})
    return img, process_classes(results)

def ceil_modulo(x, mod):
    if x % mod == 0:
        return x
    return (x // mod + 1) * mod

def pad_img_to_modulo(img, mod):
    channels, height, width = img.shape

    out_height = ceil_modulo(height, mod)
    out_width = ceil_modulo(width, mod)
    return np.pad(img, ((0, 0), (0, out_height - height), (0, out_width - width)), mode='symmetric')

def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]

    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image

    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)

    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)

    # return the resized image
    return resized

def remove(img, s):
    with torch.no_grad():
        ids = [int(x) for x in s.split(",") if x]

        img = image_resize(img, height = 720)
        img = img.astype('float32') / 255
        img = np.transpose(img, (2, 0, 1))

        print(np.unique(segment_masks))
        temp_masks = segment_masks[ids]
        print(temp_masks.shape)
        masks = np.zeros((img.shape[1], img.shape[2]), dtype = bool)
        for mask in temp_masks:
            print(mask.shape)
            mask = cv2.resize(mask, (img.shape[2], img.shape[1]), interpolation = cv2.INTER_NEAREST).astype(bool)
            masks = masks | mask

        kernel = np.ones((7, 7), np.uint8)
        masks = masks.astype(np.uint8) * 255
        masks = cv2.GaussianBlur(masks, (7,7), 0)
        masks = cv2.dilate(masks, kernel, iterations = 3)

        batch = dict(image=img, mask=masks[None, ...])

        batch['unpad_to_size'] = [torch.tensor([batch['image'].shape[1]]),torch.tensor([batch['image'].shape[2]])]
        batch['image'] = torch.tensor(pad_img_to_modulo(batch['image'], predict_config.dataset.pad_out_to_modulo))[None].to(predict_config.device)
        batch['mask'] = torch.tensor(pad_img_to_modulo(batch['mask'], predict_config.dataset.pad_out_to_modulo))[None].float().to(predict_config.device)

        cur_res = refine_predict(batch, inpaint_model, **predict_config.refiner)
        cur_res = cur_res[0].permute(1,2,0).detach().cpu().numpy()

        cur_res = np.clip(cur_res * 255, 0, 255).astype('uint8')

    return cur_res, masks

if __name__ == "__main__":
    app = gr.Blocks()
    with app:
        with gr.Row():
            with gr.Column():
                input_img = gr.Image(label="input image")
                run_btn = gr.Button(variant = "primary")
            output_mask = gr.Image(label="masks", image_mode = "RGB")
        with gr.Row():
            with gr.Column():
                list_of_objects = gr.Textbox(label="Objects id")
                objects_to_remove = gr.Textbox(label="Objects id to remove")
                remove_button = gr.Button("remove")
            with gr.Column():
                output_img = gr.Image(label = "result")
                black_white = gr.Image(label = "segment mask")
        run_btn.click(get_mask, [input_img], [output_mask, list_of_objects])
        remove_button.click(remove, [input_img,objects_to_remove], [output_img, black_white])
    print("sdfdsf")
    app.launch(share=True)


#!/usr/bin/python
#coding=utf8
#===============================================================================
#
# File:     BEFE_Span - Span class and related utility functions
#
# Contains: class Span
#

# External dependencies...
from BEFE_Struct    import *
from BEFE_functions import *

#===============================================================================
#
# Class:    Span
#
# Purpose:  Represent an inclusive range of integers
#
# Usage:    span = Span()`
#           span = Span(min,max[,step])
#
# Where:    min  - int/atr: Span minimum
#           max  - int/str: Span maximum
#           step - int/str: Span step
#
# Notes:    If min, max, or step are passed as strings they must be as follows:
#
#             min  - Integer, single byte hex (e.g. "0x35"), or single quoted char (e.g. "'a'")
#             max  - Integer, hex, or quoted char
#             step - Integer
#
#           If min > max they are swapped and step is forced negative, otherwise
#           step is forced positive.
#

class Span(Struct):

  def __init__(self,*args):

    super(Span,self).__init__('min,max,step')

    # If one arg and it's a Span, copy construct...
    if args and len(args) == 1 and type(args[0]) is Span:
      that = args[0]
      self.min = that.min
      self.max = that.max
      return

    # Get on with it...
    self.min  = None
    self.max  = None
    self.step = None

    if len(args) >= 1:
      self.min = args[0]
    if len(args) >= 2:
      self.max = args[1]
    if len(args) == 3:
      self.step = args[2]
    if len(args) > 3:
      raise RuntimeError("Expected maximum of 3 arguments (min, max, step), got "+str(len(args)))

    # If only one string passed, split on ':'...
    if type(self.min) == str and ':' in self.min:
      parts = self.min.split(':')
      self.min = parts[0]
      self.max = parts[1]
      if len(parts) > 2:
        self.step = parts[2]

    # Convert strings to integers...
    if type(self.min) == str:
      #if len(self.min) == 1:
      #  self.min = '"' + self.min + '"'
      min = IntStrToInt(self.min)
      if min is None: min = QuoteStrToInt(self.min)
      if min is None: min = HexStrToInt(self.min)
      self.min = min

    if type(self.max) == str:
      #if len(self.max) == 1:
      #  self.max = '"' + self.max + '"'
      max = IntStrToInt(self.max)
      if max is None: max = QuoteStrToInt(self.max)
      if max is None: max = HexStrToInt(self.max)
      self.max = max

    if type(self.step) == str:
      self.step = IntStrToInt(self.step)

    if self.step is None:
      self.step = 1
    if self.min is None:
      self.min = 0
    if self.max is None:
      self.max = 0
    if self.min != None and self.max != None and self.min > self.max:
      if self.step != None and self.step > 0:
        self.step = -self.step

  def __str__(self):

    smin  = ''
    smax  = ''
    if self.min != None:
      smin = str(self.min)
    if self.max != None:
      smax = str(self.max)
    s = smin + ":" + smax

    if self.step != None and self.step != 1:
      s += ':' + str(self.step)

    return s

  def __repr__(self):

    return "Span("+str(self)+")"

  def __len__(self):

    if self.min is None or self.max is None:
      return 0
    return self.max - self.min if self.max >= self.min else self.min-self.max

#===============================================================================
#
# __main__
#

if __name__ == '__main__':

  if True:
    cases = [(1,10),
             (10,1,2),
             ('"a"','"c"'),
             ('a','c'),
             ("10","1"),
             ("0x0a","0x01"),
             ("'\\x0a'","'\\x1f'"),
             ("0:10",),
             ('0:10:2',),
             ('10:0:2',),
             ('0:',),
             ('::3',),
            ]

    maxLen = 0
    for case in cases:
      maxLen = max(maxLen,len(repr(case))+1)
    for case in cases:
      a = Span(*case)
      c = repr(case) + ':'
      c += ' '*(maxLen-len(c))
      print("Case: %s Result = %s"%(c,str(a)))

  if False:
    cases = [0,1,10,255,65535,-1,-20,-65537]
    for case in cases:
      s = IntToHex(case)
      print("%s: %s"%(case,repr(s)))


import torch
import torch.nn as nn
import math

from .transformer import build_position_encoding
from .common import ModelInterface
from torch.cuda.amp import autocast

from torchreid.losses import AngleSimpleLinear

__all__ = ['build_q2l']

class GroupWiseLinear(nn.Module):
    def __init__(self, num_class, hidden_dim, use_bias=True):
        super().__init__()
        self.num_class = num_class
        self.hidden_dim = hidden_dim
        self.use_bias = use_bias

        self.weight = nn.Parameter(torch.Tensor(1, num_class, hidden_dim))
        if use_bias:
            self.bias = nn.Parameter(torch.Tensor(1, num_class))
        self.reset_parameters()

    def reset_parameters(self):
        stdv = 1. / math.sqrt(self.weight.size(2))
        for i in range(self.num_class):
            self.weight[0][i].data.uniform_(-stdv, stdv)
        if self.use_bias:
            for i in range(self.num_class):
                self.bias[0][i].data.uniform_(-stdv, stdv)

    def forward(self, x):
        # x: B,K,d
        x = (self.weight * x).sum(-1)
        if self.use_bias:
            x = x + self.bias
        return x

class BackboneWrapper(nn.Module):
    def __init__(self, backbone, position_embedding):
        super().__init__()
        self.backbone = backbone
        self.position_embedding = position_embedding
        self.num_channels = backbone.get_num_features()

    def forward(self, input):
        out = self.backbone(input, return_featuremaps=True)
        pos = self.position_embedding(out).to(out.dtype)
        return [out], [pos]

class Query2Label(ModelInterface):
    def __init__(self,
                backbone,
                transfomer,
                num_classes=80,
                pretrain=False,
                **kwargs):
        """[summary]

        Args:
            backbone ([type]): backbone model.
            transfomer ([type]): transformer model.
            num_classes ([type]): number of classes. (80 for MSCOCO).
        """
        super().__init__(**kwargs)
        self.backbone = backbone
        self.transformer = transfomer
        self.num_class = num_classes
        assert self.loss in ['asl', 'bce', 'am_binary'], "Q2L supports only ASL, BCE pr AM Binary losses"

        hidden_dim = transfomer.get_hidden_dim()
        backbone_features = backbone.num_channels
        self.input_proj = nn.Conv2d(backbone_features, hidden_dim, kernel_size=1)
        self.query_embed = nn.Embedding(num_classes, hidden_dim)
        if self.loss == "am_binary":
            self.fc = AngleSimpleLinear(hidden_dim, num_classes)
        else:
            assert self.loss in ['asl', 'bce']
            self.fc = GroupWiseLinear(num_classes, hidden_dim, use_bias=True)

    def forward(self, input):
        with autocast(enabled=self.mix_precision):
            src, pos = self.backbone(input)
            src, pos = src[-1], pos[-1]

            query_input = self.query_embed.weight
            hs = self.transformer(self.input_proj(src), query_input, pos)[0] # B,K,d
            logits = self.fc(hs[-1])
            if self.similarity_adjustment:
                logits = self.sym_adjust(logits, self.amb_t)

            if not self.training:
                return [logits]

            elif self.loss in ['asl', 'bce', 'am_binary']:
                    out_data = [logits]
            else:
                raise KeyError("Unsupported loss: {}".format(self.loss))

            return tuple(out_data)

    def get_config_optim(self, lrs):
        parameters = [
            {'params': self.backbone.named_parameters()},
            {'params': self.fc.named_parameters()},
            {'params': self.input_proj.parameters(), 'weight_decay': 0.},
            {'params': self.query_embed.parameters(), 'weight_decay': 0.}
        ]
        if isinstance(lrs, list):
            assert len(lrs) == len(parameters)
            for lr, param_dict in zip(lrs, parameters):
                param_dict['lr'] = lr
        else:
            assert isinstance(lrs, float)
            for param_dict in parameters:
                param_dict['lr'] = lrs

        return parameters

def build_q2l(backbone, transformer, hidden_dim=2048, pretrain=False, input_size=448, **kwargs):
    position_emb = build_position_encoding(hidden_dim=hidden_dim, img_size=input_size)
    wrapped_model = BackboneWrapper(backbone, position_emb)
    model = Query2Label(
        backbone=wrapped_model,
        transfomer=transformer,
        pretrain=pretrain,
        **kwargs
    )

    return model


import random
from tkinter import *

def new_game():
    global aMoves
    global bMoves
    global game

    aMoves = []
    bMoves = []
    label.config(text=f"It's {player}'s turn")
    game = True

    for r in range(3):
        for cl in range(3):
            buttons[r][cl].config(text="")

def check_winner():
    global game
    if confirm_combination(aMoves):
        label.config(text=f"{players[0]} Wins")
        game = False
    elif confirm_combination(bMoves):
        label.config(text=f"{players[1]} Wins")
        game = False
    elif len(aMoves) + len(bMoves) == 9:
        label.config(text=f"Its a Tie")
        game = False
    else:
        label.config(text=f"It's {player}'s turn")

def map_move(_row, _col):
    if _row == 0:
        if _col == 0:
            return 0
        elif _col == 1:
            return 1
        else:
            return 2
    elif _row == 1:
        if _col == 0:
            return 3
        elif _col == 1:
            return 4
        else:
            return 5
    else:
        if _col == 0:
            return 6
        elif _col == 1:
            return 7
        else:
            return 8

def confirm_combination(moves_list):
    count = 0
    if len(moves_list) < 3:
        return False
    else:
        for c in winningCombinations:
            for n in c:
                if moves_list.__contains__(n):
                    count += 1
            if count > 2:
                return True
            count = 0
    return False

def next_turn(_row, _col):
    global game
    global player
    if game:
        btn = buttons[_row][_col]
        if btn['text'] == "":
            btn['text'] = player
            buttons[_row][_col] = btn
            check_winner()

            if player == players[0]:
                player = players[1]
                aMoves.append(map_move(_row, _col))

            else:
                bMoves.append(map_move(_row, _col))
                player = players[0]
            check_winner()

window = Tk()
window.title("Tic Tac Toe")

players = ["X", "O"]
aMoves = []
bMoves = []
player = random.choice(players)
game = True

winningCombinations = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 4, 8],
    [2, 4, 6],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8]
]

buttons = [
    [0, 0, 0],
    [0, 0, 0],
    [0, 0, 0]
]

label = Label(text=f"It's {player}'s turn")
label.pack(side="top")

new_game_button = Button(text="New Game", command=lambda: new_game())
new_game_button.pack(side="top")

frame = Frame(window)
frame.pack()

for row in range(3):
    for col in range(3):
        bttn = Button(frame, text="", width=5, height=2, command=lambda _row=row, _col=col: next_turn(_row, _col))
        bttn.grid(row=row, column=col)
        buttons[row][col] = bttn

window.mainloop()