from __future__ import annotations

import codecs
import enum
import os
import re
import typing
from functools import cached_property
from pathlib import Path

import attrs
import docstring_parser
import mypy.build
import mypy.errors
import mypy.find_sources
import mypy.fscache
import mypy.modulefinder
import mypy.nodes
import mypy.options
import mypy.util

from puya import log
from puya.awst.nodes import MethodDocumentation
from puya.parse import SourceLocation
from puya.utils import make_path_relative_to_cwd

if typing.TYPE_CHECKING:
    from collections.abc import Mapping, Sequence, Set

logger = log.get_logger(__name__)
_PUYAPY_SRC_ROOT = Path(__file__).parent
_PUYA_SRC_ROOT = _PUYAPY_SRC_ROOT.parent / "puya"
TYPESHED_PATH = _PUYAPY_SRC_ROOT / "_typeshed"
_MYPY_FSCACHE = mypy.fscache.FileSystemCache()
_MYPY_SEVERITY_TO_LOG_LEVEL = {
    "error": log.LogLevel.error,
    "warning": log.LogLevel.warning,
    "note": log.LogLevel.info,
}

@attrs.frozen
class EmbeddedSource:
    path: Path
    mypy_module_name: str
    puya_module_name: str

    @classmethod
    def from_path(cls, filename: str, *, module_override: str | None = None) -> typing.Self:
        path = _PUYA_SRC_ROOT / "lib_embedded" / filename
        return cls(
            path=path,
            mypy_module_name=path.stem,
            puya_module_name=module_override or path.stem,
        )

class SourceDiscoveryMechanism(enum.Enum):
    explicit_file = enum.auto()
    explicit_directory_walk = enum.auto()
    dependency = enum.auto()

@attrs.frozen
class SourceModule:
    name: str
    node: mypy.nodes.MypyFile
    path: Path
    lines: Sequence[str] | None
    discovery_mechanism: SourceDiscoveryMechanism

@attrs.frozen
class ParseResult:
    mypy_options: mypy.options.Options
    ordered_modules: Mapping[str, SourceModule]
    """All discovered modules, topologically sorted by dependencies.
    The sort order is from leaves (nodes without dependencies) to
    roots (nodes on which no other nodes depend)."""

    @cached_property
    def sources_by_path(self) -> Mapping[Path, Sequence[str] | None]:
        return {s.path: s.lines for s in self.ordered_modules.values()}

    @cached_property
    def explicit_source_paths(self) -> Set[Path]:
        return {
            sm.path
            for sm in self.ordered_modules.values()
            if sm.discovery_mechanism != SourceDiscoveryMechanism.dependency
        }

def parse_and_typecheck(
    paths: Sequence[Path], mypy_options: mypy.options.Options
) -> tuple[mypy.build.BuildManager, dict[str, SourceModule]]:
    """Generate the ASTs from the build sources, and all imported modules (recursively)"""

    # ensure we have the absolute, canonical paths to the files
    resolved_input_paths = {p.resolve() for p in paths}
    # creates a list of BuildSource objects from the contract Paths
    mypy_build_sources = mypy.find_sources.create_source_list(
        paths=[str(p) for p in resolved_input_paths],
        options=mypy_options,
        fscache=_MYPY_FSCACHE,
    )
    build_source_paths = {
        Path(m.path).resolve() for m in mypy_build_sources if m.path and not m.followed
    }
    result = _mypy_build(mypy_build_sources, mypy_options, _MYPY_FSCACHE)
    # Sometimes when we call back into mypy, there might be errors.
    # We don't want to crash when that happens.
    result.manager.errors.set_file("<puyapy>", module=None, scope=None, options=mypy_options)
    missing_module_names = {s.module for s in mypy_build_sources} - result.manager.modules.keys()
    # Note: this shouldn't happen, provided we've successfully disabled the mypy cache
    assert (
        not missing_module_names
    ), f"mypy parse failed, missing modules: {', '.join(missing_module_names)}"

    # order modules by dependency, and also sanity check the contents
    ordered_modules = {}
    for scc_module_names in mypy.build.sorted_components(result.graph):
        for module_name in scc_module_names:
            module = result.manager.modules[module_name]
            assert (
                module_name == module.fullname
            ), f"mypy module mismatch, expected {module_name}, got {module.fullname}"
            assert module.path, f"no path for mypy module: {module_name}"
            module_path = Path(module.path).resolve()
            if module_path.is_dir():
                # this module is a module directory with no __init__.py, ie it contains
                # nothing and is only in the graph as a reference
                pass
            else:
                _check_encoding(_MYPY_FSCACHE, module_path)
                lines = mypy.util.read_py_file(str(module_path), _MYPY_FSCACHE.read)
                if module_path in resolved_input_paths:
                    discovery_mechanism = SourceDiscoveryMechanism.explicit_file
                elif module_path in build_source_paths:
                    discovery_mechanism = SourceDiscoveryMechanism.explicit_directory_walk
                else:
                    discovery_mechanism = SourceDiscoveryMechanism.dependency
                ordered_modules[module_name] = SourceModule(
                    name=module_name,
                    node=module,
                    path=module_path,
                    lines=lines,
                    discovery_mechanism=discovery_mechanism,
                )

    return result.manager, ordered_modules

def _check_encoding(mypy_fscache: mypy.fscache.FileSystemCache, module_path: Path) -> None:
    module_rel_path = make_path_relative_to_cwd(module_path)
    module_loc = SourceLocation(file=module_path, line=1)
    try:
        source = mypy_fscache.read(str(module_path))
    except OSError:
        logger.warning(
            f"Couldn't read source for {module_rel_path}",
            location=module_loc,
        )
        return
    # below is based on mypy/util.py:decode_python_encoding
    # check for BOM UTF-8 encoding
    if source.startswith(b"\xef\xbb\xbf"):
        return
    # otherwise look at first two lines and check if PEP-263 coding is present
    encoding, _ = mypy.util.find_python_encoding(source)
    # find the codec for this encoding and check it is utf-8
    codec = codecs.lookup(encoding)
    if codec.name != "utf-8":
        logger.warning(
            "UH OH SPAGHETTI-O's,"
            " darn tootin' non-utf8(?!) encoded file encountered:"
            f" {module_rel_path} encoded as {encoding}",
            location=module_loc,
        )

def _mypy_build(
    sources: list[mypy.modulefinder.BuildSource],
    options: mypy.options.Options,
    fscache: mypy.fscache.FileSystemCache | None,
) -> mypy.build.BuildResult:
    """Simple wrapper around mypy.build.build

    Makes it so that check errors and parse errors are handled the same (ie with an exception)
    """

    all_messages = list[str]()

    def flush_errors(
        _filename: str | None,
        new_messages: list[str],
        _is_serious: bool,  # noqa: FBT001
    ) -> None:
        all_messages.extend(msg for msg in new_messages if os.devnull not in msg)

    try:
        result = mypy.build.build(
            sources=sources,
            options=options,
            flush_errors=flush_errors,
            fscache=fscache,
        )
    finally:
        _log_mypy_messages(all_messages)
    return result

def _log_mypy_message(message: log.Log | None, related_lines: list[str]) -> None:
    if not message:
        return
    logger.log(
        message.level, message.message, location=message.location, related_lines=related_lines
    )

def _log_mypy_messages(messages: list[str]) -> None:
    first_message: log.Log | None = None
    related_messages = list[str]()
    for message_str in messages:
        message = _parse_log_message(message_str)
        if not first_message:
            first_message = message
        elif not message.location:
            # collate related error messages and log together
            related_messages.append(message.message)
        else:
            _log_mypy_message(first_message, related_messages)
            related_messages = []
            first_message = message
    _log_mypy_message(first_message, related_messages)

_MYPY_LOG_MESSAGE = re.compile(
    r"""^
    (?P<filename>([A-Z]:\\)?[^:]*)(:(?P<line>\d+))?
    :\s(?P<severity>error|warning|note)
    :\s(?P<msg>.*)$""",
    re.VERBOSE,
)

def _parse_log_message(log_message: str) -> log.Log:
    match = _MYPY_LOG_MESSAGE.match(log_message)
    if match:
        matches = match.groupdict()
        return _try_parse_log_parts(
            matches.get("filename"),
            matches.get("line") or "",
            matches.get("severity") or "error",
            matches["msg"],
        )
    return log.Log(
        level=log.LogLevel.error,
        message=log_message,
        location=None,
    )

def _try_parse_log_parts(
    path_str: str | None, line_str: str, severity_str: str, msg: str
) -> log.Log:
    if not path_str:
        location = None
    else:
        try:
            line = int(line_str)
        except ValueError:
            line = 1
        location = SourceLocation(file=Path(path_str).resolve(), line=line)
    level = _MYPY_SEVERITY_TO_LOG_LEVEL[severity_str]
    return log.Log(message=msg, level=level, location=location)

def _join_single_new_line(doc: str) -> str:
    return doc.strip().replace("\n", " ")

def parse_docstring(docstring_raw: str | None) -> MethodDocumentation:
    if docstring_raw is None:
        return MethodDocumentation()
    docstring = docstring_parser.parse(docstring_raw)
    method_desc = "\n".join(
        _join_single_new_line(line)
        for lines in filter(None, (docstring.short_description, docstring.long_description))
        for line in lines.split("\n\n")
    )
    return MethodDocumentation(
        description=method_desc if method_desc else None,
        args={
            p.arg_name: _join_single_new_line(p.description)
            for p in docstring.params
            if p.description
        },
        returns=(
            _join_single_new_line(docstring.returns.description)
            if docstring.returns and docstring.returns.description
            else None
        ),
    )

def source_location_from_mypy(file: Path, node: mypy.nodes.Context) -> SourceLocation:
    assert node.line is not None
    assert node.line >= 1

    match node:
        case (
            mypy.nodes.FuncDef(body=body)
            | mypy.nodes.Decorator(func=mypy.nodes.FuncDef(body=body))
        ):
            # end_line of a function node includes the entire body
            # try to get just the signature
            end_line = node.line
            # no body means the end_line is ok to use
            if body is None:
                end_line = max(end_line, node.end_line or node.line)
            # if there is a body, attempt to use the first line before the body as the end
            else:
                end_line = max(end_line, body.line - 1)
            return SourceLocation(
                file=file,
                line=node.line,
                end_line=end_line,
            )
        case mypy.nodes.ClassDef(decorators=class_decorators, defs=class_body):
            line = node.line
            for dec in class_decorators:
                line = min(dec.line, line)
            end_line = max(line, class_body.line - 1)
            return SourceLocation(
                file=file,
                line=line,
                end_line=end_line,
            )
        case mypy.nodes.WhileStmt(body=compound_body) | mypy.nodes.ForStmt(body=compound_body):
            return SourceLocation(
                file=file,
                line=node.line,
                end_line=compound_body.line - 1,
            )
        case mypy.nodes.IfStmt(body=[*bodies], else_body=else_body):
            body_start: int | None = None
            if else_body is not None:
                bodies.append(else_body)
            for body in bodies:
                if body_start is None:
                    body_start = body.line
                else:
                    body_start = min(body_start, body.line)
            if body_start is None:
                # this shouldn't happen, there should be at least one body in one branch,
                # but this serves okay as a fallback
                end_line = node.end_line or node.line
            else:
                end_line = body_start - 1
            return SourceLocation(
                file=file,
                line=node.line,
                end_line=end_line,
            )
    return SourceLocation(
        file=file,
        line=node.line,
        end_line=(
            node.end_line if (node.end_line is not None and node.end_line >= 1) else node.line
        ),
        column=node.column if (node.column is not None and node.column >= 0) else 0,
        end_column=(
            node.end_column if (node.end_column is not None and node.end_column >= 0) else None
        ),
    )


import ast
import typing

from puya.utils import valid_bytes, valid_int64

def parse_template_key_value(key_value_str: str) -> tuple[str, int | bytes]:
    try:
        name, value_str = key_value_str.split("=", maxsplit=1)
    except ValueError:
        raise ValueError(f"invalid template var definition: {key_value_str!r}") from None

    if value_str.startswith("0x"):
        value: int | bytes = bytes.fromhex(value_str[2:])
    else:
        try:
            literal = ast.literal_eval(value_str)
        except ValueError:
            literal = None
        match literal:
            case str(str_value):
                value = str_value.encode("utf8")
            case bool(bool_value):
                value = int(bool_value)
            case int(value):
                pass
            case _:
                raise ValueError(f"invalid template var definition: {key_value_str!r}")
    match value:
        case int(int_result):
            too_big = not valid_int64(int_result)
        case bytes(bytes_result):
            too_big = not valid_bytes(bytes_result)
        case unexpected:
            typing.assert_never(unexpected)
    if too_big:
        raise ValueError(f"template value {name!r} too big: {value_str}")
    return name, value


from pathlib import Path

from puyapy.options import PuyaPyOptions

def determine_out_dir(contract_path: Path, options: PuyaPyOptions) -> Path:
    if not options.out_dir:
        out_dir = contract_path
    else:
        # find input path the contract is relative to
        for src_path in options.paths:
            src_path = src_path.resolve()
            src_path = src_path if src_path.is_dir() else src_path.parent
            try:
                relative_path = contract_path.relative_to(src_path)
            except ValueError:
                continue
            # construct a path that maintains a hierarchy to src_path
            out_dir = options.out_dir / relative_path
            if not options.out_dir.is_absolute():
                out_dir = src_path / out_dir
            break
        else:
            # if not relative to any input path
            if options.out_dir.is_absolute():
                out_dir = options.out_dir / contract_path
            else:
                out_dir = contract_path / options.out_dir

    out_dir.mkdir(parents=True, exist_ok=True)
    return out_dir


import typing

if not typing.TYPE_CHECKING:
    raise RuntimeError(
        "`import algopy` cannot be executed by the python interpreter. "
        "It is intended to be used by the puyapy compiler to compile "
        "Algorand smart contracts written in Algorand Python."
        "For more details see "
        "https://algorandfoundation.github.io/puya/index.html#compiler-usage"
    )


import typing

from algopy import (
    Account,
    Application,
    ARC4Contract,
    Asset,
    Bytes,
    OnCompleteAction,
    String,
    TransactionType,
    UInt64,
    arc4,
    gtxn,
    log,
    op,
)

Bytes3: typing.TypeAlias = arc4.StaticArray[arc4.Byte, typing.Literal[3]]

class Reference(ARC4Contract):
    def __init__(self) -> None:
        self.asa = Asset(123)
        self.an_int = UInt64(2)
        self.some_bytes = Bytes3(arc4.Byte(7), arc4.Byte(8), arc4.Byte(9))
        self.creator = op.Txn.sender
        self.app = Application(123)

        assert arc4.arc4_signature("get(uint64,byte[])byte[]"), "has method selector"

    @arc4.abimethod
    def noop_with_uint64(self, a: arc4.UInt64) -> arc4.UInt8:
        result = 1 + a.native
        return arc4.UInt8(result)

    @arc4.abimethod(
        allow_actions=[
            "NoOp",
            OnCompleteAction.OptIn,
            "CloseOut",
            OnCompleteAction.UpdateApplication,
            OnCompleteAction.DeleteApplication,
        ],
        name="all_the_things",
        create="allow",
        readonly=True,
    )
    def full_abi_config(self, a: arc4.UInt64) -> arc4.UInt8:
        result = UInt64(1) + a.native
        return arc4.UInt8(result)

    @arc4.abimethod(
        allow_actions=[
            "NoOp",
            "CloseOut",
            "DeleteApplication",
        ],
        create="disallow",
        readonly=True,
    )
    def mixed_oca(self, a: arc4.UInt64) -> arc4.UInt8:
        result = UInt64(1) + a.native
        return arc4.UInt8(result)

    @arc4.baremethod(
        allow_actions=[
            "NoOp",
            "OptIn",
            "CloseOut",
            "UpdateApplication",
            "DeleteApplication",
        ],
        create="require",
    )
    def bare_abi_config(self) -> None:
        log("Hello World")

    @arc4.abimethod
    def opt_into_asset(self, asset: Asset) -> None:
        # Only allow app creator to opt the app account into a ASA
        assert op.Txn.sender == op.Global.creator_address, "Only creator can opt in to ASA"
        # Verify a ASA hasn't already been opted into
        assert not self.asa, "ASA already opted in"
        # Save ASA ID in global state
        self.asa = asset

        # Submit opt-in transaction: 0 asset transfer to self
        op.ITxnCreate.begin()
        op.ITxnCreate.set_type_enum(TransactionType.AssetTransfer)
        op.ITxnCreate.set_fee(UInt64(0))  # cover fee with outer txn
        op.ITxnCreate.set_asset_receiver(op.Global.current_application_address)
        op.ITxnCreate.set_xfer_asset(asset)
        op.ITxnCreate.submit()

    @arc4.abimethod
    def with_transactions(
        self,
        asset: Asset,
        an_int: arc4.UInt64,
        pay: gtxn.PaymentTransaction,
        another_int: arc4.UInt64,
    ) -> None:
        assert self.asa == asset, "is correct asset"
        assert an_int.native == 1, "is correct int"
        assert pay.receiver == op.Global.current_application_address, "is payment to app"
        assert another_int.native == 2, "is correct int"

    @arc4.abimethod
    def compare_assets(self, asset_a: Asset, asset_b: Asset) -> None:
        assert asset_a == asset_b, "asset a == b"

    @arc4.abimethod(readonly=True)
    def get_address(self) -> arc4.Address:
        return arc4.Address()

    @arc4.abimethod(readonly=True)
    def get_asset(self) -> arc4.UInt64:
        return arc4.UInt64(456)

    @arc4.abimethod(readonly=True, name="get_application")
    def get_app(self) -> arc4.UInt64:
        return arc4.UInt64(456)

    @arc4.abimethod(readonly=True, name="get_an_int")
    def get_a_int(self) -> arc4.UInt64:
        return arc4.UInt64(3)

    @arc4.abimethod(
        default_args={
            "asset_from_storage": "asa",
            "asset_from_function": get_asset,
            "account_from_storage": "creator",
            "account_from_function": "get_address",
            "application_from_storage": "app",
            "application_from_function": get_app,
            "bytes_from_storage": "some_bytes",
            "int_from_storage": "an_int",
            "int_from_function": "get_a_int",
            "int_from_const": arc4.UInt32(123),
            "str_from_const": arc4.String("abc"),
        }
    )
    def method_with_default_args(
        self,
        asset_from_storage: Asset,
        asset_from_function: Asset,
        account_from_storage: Account,
        account_from_function: Account,
        application_from_storage: Application,
        application_from_function: Application,
        bytes_from_storage: Bytes3,
        int_from_storage: arc4.UInt64,
        int_from_function: arc4.UInt64,
        int_from_const: arc4.UInt32,
        str_from_const: arc4.String,
    ) -> None:
        assert asset_from_storage == Asset(123), "wrong asset from storage"
        assert asset_from_function == Asset(456), "wrong asset from function"
        assert account_from_storage == op.Global.creator_address, "wrong account from storage"
        assert account_from_function == op.Global.zero_address, "wrong account from function"
        assert application_from_storage == Application(123), "wrong application from storage"
        assert application_from_function == Application(456), "wrong application from function"
        assert bytes_from_storage[0] == arc4.Byte(7), "wrong 0th byte from storage"
        assert bytes_from_storage[1] == arc4.Byte(8), "wrong 1st byte from storage"
        assert bytes_from_storage[2] == arc4.Byte(9), "wrong 2nd byte from storage"
        assert int_from_storage.native == 2, "wrong int from storage"
        assert int_from_function.native == 3, "wrong int from function"
        assert int_from_const == 123
        assert str_from_const == "abc"

    @arc4.abimethod
    def method_with_15_args(
        self,
        one: UInt64,
        two: UInt64,
        three: UInt64,
        four: UInt64,
        five: UInt64,
        six: UInt64,
        seven: UInt64,
        eight: UInt64,
        nine: UInt64,
        ten: UInt64,
        eleven: UInt64,
        twelve: UInt64,
        thirteen: UInt64,
        fourteen: UInt64,
        fifteen: Bytes,
    ) -> Bytes:
        """Fifteen args should not encode the last argument as a tuple"""
        assert (
            one
            + two
            + three
            + four
            + five
            + six
            + seven
            + eight
            + nine
            + ten
            + eleven
            + twelve
            + thirteen
            + fourteen
        )
        return fifteen

    @arc4.abimethod
    def method_with_more_than_15_args(
        self,
        a: arc4.UInt64,
        b: arc4.UInt64,
        c: arc4.UInt64,
        d: UInt64,
        asset: Asset,
        e: arc4.UInt64,
        f: arc4.UInt64,
        pay: gtxn.PaymentTransaction,
        g: arc4.UInt64,
        h: arc4.UInt64,
        i: arc4.UInt64,
        j: arc4.UInt64,
        k: arc4.UInt64,
        # ruff: noqa: E741
        l: arc4.UInt64,
        m: arc4.UInt64,
        n: arc4.UInt64,
        o: arc4.UInt64,
        p: UInt64,
        q: arc4.UInt64,
        r: arc4.UInt64,
        s: Bytes,
        t: Bytes,
        asset2: Asset,
        pay2: gtxn.PaymentTransaction,
        u: arc4.UInt64,
        v: arc4.UInt64,
    ) -> arc4.UInt64:
        """
        Application calls only support 16 args, and arc4 calls utilise the first arg for the method
        selector. Args beyond this number are packed into a tuple and placed in the 16th slot.
        """
        assert op.Txn.num_app_args == 16
        assert pay.amount == 100000
        assert pay2.amount == 200000
        assert asset.id
        assert asset2.id

        log(s + t)

        return arc4.UInt64(
            a.native
            + b.native
            + c.native
            + d
            + e.native
            + f.native
            + g.native
            + h.native
            + i.native
            + j.native
            + k.native
            + l.native
            + m.native
            + n.native
            + o.native
            + p
            + q.native
            + r.native
            + u.native
            + v.native
        )

    @arc4.abimethod
    def hello_with_algopy_string(self, name: String) -> String:
        return "Hello " + name + "!"


import typing

from algopy import ARC4Contract, Txn, UInt64, arc4, log, op

class CustomApproval(ARC4Contract):
    def __init__(self) -> None:
        super().__init__()
        assert Txn.application_id == 0, "nonsense assert just to generate an init method"

    @typing.override
    def approval_program(self) -> bool:
        if Txn.num_app_args == 2 and Txn.application_args(1) == op.itob(42):
            log("🎉🎉🎉")
        result = super().approval_program()
        if not result:
            log(
                "this will never be seen unless you're running in simulation mode anyway"
                " so I can say whatever I want here"
            )
        return result

    @arc4.abimethod
    def add_one(self, x: UInt64) -> UInt64:
        return x + 1

class CustomApproval2(CustomApproval):
    @typing.override
    def approval_program(self) -> bool:
        return False

class CustomApproval3(ARC4Contract):
    @arc4.abimethod
    def unused(self) -> None:
        return

    @typing.override
    def approval_program(self) -> bool:
        return False


from algopy import ARC4Contract, UInt64

class MinimumARC4(ARC4Contract):
    def __init__(self) -> None:
        self.gvalue = UInt64(4)


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class CustomApproval(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def add_one(
        self,
        x: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class CustomApproval2(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def add_one(
        self,
        x: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class CustomApproval3(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def unused(
        self,
    ) -> None: ...


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class MinimumARC4(algopy.arc4.ARC4Client, typing.Protocol):
    pass


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class Reference(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def noop_with_uint64(
        self,
        a: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[8]]: ...

    @algopy.arc4.abimethod(readonly=True, allow_actions=['NoOp', 'OptIn', 'CloseOut', 'UpdateApplication', 'DeleteApplication'], create='allow')
    def all_the_things(
        self,
        a: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[8]]: ...

    @algopy.arc4.abimethod(readonly=True, allow_actions=['NoOp', 'CloseOut', 'DeleteApplication'])
    def mixed_oca(
        self,
        a: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[8]]: ...

    @algopy.arc4.abimethod
    def opt_into_asset(
        self,
        asset: algopy.Asset,
    ) -> None: ...

    @algopy.arc4.abimethod
    def with_transactions(
        self,
        asset: algopy.Asset,
        an_int: algopy.arc4.UIntN[typing.Literal[64]],
        pay: algopy.gtxn.PaymentTransaction,
        another_int: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> None: ...

    @algopy.arc4.abimethod
    def compare_assets(
        self,
        asset_a: algopy.Asset,
        asset_b: algopy.Asset,
    ) -> None: ...

    @algopy.arc4.abimethod(readonly=True)
    def get_address(
        self,
    ) -> algopy.arc4.Address: ...

    @algopy.arc4.abimethod(readonly=True)
    def get_asset(
        self,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod(readonly=True)
    def get_application(
        self,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod(readonly=True)
    def get_an_int(
        self,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod
    def method_with_default_args(
        self,
        asset_from_storage: algopy.Asset,
        asset_from_function: algopy.Asset,
        account_from_storage: algopy.Account,
        account_from_function: algopy.Account,
        application_from_storage: algopy.Application,
        application_from_function: algopy.Application,
        bytes_from_storage: algopy.arc4.StaticArray[algopy.arc4.Byte, typing.Literal[3]],
        int_from_storage: algopy.arc4.UIntN[typing.Literal[64]],
        int_from_function: algopy.arc4.UIntN[typing.Literal[64]],
        int_from_const: algopy.arc4.UIntN[typing.Literal[32]],
        str_from_const: algopy.arc4.String,
    ) -> None: ...

    @algopy.arc4.abimethod
    def method_with_15_args(
        self,
        one: algopy.arc4.UIntN[typing.Literal[64]],
        two: algopy.arc4.UIntN[typing.Literal[64]],
        three: algopy.arc4.UIntN[typing.Literal[64]],
        four: algopy.arc4.UIntN[typing.Literal[64]],
        five: algopy.arc4.UIntN[typing.Literal[64]],
        six: algopy.arc4.UIntN[typing.Literal[64]],
        seven: algopy.arc4.UIntN[typing.Literal[64]],
        eight: algopy.arc4.UIntN[typing.Literal[64]],
        nine: algopy.arc4.UIntN[typing.Literal[64]],
        ten: algopy.arc4.UIntN[typing.Literal[64]],
        eleven: algopy.arc4.UIntN[typing.Literal[64]],
        twelve: algopy.arc4.UIntN[typing.Literal[64]],
        thirteen: algopy.arc4.UIntN[typing.Literal[64]],
        fourteen: algopy.arc4.UIntN[typing.Literal[64]],
        fifteen: algopy.arc4.DynamicBytes,
    ) -> algopy.arc4.DynamicBytes:
        """
        Fifteen args should not encode the last argument as a tuple
        """

    @algopy.arc4.abimethod
    def method_with_more_than_15_args(
        self,
        a: algopy.arc4.UIntN[typing.Literal[64]],
        b: algopy.arc4.UIntN[typing.Literal[64]],
        c: algopy.arc4.UIntN[typing.Literal[64]],
        d: algopy.arc4.UIntN[typing.Literal[64]],
        asset: algopy.Asset,
        e: algopy.arc4.UIntN[typing.Literal[64]],
        f: algopy.arc4.UIntN[typing.Literal[64]],
        pay: algopy.gtxn.PaymentTransaction,
        g: algopy.arc4.UIntN[typing.Literal[64]],
        h: algopy.arc4.UIntN[typing.Literal[64]],
        i: algopy.arc4.UIntN[typing.Literal[64]],
        j: algopy.arc4.UIntN[typing.Literal[64]],
        k: algopy.arc4.UIntN[typing.Literal[64]],
        l: algopy.arc4.UIntN[typing.Literal[64]],
        m: algopy.arc4.UIntN[typing.Literal[64]],
        n: algopy.arc4.UIntN[typing.Literal[64]],
        o: algopy.arc4.UIntN[typing.Literal[64]],
        p: algopy.arc4.UIntN[typing.Literal[64]],
        q: algopy.arc4.UIntN[typing.Literal[64]],
        r: algopy.arc4.UIntN[typing.Literal[64]],
        s: algopy.arc4.DynamicBytes,
        t: algopy.arc4.DynamicBytes,
        asset2: algopy.Asset,
        pay2: algopy.gtxn.PaymentTransaction,
        u: algopy.arc4.UIntN[typing.Literal[64]],
        v: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[64]]:
        """
        Application calls only support 16 args, and arc4 calls utilise the first arg for the method
        selector. Args beyond this number are packed into a tuple and placed in the 16th slot.
        """

    @algopy.arc4.abimethod
    def hello_with_algopy_string(
        self,
        name: algopy.arc4.String,
    ) -> algopy.arc4.String: ...


from algopy import (
    Application,
    Bytes,
    Contract,
    LocalState,
    Txn,
    UInt64,
    op,
    subroutine,
)

class Reference(Contract):
    def __init__(self) -> None:
        self.int_1 = UInt64(0)
        self.bytes_1 = Bytes(b"")
        self.bytes_2 = Bytes(b"")
        self.int_l1 = LocalState(UInt64)
        self.int_l2 = LocalState(UInt64)
        self.int_l3 = LocalState(UInt64)
        self.bytes_l1 = LocalState(Bytes)
        self.bytes_l2 = LocalState(Bytes)
        self.bytes_l3 = LocalState(Bytes)
        self.bytes_l4 = LocalState(Bytes)

    def approval_program(self) -> bool:
        if op.Txn.num_app_args == 1:
            if op.Txn.application_args(0) == b"validate":
                self.validate_asset(op.Global.current_application_id)
            else:
                assert False, "Expected validate"
        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def validate_asset(self, app: Application) -> None:
        assert not Txn.sender.is_opted_in(app), "app opted in"
        assert app.creator == op.Global.creator_address, "expected creator"
        assert app.global_num_uint == 1, "expected global_num_uint"
        assert app.global_num_bytes == 2, "expected global_num_bytes"
        assert app.local_num_uint == 3, "expected local_num_uint"
        assert app.local_num_bytes == 4, "expected local_num_bytes"
        assert app.approval_program, "expected approval_program"
        assert app.clear_state_program, "expected clear_state_program"
        assert app == op.Global.current_application_id, "expected current_application_id"
        assert (
            app.address == op.Global.current_application_address
        ), "expected current_application_address"


import typing

from algopy import ARC4Contract, arc4, log, subroutine

class StaticStruct(arc4.Struct):
    a: arc4.UInt64
    b: arc4.StaticArray[arc4.Byte, typing.Literal[2]]

class DynamicStruct(arc4.Struct):
    a: arc4.String
    b: arc4.String

class MixedSingleStruct(arc4.Struct):
    a: arc4.UInt64
    b: arc4.String
    c: arc4.UInt64

class MixedMultipleStruct(arc4.Struct):
    a: arc4.UInt64
    b: arc4.String
    c: arc4.UInt64
    d: arc4.DynamicArray[arc4.UInt16]
    e: arc4.UInt64

class NestedDynamicStruct(arc4.Struct):
    one: StaticStruct
    two: DynamicStruct
    three: StaticStruct
    four: MixedMultipleStruct
    five: DynamicStruct

class DynamicArrayContract(ARC4Contract):
    @arc4.abimethod()
    def test_static_elements(self) -> None:
        byte_array1 = arc4.StaticArray(get_byte1(), get_byte2())
        byte_array2 = arc4.StaticArray(get_byte3(), get_byte4())

        struct1 = StaticStruct(get_uint1(), byte_array1.copy())
        struct2 = StaticStruct(get_uint2(), byte_array2.copy())
        array = arc4.DynamicArray(struct1.copy(), struct1.copy())
        array[1] = struct2.copy()
        log(array)
        log(array[0])
        log(array[1])

        assert array.pop() == struct2
        assert array.pop() == struct1

    @arc4.abimethod()
    def test_dynamic_elements(self) -> None:
        struct1 = DynamicStruct(get_string1(), get_string2())
        struct2 = DynamicStruct(get_string3(), get_string1())
        array = arc4.DynamicArray(struct1.copy(), struct1.copy())
        array.append(struct1.copy())
        array[1] = struct2.copy()  # replace
        log(array)
        log(array[0])
        log(array[1])
        log(array[2])

        assert array.pop() == struct1
        log(array)
        assert array.pop() == struct2
        log(array)
        assert array.pop() == struct1
        log(array)

    @arc4.abimethod()
    def test_mixed_single_dynamic_elements(self) -> None:
        struct1 = MixedSingleStruct(get_uint1(), get_string1(), get_uint2())
        struct2 = MixedSingleStruct(get_uint2(), get_string2(), get_uint1())
        array = arc4.DynamicArray[MixedSingleStruct]()
        array.append(struct2.copy())
        array.append(struct2.copy())
        array[0] = struct1.copy()  # replace
        log(array)
        log(array[0])
        log(array[1])

        assert array.pop() == struct2
        assert array.pop() == struct1

    @arc4.abimethod()
    def test_mixed_multiple_dynamic_elements(self) -> None:
        struct1 = MixedMultipleStruct(
            get_uint1(), get_string1(), get_uint2(), get_u16_arr1(), get_uint1()
        )
        struct2 = MixedMultipleStruct(
            get_uint2(), get_string2(), get_uint1(), get_u16_arr2(), get_uint2()
        )
        array = arc4.DynamicArray(struct1.copy(), struct1.copy())
        array[1] = struct2.copy()
        log(array)
        log(array[0])
        log(array[1])

        assert array.pop() == struct2
        assert array.pop() == struct1

    @arc4.abimethod()
    def test_nested_struct_replacement(self) -> None:
        one = StaticStruct(get_uint1(), arc4.StaticArray(get_byte1(), get_byte2()))
        two = DynamicStruct(get_string1(), get_string2())
        three = StaticStruct(get_uint2(), arc4.StaticArray(get_byte2(), get_byte1()))
        four = MixedMultipleStruct(
            get_uint1(), get_string1(), get_uint2(), get_u16_arr1(), get_uint1()
        )
        five = DynamicStruct(get_string1(), get_string2())
        struct1 = NestedDynamicStruct(
            one=one.copy(),
            two=two.copy(),
            three=three.copy(),
            four=four.copy(),
            five=five.copy(),
        )
        struct2 = NestedDynamicStruct(
            one=one.copy(),
            two=DynamicStruct(get_string2(), get_string1()),  # this is the difference with struct1
            three=three.copy(),
            four=four.copy(),
            five=five.copy(),
        )

        struct2.two = two.copy()  # now struct2 should match struct1
        assert struct1.bytes == struct2.bytes, "struct1 does not match struct2"

    @arc4.abimethod()
    def test_nested_tuple_modification(self) -> None:
        one = StaticStruct(get_uint1(), arc4.StaticArray(get_byte1(), get_byte2()))
        two = DynamicStruct(get_string1(), get_string2())
        three = StaticStruct(get_uint2(), arc4.StaticArray(get_byte2(), get_byte1()))
        four1 = MixedMultipleStruct(
            get_uint1(), get_string1(), get_uint2(), get_u16_arr1(), get_uint1()
        )
        four2 = MixedMultipleStruct(
            get_uint1(),
            get_string1(),
            get_uint2(),
            get_u16_arr1() + (arc4.UInt16(123),),  # noqa: RUF005
            get_uint1(),
        )
        five = DynamicStruct(get_string1(), get_string2())
        tup1 = arc4.Tuple(
            (
                one.copy(),
                two.copy(),
                three.copy(),
                four1.copy(),
                five.copy(),
            )
        )
        tup2 = arc4.Tuple(
            (
                one.copy(),
                two.copy(),
                three.copy(),
                four2.copy(),
                five.copy(),
            )
        )

        tup2[3].d.pop()
        assert tup1.bytes == tup2.bytes, "tup1 does not match tup2"

@subroutine
def get_string1() -> arc4.String:
    return arc4.String("a")

@subroutine
def get_string2() -> arc4.String:
    return arc4.String("bee")

@subroutine
def get_string3() -> arc4.String:
    return arc4.String("Hello World")

@subroutine
def get_uint1() -> arc4.UInt64:
    return arc4.UInt64(3)

@subroutine
def get_uint2() -> arc4.UInt64:
    return arc4.UInt64(2**42)

@subroutine
def get_byte1() -> arc4.Byte:
    return arc4.Byte(4)

@subroutine
def get_byte2() -> arc4.Byte:
    return arc4.Byte(5)

@subroutine
def get_byte3() -> arc4.Byte:
    return arc4.Byte(42)

@subroutine
def get_byte4() -> arc4.Byte:
    return arc4.Byte(255)

@subroutine
def get_u16_arr1() -> arc4.DynamicArray[arc4.UInt16]:
    return arc4.DynamicArray(arc4.UInt16(2**16 - 1), arc4.UInt16(0), arc4.UInt16(42))

@subroutine
def get_u16_arr2() -> arc4.DynamicArray[arc4.UInt16]:
    return arc4.DynamicArray(arc4.UInt16(1), arc4.UInt16(2), arc4.UInt16(3), arc4.UInt16(4))


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class DynamicArrayContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def test_static_elements(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_dynamic_elements(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_mixed_single_dynamic_elements(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_mixed_multiple_dynamic_elements(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_nested_struct_replacement(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_nested_tuple_modification(
        self,
    ) -> None: ...


# ruff: noqa: SIM201, SIM202, SIM300, PLR0124
import typing as t

from algopy import BigUInt, Contract, UInt64, arc4, subroutine

class UIntNOrdering(Contract):
    def approval_program(self) -> bool:
        check_both_uint_n(arc4.Byte(1), arc4.UInt64(2))
        check_mixed(arc4.Byte(1), arc4.BigUIntN[t.Literal[264]](2))
        check_both_big_uint_n(arc4.UInt256(1), arc4.BigUIntN[t.Literal[264]](2))
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def check_both_uint_n(one: arc4.Byte, two: arc4.UInt64) -> None:
    one_uint64 = UInt64(1)
    one_biguint = BigUInt(1)

    two_uint64 = UInt64(2)
    two_biguint = BigUInt(2)

    assert one == 1
    assert 1 == one
    assert one == one
    assert one == one_uint64
    assert one == one_biguint
    assert not (one == 2)
    assert not (one == two)
    assert not (one == two_uint64)
    assert not (one == two_biguint)

    assert not (one != 1)
    assert not (1 != one)
    assert not (one != one)
    assert not (one != one_uint64)
    assert not (one != one_biguint)
    assert one != 2
    assert one != two
    assert one != two_uint64
    assert one != two_biguint

    assert one <= 1
    assert 1 <= one
    assert one <= one
    assert one <= one_uint64
    assert one <= one_biguint
    assert one <= 2
    assert one <= two
    assert one <= two_uint64
    assert one <= two_biguint

    assert not (one < 1)
    assert not (1 < one)
    assert not (one < one)
    assert not (one < one_uint64)
    assert not (one < one_biguint)
    assert one < 2
    assert one < two
    assert one < two_uint64
    assert one < two_biguint

    assert one >= 1
    assert 1 >= one
    assert one >= one
    assert one >= one_uint64
    assert one >= one_biguint
    assert not (one >= 2)
    assert not (one >= two)
    assert not (one >= two_uint64)
    assert not (one >= two_biguint)

    assert not (one > 1)
    assert not (1 > one)
    assert not (one > one)
    assert not (one > one_uint64)
    assert not (one > one_biguint)
    assert not (one > 2)
    assert not (one > two)
    assert not (one > two_uint64)
    assert not (one > two_biguint)

@subroutine
def check_mixed(one: arc4.Byte, two: arc4.BigUIntN[t.Literal[264]]) -> None:
    one_uint64 = UInt64(1)
    one_biguint = BigUInt(1)

    two_uint64 = UInt64(2)
    two_biguint = BigUInt(2)

    assert one == 1
    assert 1 == one
    assert one == one
    assert one == one_uint64
    assert one == one_biguint
    assert not (one == 2)
    assert not (one == two)
    assert not (one == two_uint64)
    assert not (one == two_biguint)

    assert not (one != 1)
    assert not (1 != one)
    assert not (one != one)
    assert not (one != one_uint64)
    assert not (one != one_biguint)
    assert one != 2
    assert one != two
    assert one != two_uint64
    assert one != two_biguint

    assert one <= 1
    assert 1 <= one
    assert one <= one
    assert one <= one_uint64
    assert one <= one_biguint
    assert one <= 2
    assert one <= two
    assert one <= two_uint64
    assert one <= two_biguint

    assert not (one < 1)
    assert not (1 < one)
    assert not (one < one)
    assert not (one < one_uint64)
    assert not (one < one_biguint)
    assert one < 2
    assert one < two
    assert one < two_uint64
    assert one < two_biguint

    assert one >= 1
    assert 1 >= one
    assert one >= one
    assert one >= one_uint64
    assert one >= one_biguint
    assert not (one >= 2)
    assert not (one >= two)
    assert not (one >= two_uint64)
    assert not (one >= two_biguint)

    assert not (one > 1)
    assert not (1 > one)
    assert not (one > one)
    assert not (one > one_uint64)
    assert not (one > one_biguint)
    assert not (one > 2)
    assert not (one > two)
    assert not (one > two_uint64)
    assert not (one > two_biguint)

@subroutine
def check_both_big_uint_n(one: arc4.UInt256, two: arc4.BigUIntN[t.Literal[264]]) -> None:
    one_uint64 = UInt64(1)
    one_biguint = BigUInt(1)

    two_uint64 = UInt64(2)
    two_biguint = BigUInt(2)

    assert one == 1
    assert 1 == one
    assert one == one
    assert one == one_uint64
    assert one == one_biguint
    assert not (one == 2)
    assert not (one == two)
    assert not (one == two_uint64)
    assert not (one == two_biguint)

    assert not (one != 1)
    assert not (1 != one)
    assert not (one != one)
    assert not (one != one_uint64)
    assert not (one != one_biguint)
    assert one != 2
    assert one != two
    assert one != two_uint64
    assert one != two_biguint

    assert one <= 1
    assert 1 <= one
    assert one <= one
    assert one <= one_uint64
    assert one <= one_biguint
    assert one <= 2
    assert one <= two
    assert one <= two_uint64
    assert one <= two_biguint

    assert not (one < 1)
    assert not (1 < one)
    assert not (one < one)
    assert not (one < one_uint64)
    assert not (one < one_biguint)
    assert one < 2
    assert one < two
    assert one < two_uint64
    assert one < two_biguint

    assert one >= 1
    assert 1 >= one
    assert one >= one
    assert one >= one_uint64
    assert one >= one_biguint
    assert not (one >= 2)
    assert not (one >= two)
    assert not (one >= two_uint64)
    assert not (one >= two_biguint)

    assert not (one > 1)
    assert not (1 > one)
    assert not (one > one)
    assert not (one > one_uint64)
    assert not (one > one_biguint)
    assert not (one > 2)
    assert not (one > two)
    assert not (one > two_uint64)
    assert not (one > two_biguint)


from algopy import Contract, Global, Txn, arc4

SOME_ADDRESS = "VCMJKWOY5P5P7SKMZFFOCEROPJCZOTIJMNIYNUCKH7LRO45JMJP6UYBIJA"

class Arc4AddressContract(Contract):
    def approval_program(self) -> bool:
        address = arc4.Address(Txn.sender)
        assert address == Txn.sender
        assert address.length == 32
        assert address.native == Txn.sender

        zero_address = arc4.Address(Global.zero_address.bytes)
        assert zero_address.bytes == Global.zero_address.bytes

        some_address = arc4.Address(SOME_ADDRESS)
        assert some_address == SOME_ADDRESS

        address_copy = some_address

        assert some_address == address_copy
        return True

    def clear_state_program(self) -> bool:
        return True


import typing as t

from algopy import Bytes, Contract, String, UInt64, op, subroutine, uenumerate
from algopy.arc4 import (
    Byte,
    DynamicArray,
    StaticArray,
    String as ARC4String,
    UFixedNxM,
    UInt8,
    UInt16,
    UInt32,
)

AliasedDynamicArray: t.TypeAlias = DynamicArray[UInt16]
AliasedStaticArray: t.TypeAlias = StaticArray[UInt8, t.Literal[1]]

Decimal: t.TypeAlias = UFixedNxM[t.Literal[64], t.Literal[10]]

HashResult: t.TypeAlias = StaticArray[Byte, t.Literal[32]]

class Arc4ArraysContract(Contract):
    def approval_program(self) -> bool:
        dynamic_uint8_array = DynamicArray[UInt8](UInt8(1), UInt8(2))
        total = UInt64(0)
        for uint8_item in dynamic_uint8_array:
            total += uint8_item.native

        assert total == 3, "Total should be sum of dynamic_uint8_array items"
        aliased_dynamic = AliasedDynamicArray(UInt16(1))
        for uint16_item in aliased_dynamic:
            total += uint16_item.native
        assert total == 4, "Total should now include sum of aliased_dynamic items"
        dynamic_string_array = DynamicArray[ARC4String](ARC4String("Hello"), ARC4String("World"))
        assert dynamic_string_array.length == 2
        assert dynamic_string_array[0] == ARC4String("Hello")
        result = String("")
        for index, string_item in uenumerate(dynamic_string_array):
            if index == 0:
                result = string_item.native
            else:
                result += " " + string_item.native

        assert result == "Hello World"

        static_uint32_array = StaticArray(UInt32(1), UInt32(10), UInt32(255), UInt32(128))

        for uint32_item in static_uint32_array:
            total += uint32_item.native

        assert total == 4 + 1 + 10 + 255 + 128

        aliased_static = AliasedStaticArray(UInt8(101))

        index = UInt64(0)

        assert (aliased_static[0].native + aliased_static[index].native) == 202

        static_string_array = StaticArray(ARC4String("Ping"), ARC4String("Pong"))

        result = String("")
        for index, string_item in uenumerate(static_string_array):
            if index == 0:
                result = string_item.native
            else:
                result += " " + string_item.native

        assert result == "Ping Pong"

        self.hash_as_array(Bytes(b"Testing 123"))

        return True

    @subroutine
    def hash_as_array(self, commitment_args_concat: Bytes) -> HashResult:
        return HashResult.from_bytes(op.sha3_256(commitment_args_concat))

    def clear_state_program(self) -> bool:
        return True


import typing as t

from algopy import Bytes, Contract, Txn, subroutine
from algopy.arc4 import (
    Bool as ARC4Bool,
    DynamicArray,
    StaticArray,
)

class Arc4BoolTypeContract(Contract):
    def approval_program(self) -> bool:
        self.test_stuff(ARC4Bool(True), ARC4Bool(False))
        assert ARC4Bool(
            False if Txn.num_app_args else True  # noqa: SIM211
        ).native, "conditional expr"

        static_boolean_array = StaticArray[ARC4Bool, t.Literal[12]](
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
            ARC4Bool(True),
        )

        assert static_boolean_array.bytes == Bytes.from_hex("FFF0")

        assert static_boolean_array[0] == ARC4Bool(True), "Single boolean can be unpacked"
        assert static_boolean_array[-1] == ARC4Bool(True), "Single boolean can be unpacked"

        dynamic_boolean_array = DynamicArray[ARC4Bool](
            ARC4Bool(True), ARC4Bool(False), ARC4Bool(True)
        )

        assert dynamic_boolean_array.bytes == Bytes.from_hex("0003A0")

        assert ARC4Bool(True) == True  # noqa: E712
        assert ARC4Bool(False) != True  # noqa: E712
        assert False == ARC4Bool(False)  # noqa: E712, SIM300
        assert False != ARC4Bool(True)  # noqa: E712, SIM300

        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def test_stuff(self, true: ARC4Bool, false: ARC4Bool) -> bool:
        assert true.native

        assert not false.native

        assert true == ARC4Bool(true.native)
        assert false == ARC4Bool(false.native)

        return true.native


import typing as t

from algopy import Contract, arc4
from algopy.op import Txn

class Arc4BoolEvalContract(Contract):
    def approval_program(self) -> bool:
        assert not arc4.Bool(False)
        assert arc4.Bool(True)

        assert not arc4.String("")
        assert arc4.String(".")

        assert not arc4.Address()
        assert arc4.Address(Txn.sender)

        assert not arc4.UInt8(0)
        assert arc4.UInt8(1)

        assert not arc4.UInt16(0)
        assert arc4.UInt16(1)

        assert not arc4.UInt32(0)
        assert arc4.UInt32(1)

        assert not arc4.UInt64(0)
        assert arc4.UInt64(1)

        assert not arc4.UInt128(0)
        assert arc4.UInt128(1)

        assert not arc4.UInt256(0)
        assert arc4.UInt256(1)

        assert not arc4.UInt512(0)
        assert arc4.UInt512(1)

        assert not arc4.UIntN[t.Literal[24]](0)
        assert arc4.UIntN[t.Literal[24]](1)

        assert not arc4.BigUIntN[t.Literal[504]](0)
        assert arc4.BigUIntN[t.Literal[504]](1)

        assert not arc4.UFixedNxM[t.Literal[48], t.Literal[10]]("0.0")
        assert arc4.UFixedNxM[t.Literal[48], t.Literal[10]]("1.0")

        assert not arc4.BigUFixedNxM[t.Literal[496], t.Literal[10]]("0.0")
        assert arc4.BigUFixedNxM[t.Literal[496], t.Literal[10]]("0.01")

        dynamic_arr = arc4.DynamicArray[arc4.UInt64]()
        assert not dynamic_arr
        dynamic_arr.append(arc4.UInt64(0))
        assert dynamic_arr

        assert arc4.Bool() == arc4.Bool(False)

        return True

    def clear_state_program(self) -> bool:
        return True


from algopy import Bytes, Contract, Txn, UInt64
from algopy.arc4 import (
    Byte,
    DynamicBytes,
    UInt8,
)

class Arc4DynamicBytesContract(Contract):
    def approval_program(self) -> bool:
        total = UInt64(0)
        dynamic_bytes = DynamicBytes(Byte(2), UInt8(3), 1)
        assert dynamic_bytes.native == b"\x02\x03\x01"
        assert dynamic_bytes.bytes == b"\x00\x03\x02\x03\x01"

        for uint8_item in dynamic_bytes:
            total += uint8_item.native

        assert total == 6, "Total should be of dynamic_bytes items"

        dynamic_bytes2 = DynamicBytes(b"\x03\x04")
        assert dynamic_bytes2.native == b"\x03\x04"
        assert dynamic_bytes2.bytes == b"\x00\x02\x03\x04"

        for uint8_item in dynamic_bytes2:
            total += uint8_item.native

        dynamic_bytes3 = DynamicBytes(dynamic_bytes2.native)
        assert dynamic_bytes3.native == b"\x03\x04"
        assert dynamic_bytes3.bytes == b"\x00\x02\x03\x04"

        for uint8_item in dynamic_bytes3:
            total += uint8_item.native

        assert total == 20, "Total should now include sum of dynamic_bytes3 items"

        dynamic_bytes3.extend(DynamicBytes(b"abc"))
        assert dynamic_bytes3.bytes == b"\x00\x05\x03\x04abc"
        assert dynamic_bytes3.native == b"\x03\x04abc"

        dynamic_bytes = DynamicBytes(2 if Txn.num_app_args else 3, UInt8(3), 1)
        assert dynamic_bytes.native == Bytes.from_hex("030301")

        dynamic_bytes = DynamicBytes(b"2" if Txn.num_app_args else b"3")
        assert dynamic_bytes.native == b"3"
        return True

    def clear_state_program(self) -> bool:
        return True


from algopy import ARC4Contract, arc4

class Arc4DynamicStringArrayContract(ARC4Contract):
    @arc4.abimethod
    def xyz(self) -> arc4.DynamicArray[arc4.String]:
        return arc4.DynamicArray(
            arc4.String("X"),
            arc4.String("Y"),
            arc4.String("Z"),
        )

    @arc4.abimethod
    def xyz_raw(self) -> arc4.DynamicArray[arc4.String]:
        raw = arc4.DynamicArray(
            arc4.DynamicArray(arc4.Byte(88)),
            arc4.DynamicArray(arc4.Byte(89)),
            arc4.DynamicArray(arc4.Byte(90)),
        )
        return arc4.DynamicArray[arc4.String].from_bytes(raw.bytes)


import typing

from algopy import Contract, UInt64, subroutine
from algopy.arc4 import (
    Bool,
    StaticArray,
    String,
    Struct,
    UInt8,
)

class TestStruct(Struct):
    b_val: Bool
    u_val: UInt8
    s_val_1: String
    s_val_2: String

TestArray: typing.TypeAlias = StaticArray[UInt8, typing.Literal[4]]

class StructWithArray(Struct):
    test_array: TestArray

class Arc4MutableParamsContract(Contract):
    def approval_program(self) -> bool:
        self.mutating_copies()

        return True

    @subroutine
    def mutating_copies(self) -> None:
        my_array = StaticArray(UInt8(1), UInt8(2), UInt8(3), UInt8(4))
        my_struct = TestStruct(
            b_val=Bool(True),
            u_val=UInt8(50),
            s_val_1=String("Happy"),
            s_val_2=String("Days"),
        )

        my_array_copy = my_array.copy()
        my_struct_copy = my_struct.copy()

        my_array[2] = UInt8(5)

        assert my_array_copy[2] == UInt8(3), "my_array_copy should be unchanged"
        assert my_array[2] == UInt8(5), "my_array should be mutated"

        # Pass to subroutine without a copy
        t, f = self.other_routine(my_array, my_struct)
        assert t
        assert not f

        assert my_array[1] == UInt8(5), "my_array has been mutated by the subroutine"

        assert my_struct.s_val_1 == String(
            "AARRGH!"
        ), "my_struct has been mutated by the subroutine"

        # Pass to subroutine with copy
        self.other_routine(my_array_copy.copy(), my_struct_copy.copy())

        assert my_array_copy[1] == UInt8(
            2
        ), "my_array_copy should not be mutated by the subroutine"

        assert my_struct_copy.s_val_1 == String(
            "Happy"
        ), "my_struct_copy should not be mutated by the subroutine"

        my_array_copy_2 = my_array_copy.copy()

        my_array_copy_2 = self.other_routine_2(my_array_copy_2)

        assert my_array_copy_2[0] == UInt8(1), "my_array_copy_2 should have original value"

        self.other_routine_2(my_array_copy_2)
        assert my_array_copy_2[0] == UInt8(10), "my_array_copy_2 should have mutated value"

        my_array_copy_3 = my_array_copy.copy()

        originals = (my_array.copy(), my_array_copy_2.copy(), my_array_copy_3.copy())
        self.mutate_tuple_items_and_reassign(
            (my_array.copy(), my_array_copy_2.copy(), my_array_copy_3.copy()),
            start=UInt64(0),
            reassign=True,
        )
        assert originals == (my_array, my_array_copy_2, my_array_copy_3)

        self.mutate_tuple_items_and_reassign(
            (my_array, my_array_copy_2, my_array_copy_3), start=UInt64(100), reassign=True
        )

        assert my_array[0] == 100
        assert my_array_copy_2[0] == 101
        assert my_array_copy_3[0] == 102
        assert my_array[1] == 103
        assert my_array_copy_2[1] == 104
        assert my_array_copy_3[1] == 105

        self.mutate_tuple_items_and_reassign(
            (my_array, my_array_copy_2, my_array_copy_3), start=UInt64(200), reassign=False
        )

        assert my_array[0] == 200
        assert my_array_copy_2[0] == 201
        assert my_array_copy_3[0] == 202
        assert my_array[1] == 206
        assert my_array_copy_2[1] == 207
        assert my_array_copy_3[1] == 208

        # Nested array items should still require a copy
        nested = StructWithArray(test_array=my_array.copy())
        self.other_routine_2(nested.test_array.copy())

    @subroutine
    def other_routine(self, array: TestArray, struct: TestStruct) -> tuple[bool, bool]:
        array[1] = UInt8(5)
        struct.s_val_1 = String("AARRGH!")
        return True, False

    @subroutine
    def other_routine_2(self, array: TestArray) -> TestArray:
        copy = array.copy()
        array[0] = UInt8(10)
        return copy

    @subroutine
    def mutate_tuple_items_and_reassign(
        self, arrays: tuple[TestArray, TestArray, TestArray], *, start: UInt64, reassign: bool
    ) -> None:
        arrays[0][0] = UInt8(start)
        arrays[1][0] = UInt8(start + 1)
        arrays[2][0] = UInt8(start + 2)

        assert arrays[0][0] == start
        assert arrays[1][0] == start + 1
        assert arrays[2][0] == start + 2

        arrays[0][1] = UInt8(start + 3)
        arrays[1][1] = UInt8(start + 4)
        arrays[2][1] = UInt8(start + 5)

        # overwrite params
        if reassign:
            arrays = (arrays[0].copy(), arrays[1].copy(), arrays[2].copy())

        arrays[0][1] = UInt8(start + 6)
        arrays[1][1] = UInt8(start + 7)
        arrays[2][1] = UInt8(start + 8)

        assert arrays[0][1] == start + 6
        assert arrays[1][1] == start + 7
        assert arrays[2][1] == start + 8

    def clear_state_program(self) -> bool:
        return True


from algopy import arc4, subroutine

class MutableParams2(arc4.ARC4Contract):
    @arc4.abimethod()
    def test_array_rebinding(self) -> None:
        a = arc4.DynamicBytes(0)
        self.maybe_modify_array(a, assign_local=True)
        assert a == arc4.DynamicBytes(0, 1)

        a = arc4.DynamicBytes(1)
        self.maybe_modify_array(a, assign_local=False)
        assert a == arc4.DynamicBytes(1, 42, 4)

    @subroutine
    def maybe_modify_array(self, a: arc4.DynamicBytes, *, assign_local: bool) -> None:  # v0
        if assign_local:
            a.append(arc4.Byte(1))  # v1: modify out
            a = arc4.DynamicBytes(1, 2, 3)  # v2: BOUNDARY
            a.append(arc4.Byte(4))  # v3: local only
            a = arc4.DynamicBytes(1, 2, 4)  # v4: local only
        else:
            a.append(arc4.Byte(42))  # v5: modify out

        a.append(arc4.Byte(4))  # v6: modify out IF not b ELSE local only


import typing
import typing as t

from algopy import Bytes, Contract, subroutine
from algopy.arc4 import (
    Bool,
    DynamicArray,
    StaticArray,
    String,
    Struct,
    UFixedNxM,
    UInt8,
    UInt16,
)

AliasedDynamicArray: t.TypeAlias = DynamicArray[UInt16]
AliasedStaticArray: t.TypeAlias = StaticArray[UInt8, t.Literal[1]]

Decimal: t.TypeAlias = UFixedNxM[t.Literal[64], t.Literal[10]]

class TestStruct(Struct):
    b_val: Bool
    u_val: UInt8
    s_val_1: String
    s_val_2: String

class StructWithArray(Struct):
    d_array: DynamicArray[String]

class Arc4MutationContract(Contract):
    def approval_program(self) -> bool:
        self.dynamic_array_fixed_size()
        self.dynamic_array_bool()
        self.dynamic_array_string()
        self.array_of_array_dynamic()
        self.array_of_array_static()
        self.index_assign()
        self.struct_assign()
        self.array_concat()
        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def check_copy_required(self) -> None:
        d_array = DynamicArray(String("Test"))

        nested_in_array = DynamicArray(d_array.copy())

        nested_in_struct = StructWithArray(d_array=nested_in_array[0].copy())

        assert d_array == nested_in_struct.d_array

    @subroutine
    def array_concat(self) -> None:
        uint8_array = DynamicArray(UInt8(1), UInt8(2))
        array_concat_tuple = uint8_array + (UInt8(3), UInt8(4))  # noqa: RUF005
        assert array_concat_tuple == DynamicArray(UInt8(1), UInt8(2), UInt8(3), UInt8(4))
        array_concat_tuple += (UInt8(5),)
        assert array_concat_tuple == DynamicArray(UInt8(1), UInt8(2), UInt8(3), UInt8(4), UInt8(5))

        hello_world = DynamicArray(String("Hello"), String("World"))
        hello_world_concat = DynamicArray(String("Hello")) + DynamicArray(String("World"))
        assert hello_world == hello_world_concat

    @subroutine
    def array_of_array_dynamic(self) -> None:
        array_of_array = DynamicArray[DynamicArray[UInt8]]()
        assert array_of_array.bytes == Bytes.from_hex("0000")
        array_of_array.append(DynamicArray[UInt8](UInt8(10)))
        assert array_of_array.bytes == Bytes.from_hex("0001000200010A")
        array_of_array.append(DynamicArray[UInt8](UInt8(16)))
        assert array_of_array.bytes == Bytes.from_hex("00020004000700010A000110")
        array_of_array[0].append(UInt8(255))

        assert array_of_array.bytes == Bytes.from_hex("00020004000800020AFF000110")

        array_of_array[0][1] = UInt8(0)
        assert array_of_array.bytes == Bytes.from_hex("00020004000800020A00000110")

    @subroutine
    def array_of_array_static(self) -> None:
        array_of_array = StaticArray[StaticArray[UInt8, typing.Literal[2]], typing.Literal[2]](
            StaticArray[UInt8, typing.Literal[2]](UInt8(10), UInt8(9)),
            StaticArray[UInt8, typing.Literal[2]](UInt8(64), UInt8(128)),
        )

        assert array_of_array.bytes == Bytes.from_hex("0A094080")

        array_of_array[0] = StaticArray[UInt8, typing.Literal[2]](UInt8(255), UInt8(254))
        assert array_of_array.bytes == Bytes.from_hex("FFFE4080")

        array_of_array[1][0] = UInt8(1)
        assert array_of_array.bytes == Bytes.from_hex("FFFE0180")

    @subroutine
    def index_assign(self) -> None:
        dynamic_uint8_array = DynamicArray[UInt8](UInt8(1), UInt8(2))
        dynamic_uint8_array[0] = UInt8(255)
        assert dynamic_uint8_array.bytes == Bytes.from_hex("0002ff02")
        static_uint8_array = StaticArray(UInt8(1), UInt8(2))
        static_uint8_array[1] = UInt8(255)
        assert static_uint8_array.bytes == Bytes.from_hex("01ff")

        dynamic_bool_array = DynamicArray(Bool(True), Bool(False))
        dynamic_bool_array[0] = Bool(False)
        assert dynamic_bool_array.bytes == Bytes.from_hex("000200")
        static_bool_array = StaticArray[Bool, typing.Literal[2]](Bool(True), Bool(True))
        static_bool_array[1] = Bool(False)
        assert static_bool_array.bytes == Bytes.from_hex("80")

    @subroutine
    def struct_assign(self) -> None:
        test_struct = TestStruct(
            b_val=Bool(True),
            u_val=UInt8(50),
            s_val_1=String("Happy"),
            s_val_2=String("Days"),
        )

        test_struct.b_val = Bool(False)
        test_struct.u_val = UInt8(12)
        assert test_struct == TestStruct(
            b_val=Bool(False),
            u_val=UInt8(12),
            s_val_1=String("Happy"),
            s_val_2=String("Days"),
        )
        test_struct.s_val_1 = String("Hmmmm")
        test_struct.s_val_2 = String("Oh well")

        assert test_struct == TestStruct(
            b_val=Bool(False),
            u_val=UInt8(12),
            s_val_1=String("Hmmmm"),
            s_val_2=String("Oh well"),
        )

    @subroutine
    def dynamic_array_fixed_size(self) -> None:
        dynamic_uint8_array = DynamicArray[UInt8](UInt8(1), UInt8(2))
        dynamic_uint8_array.append(UInt8(50))
        assert dynamic_uint8_array == DynamicArray[UInt8](UInt8(1), UInt8(2), UInt8(50))
        dynamic_uint8_array.extend(dynamic_uint8_array)

        assert dynamic_uint8_array == DynamicArray[UInt8](
            UInt8(1), UInt8(2), UInt8(50), UInt8(1), UInt8(2), UInt8(50)
        )
        dynamic_uint8_array.extend((UInt8(4), UInt8(90)))

        assert dynamic_uint8_array == DynamicArray(
            UInt8(1), UInt8(2), UInt8(50), UInt8(1), UInt8(2), UInt8(50), UInt8(4), UInt8(90)
        )

        popped = dynamic_uint8_array.pop()
        assert popped == UInt8(90)

        assert dynamic_uint8_array == DynamicArray(
            UInt8(1), UInt8(2), UInt8(50), UInt8(1), UInt8(2), UInt8(50), UInt8(4)
        )

    @subroutine
    def dynamic_array_bool(self) -> None:
        dynamic_bool_array = DynamicArray[Bool](Bool(True), Bool(False))
        assert dynamic_bool_array.bytes == Bytes.from_hex("000280")
        dynamic_bool_array.extend((Bool(True), Bool(False)))
        assert dynamic_bool_array.bytes == Bytes.from_hex("0004A0")
        assert dynamic_bool_array == DynamicArray[Bool](
            Bool(True), Bool(False), Bool(True), Bool(False)
        )
        dynamic_bool_array.extend(dynamic_bool_array)

        assert dynamic_bool_array == DynamicArray[Bool](
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
        )
        dynamic_bool_array.append(Bool(True))

        assert dynamic_bool_array == DynamicArray[Bool](
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
        )

        assert dynamic_bool_array.pop() == Bool(True)
        assert dynamic_bool_array.pop() == Bool(False)
        assert dynamic_bool_array == DynamicArray[Bool](
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
            Bool(False),
            Bool(True),
        )

    @subroutine
    def dynamic_array_string(self) -> None:
        hello = String("Hello")
        world = String("World")
        foo = String("Foo")
        bar = String("Bar")
        dynamic_string_array = DynamicArray(hello, world)
        assert dynamic_string_array.bytes == Bytes(
            b"\x00\x02\x00\x04\x00\x0b\x00\x05Hello\x00\x05World"
        )
        dynamic_string_array.extend((foo, bar))

        assert dynamic_string_array.bytes == Bytes(
            b"\x00\x04\x00\x08\x00\x0f\x00\x16\x00\x1b\x00\x05Hello\x00\x05World\x00\x03Foo\x00\x03Bar"
        )

        dynamic_string_array.extend(dynamic_string_array)

        assert dynamic_string_array == DynamicArray(hello, world, foo, bar, hello, world, foo, bar)
        dynamic_string_array = DynamicArray(hello, world, foo, bar, hello, world, foo, bar)
        dynamic_string_array[3] = hello
        dynamic_string_array[5] = hello

        assert dynamic_string_array == DynamicArray(
            hello, world, foo, hello, hello, hello, foo, bar
        )

        assert dynamic_string_array.pop() == bar
        assert dynamic_string_array.pop() == foo
        assert dynamic_string_array == DynamicArray(hello, world, foo, hello, hello, hello)


import typing
import typing as t

from algopy import BigUInt, Bytes, Contract, OnCompleteAction, Txn, UInt64, op
from algopy.arc4 import (
    BigUFixedNxM,
    BigUIntN,
    Byte,
    Tuple,
    UFixedNxM,
    UInt8,
    UInt16,
    UInt32,
    UInt64 as ARC4UInt64,
    UInt512,
    UIntN,
)

Decimal: t.TypeAlias = UFixedNxM[t.Literal[64], t.Literal[10]]

ARC4BigUInt: t.TypeAlias = BigUIntN[t.Literal[128]]

ARC4BiggieSmalls: t.TypeAlias = BigUIntN[t.Literal[32]]

sixty_four_byte_num = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  # noqa: E501

class Arc4NumericTypesContract(Contract):
    def approval_program(self) -> bool:
        uint8 = UInt64(255)

        int8_encoded = UInt8(uint8)

        int8_decoded = int8_encoded.native

        assert uint8 == int8_decoded

        test_bytes = Bytes.from_hex("7FFFFFFFFFFFFFFF00")
        assert UInt8.from_bytes(test_bytes[:1]).native == 2**8 - 1 - 2**7
        assert UIntN[typing.Literal[24]].from_bytes(test_bytes[:3]).native == 2**24 - 1 - 2**23
        assert UInt16.from_bytes(test_bytes[:2]).native == 2**16 - 1 - 2**15
        assert UInt32.from_bytes(test_bytes[:4]).native == 2**32 - 1 - 2**31
        assert ARC4UInt64.from_bytes(test_bytes[:8]).native == 2**64 - 1 - 2**63
        assert UInt8(1 if Txn.num_app_args else 2) == 2
        assert UInt512(1 if Txn.num_app_args else 2) == 2

        decimals = Decimal("145.6853943940")

        assert decimals.bytes == op.itob(145_6853943940)

        decimals_from_truncated_str = Decimal("145.0")

        assert decimals_from_truncated_str.bytes == op.itob(145_0000000000)

        thousand = Decimal("1e3")

        assert thousand.bytes.length == 8
        assert thousand.bytes == op.itob(1000_0000000000)

        one_decimal = Decimal("1.0")

        assert one_decimal.bytes == op.itob(1_0000000000)

        zero_decimal = Decimal("0.0")

        assert zero_decimal.bytes == op.itob(0)

        small_decimal = Decimal("0.00000001")

        assert small_decimal.bytes == op.itob(100)

        smaller_decimal = Decimal("1E-9")

        assert smaller_decimal.bytes == op.itob(10)

        smallest_decimal = Decimal("0.0000000001")

        assert smallest_decimal.bytes == op.itob(1)

        sixty_four_decimal = Decimal("1844674407.3709551615")

        assert sixty_four_decimal.bytes == op.itob(1844674407_3709551615)

        really_big_int = BigUIntN[t.Literal[512]](sixty_four_byte_num)

        assert really_big_int == BigUIntN[t.Literal[512]].from_bytes(really_big_int.bytes)

        really_big_decimal = BigUFixedNxM[t.Literal[512], t.Literal[2]].from_bytes(
            BigUInt(sixty_four_byte_num).bytes
        )

        assert Decimal("1844674407.3709551615" if Txn.num_app_args else "0.0") == Decimal()

        biguint = BigUInt(1)
        arc4_biguint_const = ARC4BigUInt(1)
        arc4_biguint_dynamic = ARC4BigUInt(biguint + 1)

        assert biguint == arc4_biguint_const.native

        assert arc4_biguint_dynamic.bytes.length == (128 // 8)

        assert really_big_decimal.bytes.length == 64

        # check UInt64 sub-types are converted properly
        tup = Tuple((ARC4UInt64(OnCompleteAction.ClearState),))
        assert tup[0].native == OnCompleteAction.ClearState

        assert ARC4BiggieSmalls(1).native == BigUInt(1)

        return True

    def clear_state_program(self) -> bool:
        assert BigUInt.from_bytes(Decimal().bytes) == 0
        assert BigUInt.from_bytes(BigUFixedNxM[t.Literal[512], t.Literal[5]]().bytes) == 0
        assert Byte() == 0
        assert ARC4UInt64() == 0
        assert UInt512() == 0

        return True


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class Arc4DynamicStringArrayContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def xyz(
        self,
    ) -> algopy.arc4.DynamicArray[algopy.arc4.String]: ...

    @algopy.arc4.abimethod
    def xyz_raw(
        self,
    ) -> algopy.arc4.DynamicArray[algopy.arc4.String]: ...


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class MutableParams2(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def test_array_rebinding(
        self,
    ) -> None: ...


from algopy import Contract, arc4, op

class Arc4RefTypesContract(Contract):
    def approval_program(self) -> bool:
        # When creating an address from an account no need to check the length as we assume the
        # Account is valid
        sender_address = arc4.Address(op.Txn.sender)
        assert sender_address == op.Txn.sender
        # When creating an address from bytes, we check the length is 32 as we don't know the
        # source of the bytes
        checked_address = arc4.Address(op.Txn.sender.bytes)
        # When using from_bytes, no validation is performed as per all implementations of
        # from_bytes
        unchecked_address = arc4.Address.from_bytes(op.Txn.sender.bytes)
        assert sender_address == checked_address and checked_address == unchecked_address

        assert arc4.Address() == "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY5HFKQ"
        return True

    def clear_state_program(self) -> bool:
        return True


from algopy import Contract, String, Txn, arc4

HELLO_WORLD = b"Hello World!"

class Arc4StringTypesContract(Contract):
    def approval_program(self) -> bool:
        some_bytes_as_string = arc4.String(String.from_bytes(HELLO_WORLD))

        some_bytes_as_bytes_again = some_bytes_as_string.native.bytes

        assert (
            some_bytes_as_string.bytes != HELLO_WORLD
        ), "Original bytes should not match encoded bytes"

        assert (
            some_bytes_as_string.bytes[2:] == HELLO_WORLD
        ), "Original bytes should match encoded if we strip the length header"

        assert some_bytes_as_bytes_again == HELLO_WORLD

        hello = arc4.String("Hello")
        space = arc4.String(" ")
        world = arc4.String("World!")

        assert arc4.String("Hello World!") == (hello + space + world)

        thing = arc4.String("hi")
        thing += thing
        assert thing == arc4.String("hihi")

        value = arc4.String("a") + arc4.String("b") + "cd"
        value += "e"
        value += arc4.String("f")
        value += arc4.String("g")
        assert arc4.String("abcdefg") == value

        assert arc4.String().native == ""
        assert arc4.String("hello") == String("hello")

        assert arc4.String("aye" if Txn.num_app_args else "bee") == "bee"
        return True

    def clear_state_program(self) -> bool:
        return True


import typing

from algopy import Contract, arc4, log, op, subroutine

Decimal: typing.TypeAlias = arc4.UFixedNxM[typing.Literal[64], typing.Literal[9]]

class Vector(arc4.Struct, kw_only=True):
    x: Decimal
    y: Decimal

class Flags(arc4.Struct):
    a: arc4.Bool
    b: arc4.Bool
    c: arc4.Bool
    d: arc4.Bool

class VectorFlags(arc4.Struct):
    vector: Vector
    flags: Flags

class FrozenButMutable(arc4.Struct, frozen=True):
    mutable: arc4.DynamicBytes

class FrozenAndImmutable(arc4.Struct, frozen=True):
    one: arc4.UInt64
    two: arc4.UInt64

class Arc4StructsTypeContract(Contract):
    def approval_program(self) -> bool:
        coord_1 = Vector(x=Decimal("35.382882839"), y=Decimal("150.382884930"))
        coord_2 = Vector(y=Decimal("150.382884930"), x=Decimal("35.382882839"))
        coord_3 = add(coord_1.copy(), coord_2.copy())
        for val in (coord_3.x, coord_3.y):
            log(val.bytes)

        flags = Flags(a=arc4.Bool(True), b=arc4.Bool(False), c=arc4.Bool(True), d=arc4.Bool(False))
        check(flags.copy())
        log(flags.bytes)
        assert Vector.from_bytes(coord_1.bytes).bytes == coord_1.bytes

        nested_decode(VectorFlags(coord_1.copy(), flags.copy()))

        mutable = FrozenButMutable(arc4.DynamicBytes())
        copy = mutable.copy()
        copy.mutable.append(arc4.Byte(42))
        assert mutable != copy, "expected copy is different"

        immutable = FrozenAndImmutable(arc4.UInt64(12), arc4.UInt64(34))
        no_copy = immutable
        assert no_copy == immutable

        immutable2 = immutable._replace(two=arc4.UInt64(123))
        assert immutable2.two == 123
        assert immutable2.one == immutable.one

        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def add(v1: Vector, v2: Vector) -> Vector:
    return Vector(
        x=add_decimal(v1.x, v2.x),
        y=add_decimal(v1.y, v2.y),
    )

@subroutine
def check(flags: Flags) -> None:
    assert flags.a.native
    assert not flags.b.native
    assert flags.c.native
    assert not flags.d.native

@subroutine
def nested_decode(vector_flags: VectorFlags) -> None:
    assert vector_flags.vector.x.bytes == op.itob(35382882839)
    assert vector_flags.flags.c.native

@subroutine
def add_decimal(x: Decimal, y: Decimal) -> Decimal:
    return Decimal.from_bytes(op.itob(op.btoi(x.bytes) + op.btoi(y.bytes)))


from algopy import Contract, arc4, log

from test_cases.arc4_types.structs import Flags

class Arc4StructsFromAnotherModule(Contract):
    def approval_program(self) -> bool:
        flags = Flags(a=arc4.Bool(True), b=arc4.Bool(False), c=arc4.Bool(True), d=arc4.Bool(False))
        log(flags.bytes)

        return True

    def clear_state_program(self) -> bool:
        return True


import typing

from algopy import Bytes, Contract, UInt64, subroutine
from algopy.arc4 import Bool, Byte, DynamicBytes, String, Tuple, UInt8

TestTuple: typing.TypeAlias = Tuple[UInt8, UInt8, String, String, UInt8]

TestBooleanPacking: typing.TypeAlias = Tuple[
    UInt8, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, Bool, UInt8
]

class Arc4TuplesTypeContract(Contract):
    def approval_program(self) -> bool:
        my_tuple = Tuple((UInt8(1), UInt8(2), String("hello"), String("world"), UInt8(255)))

        assert my_tuple == TestTuple.from_bytes(  # type: ignore[comparison-overlap]
            Bytes.from_hex("01020007000EFF000568656C6C6F0005776F726C64")
        )
        boolean_packing = Tuple(
            (
                UInt8(4),
                Bool(True),
                Bool(False),
                Bool(True),
                Bool(True),
                Bool(True),
                Bool(True),
                Bool(False),
                Bool(True),
                Bool(True),
                UInt8(16),
            )
        )
        assert boolean_packing.bytes == Bytes.from_hex("04BD8010")
        a, b, c, d, e, f, g, h, i, j, k = boolean_packing.native
        assert boolean_packing[10] == k
        assert a.native == 4, "a is 4"
        assert b and d and e and f and g and i and j, "b,d,e,f,g,i,j are true"
        assert not (c or h), "c and h are false"
        assert k.native == 16, "k is 16"

        assert boolean_packing == TestBooleanPacking(boolean_packing.native)

        total, concat = self.test_stuff(my_tuple)
        assert concat.native == "hello world"
        assert total == 258

        self.test_copy()

        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def test_stuff(self, test_tuple: TestTuple) -> tuple[UInt64, String]:
        a, b, c, d, e = test_tuple.native

        assert test_tuple[-0] == a
        assert test_tuple[-1] == e
        assert test_tuple[-2] == d

        total = a.native + b.native + e.native
        text = c.native + " " + d.native

        return total, String(text)

    @subroutine
    def test_copy(self) -> None:
        tup = Tuple((UInt8(), DynamicBytes()))
        tup[1].append(Byte())

        assert tup[1] == DynamicBytes(0)

        tup2 = tup.copy()
        tup[1][0] = Byte(1)

        assert tup[1] != tup2[1]


import typing

from algopy import (
    ARC4Contract,
    Box,
    BoxMap,
    BoxRef,
    Bytes,
    GlobalState,
    LocalState,
    String,
    TemplateVar,
    UInt64,
    arc4,
    subroutine,
)

class SharedStruct(arc4.Struct):
    """This struct is shared"""

    foo: arc4.DynamicBytes
    bar: arc4.UInt8

class EventOnly(arc4.Struct):
    """This struct is only used in an event"""

    x: arc4.UInt64
    y: arc4.UInt64

class TopLevelStruct(arc4.Struct):
    a: arc4.UInt64
    b: arc4.String
    shared: SharedStruct

class StateStruct(arc4.Struct):
    a: arc4.UInt64
    b: arc4.String

@subroutine
def constant_method() -> UInt64:
    return UInt64(42)

@subroutine
def echo_number(x: UInt64) -> UInt64:
    return x

class Contract(ARC4Contract):
    """Contract docstring"""

    def __init__(self) -> None:
        self.g_struct = GlobalState(StateStruct)
        self.g_uint64 = GlobalState(UInt64, key=b"gu")
        self.g_address = GlobalState(arc4.Address, key=b"ga")

        self.l_struct = LocalState(StateStruct)
        self.l_uint64 = LocalState(UInt64, key=b"lu")
        self.l_address = LocalState(arc4.Address, key=b"la")

        self.b_struct = Box(StateStruct)
        self.b_uint64 = Box(UInt64, key=b"bu")
        self.b_address = Box(arc4.Address, key=b"ba")

        self.box_map_struct = BoxMap(StateStruct, SharedStruct)
        self.box_map_uint64 = BoxMap(UInt64, SharedStruct, key_prefix=b"bmu")
        self.box_map_address = BoxMap(arc4.Address, SharedStruct, key_prefix=b"bma")

        self.box_ref = BoxRef()
        self.box_ref2 = BoxRef(key=b"br")

    @arc4.baremethod(create="allow", allow_actions=["NoOp", "OptIn"])
    def bare_create(self) -> None:
        pass

    @arc4.abimethod(create="allow", allow_actions=["NoOp", "OptIn"])
    def create(self) -> None:
        """Method docstring"""

    @arc4.baremethod(create="require", allow_actions=["DeleteApplication"])
    def transient(self) -> None:
        pass

    @arc4.abimethod()
    def struct_arg(self, arg: TopLevelStruct, shared: SharedStruct) -> UInt64:
        """Method docstring2"""
        assert arg.shared == shared, "this might error"
        return UInt64(42)

    @arc4.abimethod(readonly=True)
    def struct_return(self, arg: TopLevelStruct) -> SharedStruct:
        assert arg.shared == echo(arg.shared), "this won't error"
        return arg.shared

    @arc4.abimethod(name="emits_error", readonly=True)
    def errors(self, arg: TopLevelStruct) -> None:
        assert arg.a.bytes == arc4.UInt8(0).bytes, "this will error"

    @arc4.abimethod()
    def emitter(self) -> None:
        arc4.emit(SharedStruct(foo=arc4.DynamicBytes(b"hello1"), bar=arc4.UInt8(42)))

        arc4.emit(
            "Anonymous",
            String("hello"),
            SharedStruct(foo=arc4.DynamicBytes(b"hello2"), bar=arc4.UInt8(42)),
        )

    @arc4.abimethod()
    def conditional_emit(self, should_emit: bool) -> None:
        if should_emit:
            arc4.emit(
                "Anonymous2",
                EventOnly(arc4.UInt64(42), arc4.UInt64(43)),
                SharedStruct(foo=arc4.DynamicBytes(b"hello3"), bar=arc4.UInt8(42)),
            )

    @arc4.abimethod()
    def template_value(self) -> tuple[SharedStruct, UInt64, String, arc4.UInt8]:
        return (
            TemplateVar[SharedStruct]("STRUCT"),
            TemplateVar[UInt64]("AVM_UINT64"),
            TemplateVar[String]("AVM_STRING"),
            TemplateVar[arc4.UInt8]("ARC4_UINT8"),
        )

    @arc4.abimethod(
        default_args={
            "a": arc4.UInt64(123),
            "c": Bytes(b"123"),
            "b": UInt64(234),
            "d": EventOnly(x=arc4.UInt64(1), y=arc4.UInt64(2)),
            "e": (UInt64(42), String("on the edge")),
            "f": arc4.StaticArray(arc4.String("hi"), arc4.String("there!")),
            "g": arc4.DynamicArray(arc4.String("one"), arc4.String("two")),
            "h": constant_method(),
            "i": echo_number(UInt64(1234)),
        }
    )
    def with_constant_defaults(
        self,
        a: arc4.UInt64,
        b: UInt64,
        c: Bytes,
        d: EventOnly,
        e: tuple[UInt64, String],
        f: arc4.StaticArray[arc4.String, typing.Literal[2]],
        g: arc4.DynamicArray[arc4.String],
        h: UInt64,
        i: UInt64,
    ) -> None:
        assert a
        assert b
        assert c
        assert d.x or d.y
        assert e[0] or e[1]
        assert f[0] == "hi"
        assert g.length == 2
        assert h == 42
        assert i == 1234

@subroutine
def echo(s: SharedStruct) -> SharedStruct:
    return s


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class SharedStruct(algopy.arc4.Struct):
    foo: algopy.arc4.DynamicBytes
    bar: algopy.arc4.UIntN[typing.Literal[8]]

class TopLevelStruct(algopy.arc4.Struct):
    a: algopy.arc4.UIntN[typing.Literal[64]]
    b: algopy.arc4.String
    shared: SharedStruct

class EventOnly(algopy.arc4.Struct):
    x: algopy.arc4.UIntN[typing.Literal[64]]
    y: algopy.arc4.UIntN[typing.Literal[64]]

class Contract(algopy.arc4.ARC4Client, typing.Protocol):
    """
    Contract docstring
    """
    @algopy.arc4.abimethod(allow_actions=['NoOp', 'OptIn'], create='allow')
    def create(
        self,
    ) -> None:
        """
        Method docstring
        """

    @algopy.arc4.abimethod
    def struct_arg(
        self,
        arg: TopLevelStruct,
        shared: SharedStruct,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]:
        """
        Method docstring2
        """

    @algopy.arc4.abimethod(readonly=True)
    def struct_return(
        self,
        arg: TopLevelStruct,
    ) -> SharedStruct: ...

    @algopy.arc4.abimethod(readonly=True)
    def emits_error(
        self,
        arg: TopLevelStruct,
    ) -> None: ...

    @algopy.arc4.abimethod
    def emitter(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def conditional_emit(
        self,
        should_emit: algopy.arc4.Bool,
    ) -> None: ...

    @algopy.arc4.abimethod
    def template_value(
        self,
    ) -> algopy.arc4.Tuple[algopy.arc4.Tuple[algopy.arc4.DynamicBytes, algopy.arc4.UIntN[typing.Literal[8]]], algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.String, algopy.arc4.UIntN[typing.Literal[8]]]: ...

    @algopy.arc4.abimethod
    def with_constant_defaults(
        self,
        a: algopy.arc4.UIntN[typing.Literal[64]],
        b: algopy.arc4.UIntN[typing.Literal[64]],
        c: algopy.arc4.DynamicBytes,
        d: EventOnly,
        e: algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.String],
        f: algopy.arc4.StaticArray[algopy.arc4.String, typing.Literal[2]],
        g: algopy.arc4.DynamicArray[algopy.arc4.String],
        h: algopy.arc4.UIntN[typing.Literal[64]],
        i: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> None: ...


from algopy import (
    Asset,
    Contract,
    TransactionType,
    UInt64,
    subroutine,
)
from algopy.op import (
    Global,
    ITxnCreate,
    Txn,
    bzero,
)

class Reference(Contract):
    def __init__(self) -> None:
        self.asa = Asset()

    def approval_program(self) -> bool:
        if Txn.num_app_args == 1:
            if Txn.application_args(0) == b"opt_in":
                asset = Txn.assets(0)
                self.opt_into_asset(asset)
            elif Txn.application_args(0) == b"is_opted_in":
                asset = Txn.assets(0)
                self.is_opted_asset(asset)
            else:
                assert False, "Expected opt_in or is_opted_in"
        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def opt_into_asset(self, asset: Asset) -> None:
        # Only allow app creator to opt the app account into a ASA
        assert Txn.sender == Global.creator_address, "Only creator can opt in to ASA"
        # Verify a ASA hasn't already been opted into
        assert not self.asa, "ASA already opted in"
        # Save ASA ID in global state
        self.asa = asset

        # Submit opt-in transaction: 0 asset transfer to self
        ITxnCreate.begin()
        ITxnCreate.set_type_enum(TransactionType.AssetTransfer)
        ITxnCreate.set_fee(UInt64(0))  # cover fee with outer txn
        ITxnCreate.set_asset_receiver(Global.current_application_address)
        ITxnCreate.set_xfer_asset(asset)
        ITxnCreate.submit()

    @subroutine
    def is_opted_asset(self, asset: Asset) -> None:
        assert self.asa == asset, "asset self.asa == asset"
        assert asset.total == 10000000, "total"
        assert asset.decimals == 0, "decimals"
        assert not asset.default_frozen, "default frozen"
        assert asset.unit_name == b"a", "unit_name"
        assert asset.name == b"asset a", "name"
        assert asset.url == b"", "URL"
        assert asset.metadata_hash == bzero(32), "hash"
        assert asset.manager == Global.zero_address, "manager"
        assert asset.reserve == Global.zero_address, "reserve"
        assert asset.freeze == Global.zero_address, "freeze"
        assert asset.clawback == Global.zero_address, "clawback"
        assert asset.creator == Global.creator_address, "creator"
        assert Global.current_application_address.is_opted_in(asset), "asset opted in"


from algopy import (
    Account,
    BigUInt,
    Bytes,
    Contract,
    LocalState,
    OnCompleteAction,
    UInt64,
    op,
    subroutine,
)

class Augmented(Contract):
    def __init__(self) -> None:
        self.my_uint = LocalState(UInt64)
        self.my_bytes = LocalState(Bytes)
        self.counter = UInt64()
        self.global_uint = UInt64(0)
        self.global_bytes = Bytes(b"")

    def approval_program(self) -> bool:
        me = op.Txn.sender

        if op.Txn.on_completion == OnCompleteAction.OptIn:
            self.my_uint[me] = UInt64(0)
            self.my_bytes[me] = Bytes(b"")
        if op.Txn.application_id:
            # variable augmented assignment
            n = op.Txn.num_app_args
            bytes_to_add = BigUInt(n).bytes

            # local augmented assignment
            self.my_uint[me] += n
            self.my_bytes[me] += bytes_to_add

            # global augmented assignment
            self.global_uint += n
            self.global_bytes += bytes_to_add

            self.counter = UInt64()
            assert self.counter == 0
            self.my_uint[self.get_sender_with_side_effect()] += 1
            assert self.counter == 1, "this should not error"
            self.my_uint[self.get_sender_with_side_effect()] -= 1
            assert self.counter == 2, "this should not error"

        return True

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def get_sender_with_side_effect(self) -> Account:
        self.counter += 1
        return op.Txn.sender


from algopy import ARC4Contract, Txn, arc4, op

class Contract(ARC4Contract, avm_version=11):
    @arc4.abimethod
    def test_new_ops(self) -> None:
        # op functions
        assert op.online_stake()
        assert op.mimc(op.MiMCConfigurations.BN254Mp110, op.bzero(32))

        # AcctParamsGet, TODO: add to Account once 11 is in mainnet?
        a, b = op.AcctParamsGet.acct_incentive_eligible(Txn.sender)
        c, d = op.AcctParamsGet.acct_last_proposed(Txn.sender)
        e, f = op.AcctParamsGet.acct_last_heartbeat(Txn.sender)

        # Block
        assert not op.Block.blk_proposer(0), "proposer"
        assert op.Block.blk_fees_collected(0), "fees collected"
        assert op.Block.blk_bonus(0), "bonus"
        assert op.Block.blk_branch(0), "branch"
        assert op.Block.blk_fee_sink(0), "fee sink"
        assert op.Block.blk_protocol(0), "protocol"
        assert op.Block.blk_txn_counter(0), "txn counter"
        assert op.Block.blk_proposer_payout(0), "proposer payout"

        # Global
        assert op.Global.payouts_enabled, "payouts_enabled"
        assert op.Global.payouts_go_online_fee, "payouts_go_online_fee"
        assert op.Global.payouts_percent, "payouts_percent"
        assert op.Global.payouts_min_balance, "payouts_min_balance"
        assert op.Global.payouts_max_balance, "payouts_max_balance"

        # Voter params
        g, h = op.VoterParamsGet.voter_balance(0)
        i, j = op.VoterParamsGet.voter_incentive_eligible(0)


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class Contract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def test_new_ops(
        self,
    ) -> None: ...


from algopy import ARC4Contract, UInt64, arc4, logicsig, op

@logicsig(avm_version=12)
def avm_12_sig() -> UInt64:
    return op.sumhash512(b"").length

class Contract(ARC4Contract, avm_version=12):
    @arc4.abimethod
    def test_new_ops(self) -> None:
        # op functions
        assert not op.falcon_verify(b"", b"", op.bzero(1793))
        assert op.sumhash512(b"")


from algopy import BigUInt, Bytes, String, UInt64, arc4

class TestContract(arc4.ARC4Contract):
    @arc4.abimethod(allow_actions=["NoOp"], create="require")
    def create(
        self,
        bool_param: bool,
        uint64_param: UInt64,
        bytes_param: Bytes,
        biguint_param: BigUInt,
        string_param: String,
        tuple_param: tuple[bool, UInt64, Bytes, BigUInt, String],
    ) -> tuple[bool, UInt64, Bytes, BigUInt, String]:
        result = (bool_param, uint64_param, bytes_param, biguint_param, string_param)
        assert result == tuple_param
        return result

    @arc4.abimethod
    def tuple_of_arc4(
        self, args: tuple[arc4.UInt8, arc4.Address]
    ) -> tuple[arc4.UInt8, arc4.Address]:
        assert args[0].bytes.length == 1
        assert args[1].bytes.length == 32
        return args


# This file is auto-generated, do not modify
# flake8: noqa
# fmt: off
import typing

import algopy

class TestContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod(create='require')
    def create(
        self,
        bool_param: algopy.arc4.Bool,
        uint64_param: algopy.arc4.UIntN[typing.Literal[64]],
        bytes_param: algopy.arc4.DynamicBytes,
        biguint_param: algopy.arc4.BigUIntN[typing.Literal[512]],
        string_param: algopy.arc4.String,
        tuple_param: algopy.arc4.Tuple[algopy.arc4.Bool, algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.DynamicBytes, algopy.arc4.BigUIntN[typing.Literal[512]], algopy.arc4.String],
    ) -> algopy.arc4.Tuple[algopy.arc4.Bool, algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.DynamicBytes, algopy.arc4.BigUIntN[typing.Literal[512]], algopy.arc4.String]: ...

    @algopy.arc4.abimethod
    def tuple_of_arc4(
        self,
        args: algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[8]], algopy.arc4.Address],
    ) -> algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[8]], algopy.arc4.Address]: ...


from algopy import BigUInt, Contract, op, subroutine

class BiguintBinaryOps(Contract):
    def approval_program(self) -> bool:
        left = BigUInt(58446744073709552000)
        right = BigUInt(18446744073709552000)
        # Math
        assert left + right == BigUInt(76893488147419104000)
        assert left - right == BigUInt(40000000000000000000)
        assert left * right == BigUInt(1078152129869320557630474056040704000000)
        assert left // right == BigUInt(3)
        assert left % right == BigUInt(3106511852580896000)
        # Boolean
        assert not (left < right)
        assert not (left <= right)
        assert left > right
        assert left >= right
        assert not (left == right)  # noqa: SIM201
        assert left != right
        # Bitwise
        assert left | right == BigUInt(58446744073709552000)
        assert left & right == BigUInt(18446744073709552000)
        assert left ^ right == BigUInt(40000000000000000000)
        assert bitwise_ops(left) == bitwise_ops(left)
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def bitwise_ops(value: BigUInt) -> BigUInt:
    low128 = BigUInt.from_bytes(op.bzero(16) + ~op.bzero(16))
    wide_value_compl = (value ^ low128) + BigUInt(1)

    return wide_value_compl & low128