from algopy import Contract, UInt64, log, op, uenumerate, urange

LOOP_ITERATIONS = 2  # max op code budget is exceeded with more

class Nested(Contract):
    def approval_program(self) -> UInt64:
        n = UInt64(LOOP_ITERATIONS)
        x = UInt64(0)

        for a in urange(n):
            for b in urange(n):
                for c in urange(n):
                    for d in urange(n):
                        for e in urange(n):
                            for f in urange(n):
                                x += a + b + c + d + e + f
            # Iterator variable should be mutable, but mutating it
            # should not affect the next iteration
            a += n

        log(op.itob(x))

        y = UInt64(0)
        for index, item in uenumerate(urange(UInt64(10))):
            y += item * index

        log(op.itob(y))
        return x

    def clear_state_program(self) -> UInt64:
        return UInt64(1)


from algopy import ARC4Contract, Txn
from algopy.arc4 import Bool, DynamicArray, String, Tuple, UInt256, abimethod

class Issue118(ARC4Contract):
    # ref: https://github.com/algorandfoundation/puya/issues/118
    @abimethod
    def verify(self, values: DynamicArray[UInt256]) -> Tuple[Bool, String]:
        val1 = Bool(
            bool(Txn.num_app_args)
        )  # use a non constant value so the repeated expression is not simplified
        if values.length != 2:
            return Tuple((val1, String("")))
        return Tuple((val1, String("")))


from algopy import Contract, UInt64

class Issue194(Contract):
    # ref: https://github.com/algorandfoundation/puya/issues/194
    def approval_program(self) -> bool:
        assert bool(UInt64(1)) == bool(UInt64(2))
        two = UInt64(2)
        match bool(two):
            case True:
                return True
            case _:
                return False

    def clear_state_program(self) -> bool:
        return True


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

import algopy

class Issue118(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def verify(
        self,
        values: algopy.arc4.DynamicArray[algopy.arc4.BigUIntN[typing.Literal[256]]],
    ) -> algopy.arc4.Tuple[algopy.arc4.Bool, algopy.arc4.String]: ...


import algopy as a

class TealSwitchInlining(a.Contract):
    """Test for an issue introduced by TEAL block optimizations,
    whereby a target that is referenced multiple times by the same source block
    might be incorrectly inlined, when it should remain a labelled destination.
    """

    def approval_program(self) -> bool:
        match a.Txn.num_app_args:
            # at -O2, this block and the default block will be de-duplicated,
            # resulting in there being a reference to the default block in the `switch` cases
            # also, which cannot be inlined an must be preserved despite only having a single
            # predecessor block
            case 0:
                return True
            case 1:
                return False
            case _:
                return True

    def clear_state_program(self) -> bool:
        return True


from algopy import ARC4Contract, BigUInt, Bytes, arc4, subroutine

class Contract(ARC4Contract):
    @arc4.abimethod()
    def bytes_to_bool(self) -> bool:
        return bool(Bytes())

    @arc4.abimethod()
    def test_bytes_to_biguint(self) -> None:
        assert bytes_to_biguint()

@subroutine
def bytes_to_biguint() -> BigUInt:
    return BigUInt.from_bytes(Bytes())


# 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 bytes_to_bool(
        self,
    ) -> algopy.arc4.Bool: ...

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


from algopy import Bytes, Contract, Txn, UInt64, op, urange

TWO = 2
TWENTY = 20

class MyContract(Contract, scratch_slots=(1, TWO, urange(3, TWENTY))):
    def approval_program(self) -> bool:
        op.Scratch.store(UInt64(1), 5 if Txn.application_id == 0 else 0)

        hello_world = Bytes(b"Hello World")
        op.Scratch.store(TWO, hello_world)

        for i in urange(3, 20):
            op.Scratch.store(i, i)

        assert op.Scratch.load_uint64(1) == UInt64(5)

        assert op.Scratch.load_bytes(2) == b"Hello World"

        assert op.Scratch.load_uint64(5) == UInt64(5)

        op.Scratch.store(TWENTY - 1, b"last")
        assert op.Scratch.load_bytes(TWENTY - 1) == b"last"
        return True

    def clear_state_program(self) -> bool:
        return True


import typing

import algopy
from algopy import Bytes, UInt64, op, subroutine, urange

from test_cases.scratch_slots.contract import MyContract

RenamedURange: typing.TypeAlias = urange

class MyContract2(
    MyContract,
    scratch_slots=[5, 25, urange(50, 53, 2), RenamedURange(100, 105), algopy.urange(110, 115)],
):
    @subroutine
    def my_sub(self) -> None:
        op.Scratch.store(UInt64(1), Bytes(b"abc"))

        op.Scratch.store(52, Bytes(b"52"))


from algopy import Contract, urange

class MyOtherContract(Contract, scratch_slots=urange(0, -1, -1)):
    def approval_program(self) -> bool:
        return True

    def clear_state_program(self) -> bool:
        return True


import algopy as algo

from test_cases.simple import pkg_a, subs

class MyContract(algo.Contract):
    """My contract"""

    def approval_program(self) -> algo.UInt64:
        a = algo.UInt64(1) + 2
        b = algo.UInt64(4) * 5
        if a == (1 + 2) * (4 - 3):
            if b < 2:
                b = 3 + algo.UInt64(2)
                return a + b
            else:
                b = 2 * a
                if ((3 * 4) + 2) * b:
                    return algo.UInt64(2)
                else:
                    return algo.UInt64(3)
        else:
            return pkg_a.MyUInt64(4)

    def clear_state_program(self) -> algo.UInt64:
        assert pkg_a.Txn2.num_app_args == 0
        return subs.zero() * pkg_a.pkg_1.subs.one()


from algopy import UInt64 as MyUInt64
from algopy.op import Txn

from . import pkg_1

__all__ = [
    "MyUInt64",
    "pkg_1",
    "Txn2",
]

Txn2 = Txn


import algopy

@algopy.subroutine
def one() -> algopy.UInt64:
    return algopy.UInt64(1)


import algopy

@algopy.subroutine
def zero() -> algopy.UInt64:
    return algopy.UInt64(0)


import abc

from algopy import Bytes, Contract, LocalState, UInt64, subroutine

class CallCounter(Contract, abc.ABC):
    def __init__(self) -> None:
        self.counter = UInt64(0)
        self.name = LocalState(Bytes)

    @subroutine
    def increment_counter(self) -> None:
        self.counter += 1

    @subroutine
    def set_sender_nickname(self, nickname: Bytes) -> None:
        self.name[0] = nickname


from algopy import (
    Bytes,
    OnCompleteAction,
    UInt64,
    log,
    subroutine,
)
from algopy.op import (
    AppGlobal,
    AssetHoldingGet,
    Base64,
    Global,
    Txn,
    base64_decode,
    btoi,
    divmodw,
    gload_bytes,
    setbit_bytes,
    setbit_uint64,
)

from test_cases.simplish.base_class import CallCounter

SCALE = 100000
# SCALED_PI = int(3.14159 * SCALE)  # hmmmm
SCALED_PI = 314159

class Simplish(CallCounter):
    def approval_program(self) -> bool:
        if Txn.application_id == 0:
            return True
        oca = Txn.on_completion
        sender = Txn.sender
        if oca in (
            OnCompleteAction.UpdateApplication,
            OnCompleteAction.DeleteApplication,
        ):
            if oca == OnCompleteAction.DeleteApplication:
                log(Bytes(b"I was used ") + itoa(self.counter) + b" time(s) before I died")
            return Global.creator_address == sender

        if oca == OnCompleteAction.OptIn:
            if Txn.num_app_args > 0:
                self.set_sender_nickname(Txn.application_args(0))
            return True
        if oca != OnCompleteAction.NoOp:
            return False

        if (num_app_args := Txn.num_app_args) > 0:
            method_name = Txn.application_args(0)
            msg, result = self.call(method_name, num_app_args)
        elif Txn.num_assets == 1:
            asset_balance, asset_exists = AssetHoldingGet.asset_balance(sender, 0)
            if not asset_exists:
                msg = Bytes(b"You do not have any of the asset")
            else:
                msg = Bytes(b"You have asset balance: ") + itoa(asset_balance)
            result = True
        else:
            msg = Bytes(b"not enough app args or foreign assets")
            result = False
        log(msg)
        self.increment_counter()
        return result

    def clear_state_program(self) -> bool:
        return True

    @subroutine
    def call(self, method_name: Bytes, num_app_args: UInt64) -> tuple[Bytes, bool]:
        assert num_app_args == 2, "insufficient arguments"
        radius = btoi(Txn.application_args(1))

        status = True
        if method_name == b"circle_area":
            area = circle_area(radius)
            result = itoa(area)
        elif method_name == b"circle_circumference":
            circumference = circle_circumference(radius)
            result = itoa(circumference)
        elif method_name == b"circle_report":
            area, circumference = circle_area(radius), circle_circumference(radius)
            result = (
                Bytes(b"Approximate area and circumference of circle with radius ")
                + itoa(radius)
                + b" = "
                + itoa(area)
                + b", "
                + itoa(circumference)
            )
        else:
            status = False
            result = Bytes(b"unknown method name")
        return result, status

    @subroutine
    def increment_counter(self) -> None:
        log("Incrementing counter!")
        super().increment_counter()

@subroutine
def circle_circumference(radius: UInt64) -> UInt64:
    # PI * 2r
    two_pi = UInt64(2) * SCALED_PI
    return radius * two_pi // SCALE

@subroutine
def circle_area(radius: UInt64) -> UInt64:
    # PI * r ^ 2
    result = radius**2 * SCALED_PI // SCALE
    return result

@subroutine
def itoa(i: UInt64) -> Bytes:
    """Itoa converts an integer to the ascii byte string it represents"""
    digits = Bytes(b"0123456789")
    radix = digits.length
    if i < radix:
        return digits[i]
    return itoa(i // radix) + digits[i % radix]

@subroutine
def test_intrinsics() -> UInt64:
    _ii = gload_bytes(1, 1)
    _si = gload_bytes(UInt64(2), 2)
    _ss = gload_bytes(UInt64(3), UInt64(3))
    _is = gload_bytes(4, UInt64(4))
    _foo_uint: UInt64 = setbit_uint64(UInt64(32), 0, 3)
    _foo_int = setbit_uint64(32, 0, 3)
    _foo_bytes: Bytes = setbit_bytes(Bytes(b"32"), 0, 3)
    test = AppGlobal.get_bytes(b"foo")
    AppGlobal.put(b"b", b"yeah")
    AppGlobal.delete(b"foo")
    _expect_bytes: Bytes = test
    _abcd = divmodw(1, 2, 3, 4)
    _hello_str = base64_decode(Base64.StdEncoding, Bytes(b"SGVsbG8="))
    return UInt64(0)


from algopy import Contract, UInt64, log, op, subroutine, urange

class MyContract(Contract):
    """My contract"""

    def approval_program(self) -> UInt64:
        a = UInt64(1) + 2
        b = UInt64(4) * 5

        a = a * b
        b = a + b

        while a < UInt64(5):
            b = b + a
            a = a + 1

        for i in urange(5):
            b = b + a
            a = a + i

        if a < b:
            if b < 2:
                b = 3 + UInt64(2)
                c = a + b
            else:
                b = 2 * a
                if ((3 * 4) + 2) * b:
                    c = UInt64(2)
                else:
                    return UInt64(3)
        elif b == a:
            c = a * b
        else:
            c = a - b
        c = c + one_hundred(c)
        c_bytes = op.itob(c)
        log(c_bytes)
        assert phi_in_equiv_class(UInt64(3), True) == 4
        assert phi_in_equiv_class(UInt64(3), False) == 4
        return c

    def clear_state_program(self) -> UInt64:
        return one_hundred(UInt64(40))

@subroutine
def one_hundred(c: UInt64) -> UInt64:
    a = UInt64(25)
    b = UInt64(2)
    if a < c:
        b = UInt64(1)
        a = UInt64(100)

    b *= b
    return a * b

@subroutine
def phi_in_equiv_class(y: UInt64, b: bool) -> UInt64:
    if b:
        tmp1 = y
        x = tmp1
    else:
        tmp2 = y
        x = tmp2
    x += 1
    return x


from algopy import Contract, UInt64, subroutine, urange

class MyContract(Contract):
    """My contract"""

    def approval_program(self) -> UInt64:
        a = UInt64(1) + 2
        b = UInt64(4) * 5

        # while a < UInt64(5):
        #     b = b + a
        #     a = a + 1

        for i in urange(5):
            b = b + a
            a = a + i
        return a + b

        # if a < b:
        #     if b < 2:
        #         b = 3 + UInt64(2)
        #         c = a + b
        #     else:
        #         b = 2 * a
        #         if ((3 * 4) + 2) * b:
        #             c = UInt64(2)
        #         else:
        #             return UInt64(3)
        # elif b == a:
        #     c = a * b
        # else:
        #     c = a - b
        # c = c + one_hundred(c)
        # return c

    def clear_state_program(self) -> UInt64:
        return one_hundred(UInt64(40))

@subroutine
def one_hundred(c: UInt64) -> UInt64:
    a = UInt64(25)
    b = UInt64(2)
    if a < c:
        b = UInt64(1)
        a = UInt64(100)

    b *= b
    return a * b


from algopy import (
    Account,
    ARC4Contract,
    Box,
    BoxMap,
    GlobalState,
    LocalState,
    Txn,
    arc4,
    subroutine,
)

class MyStruct(arc4.Struct):
    bar: arc4.UInt64
    baz: arc4.String

MyArray = arc4.DynamicArray[MyStruct]

class Contract(ARC4Contract):
    def __init__(self) -> None:
        self.glob = GlobalState(MyArray)
        self.loc = LocalState(MyArray)
        self.box = Box(MyArray)
        self.map = BoxMap(Account, MyArray)

    @arc4.baremethod(allow_actions=["OptIn"])
    def opt_in(self) -> None:
        self.glob.value = MyArray()
        self.box.value = MyArray()
        self.loc[Txn.sender] = MyArray()
        self.map[Txn.sender] = MyArray()

    @arc4.abimethod
    def append(self) -> None:
        struct = get_struct()
        self.glob.value.append(struct.copy())
        self.loc[Txn.sender].append(struct.copy())
        self.box.value.append(struct.copy())
        self.map[Txn.sender].append(struct.copy())

    @arc4.abimethod
    def modify(self) -> None:
        self.glob.value[0].baz = arc4.String("modified")
        self.loc[Txn.sender][0].baz = arc4.String("modified")
        self.box.value[0].baz = arc4.String("modified")
        self.map[Txn.sender][0].baz = arc4.String("modified")

    @arc4.abimethod
    def get(self) -> MyArray:
        a1 = self.glob.value.copy()
        a2 = self.loc[Txn.sender].copy()
        a3 = self.box.value.copy()
        a4 = self.map[Txn.sender].copy()

        assert a1 == a2, "expected local == global"
        assert a1 == a3, "expected box == global"
        assert a1 == a4, "expected map == global"
        return a1

@subroutine
def get_struct() -> MyStruct:
    return MyStruct(
        bar=arc4.UInt64(1),
        baz=arc4.String("baz"),
    )


# 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 append(
        self,
    ) -> None: ...

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

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


from algopy import (
    ARC4Contract,
    GlobalState,
    LocalState,
    StateTotals,
    Txn,
    UInt64,
    arc4,
)

class StateProxyContract(ARC4Contract, state_totals=StateTotals(global_uints=3)):
    def __init__(self) -> None:
        self.local1 = LocalState(UInt64, key="l1", description="l1 description")
        self.local2 = LocalState[UInt64](UInt64, key=b"l2", description="l2 description")
        self.global1 = GlobalState(UInt64, key="g1", description="g1 description")
        self.global2 = GlobalState[UInt64](UInt64(0), key=b"g2", description="g2 description")
        funky_town = (
            GlobalState(UInt64, key="funky")
            if Txn.num_app_args
            else GlobalState(UInt64, key="town")
        )
        funky_town.value = UInt64(123)

    @arc4.abimethod(allow_actions=["OptIn"], create="require")
    def create(self) -> None:
        self.global1.value = UInt64(1)
        self.local1[Txn.sender] = UInt64(2)
        self.local2[Txn.sender] = UInt64(3)


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

import algopy

class StateProxyContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod(allow_actions=['OptIn'], create='require')
    def create(
        self,
    ) -> None: ...


from algopy import Bytes, GlobalState, LocalState, StateTotals, UInt64, arc4

GLOBAL_UINTS = 3

class Contract(
    arc4.ARC4Contract,
    state_totals=StateTotals(local_bytes=1, global_uints=GLOBAL_UINTS),
):
    def __init__(self) -> None:
        self.local_one = LocalState(UInt64)
        self.global_one = GlobalState(Bytes)

    @arc4.baremethod(create="require")
    def create(self) -> None:
        pass


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

import algopy

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


from algopy import Contract, UInt64, subroutine

class BruteForceRotationSearch(Contract):
    def approval_program(self) -> bool:
        (
            a,
            b,
            c,
            d,
            e,
            f,
            g,
            h,
            i,
            j,
            k,
            l,  # noqa: E741
            m,
            n,
        ) = do_some_ops(UInt64(0), UInt64(0))

        assert a == 0
        assert b == 1
        assert c == 2
        assert d == 3
        assert e == 4
        assert f == 5
        assert g == 6
        assert h == 7
        assert i == 8
        assert j == 9
        assert k == 10
        assert l == 11
        assert m == 12
        assert n == 13
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def do_some_ops(
    a: UInt64, b: UInt64
) -> tuple[
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
    UInt64,
]:
    c = a + b
    return (
        c,
        c + 1,
        c + 2,
        c + 3,
        c + 4,
        c + 5,
        c + 6,
        c + 7,
        c + 8,
        c + 9,
        c + 10,
        c + 11,
        c + 12,
        c + 13,
    )


from algopy import Bytes, Contract

class MyContract(Contract):
    def approval_program(self) -> bool:
        assert Bytes(b"brown fox") in Bytes(b"The quick brown fox jumped over the lazy dog")
        assert b"brown fox" in Bytes(b"The quick brown fox jumped over the lazy dog")
        assert Bytes(b"red fox") not in Bytes(b"The quick brown fox jumped over the lazy dog")
        assert b"red fox" not in Bytes(b"The quick brown fox jumped over the lazy dog")

        return True

    def clear_state_program(self) -> bool:
        return True


import typing

from algopy import ARC4Contract, arc4

from test_cases.struct_by_name.mod import StructTwo as StructThree

class StructOne(typing.NamedTuple):
    x: arc4.UInt8
    y: arc4.UInt8

class StructTwo(typing.NamedTuple):
    x: arc4.UInt8
    y: arc4.UInt8

class DemoContract(ARC4Contract):
    """
    Verify that even though named tuples with different names but the same structure should be
    considered 'comparable' in the type system, they should be output separately when being
    interpreted as an arc4 Struct in an abi method
    """

    @arc4.abimethod()
    def get_one(self) -> StructOne:
        return StructOne(
            x=arc4.UInt8(1),
            y=arc4.UInt8(1),
        )

    @arc4.abimethod()
    def get_two(self) -> StructTwo:
        return StructTwo(
            x=arc4.UInt8(1),
            y=arc4.UInt8(1),
        )

    @arc4.abimethod()
    def get_three(self) -> StructThree:
        return StructThree(
            x=arc4.UInt8(1),
            y=arc4.UInt8(1),
        )

    @arc4.abimethod()
    def compare(self) -> bool:
        return self.get_one() == self.get_two() and self.get_two() == self.get_three()


import typing

from algopy import arc4

class StructTwo(typing.NamedTuple):
    x: arc4.UInt8
    y: arc4.UInt8


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

import algopy

class StructOne(algopy.arc4.Struct):
    x: algopy.arc4.UIntN[typing.Literal[8]]
    y: algopy.arc4.UIntN[typing.Literal[8]]

class StructTwo(algopy.arc4.Struct):
    x: algopy.arc4.UIntN[typing.Literal[8]]
    y: algopy.arc4.UIntN[typing.Literal[8]]

class test_cases_struct_by_name_mod_StructTwo(algopy.arc4.Struct):
    x: algopy.arc4.UIntN[typing.Literal[8]]
    y: algopy.arc4.UIntN[typing.Literal[8]]

class DemoContract(algopy.arc4.ARC4Client, typing.Protocol):
    """

        Verify that even though named tuples with different names but the same structure should be
        considered 'comparable' in the type system, they should be output separately when being
        interpreted as an arc4 Struct in an abi method

    """
    @algopy.arc4.abimethod
    def get_one(
        self,
    ) -> StructOne: ...

    @algopy.arc4.abimethod
    def get_two(
        self,
    ) -> StructTwo: ...

    @algopy.arc4.abimethod
    def get_three(
        self,
    ) -> test_cases_struct_by_name_mod_StructTwo: ...

    @algopy.arc4.abimethod
    def compare(
        self,
    ) -> algopy.arc4.Bool: ...


from algopy import BigUInt, Contract, Txn, UInt64, op, subroutine

class BigUIntContract(Contract):
    def approval_program(self) -> bool:
        one = BigUInt(1)
        compare_biguints(one, BigUInt(2))
        compare_biguint_vs_uint64(one, UInt64(2))
        compare_uint64_vs_biguint(UInt64(1), BigUInt(2))
        assert BigUInt(1 if Txn.num_app_args else 5) == 5, "constructor expressions supported"
        assert op.bsqrt(BigUInt(9)) == op.bsqrt(10)
        assert one == +one
        return True

    def clear_state_program(self) -> bool:
        assert BigUInt() == 0
        return True

@subroutine
def compare_biguints(one: BigUInt, two: BigUInt) -> None:
    assert one < two
    assert one <= two
    assert one == one  # noqa: PLR0124
    assert two > one
    assert two >= one
    assert one != two

@subroutine
def compare_biguint_vs_uint64(one: BigUInt, two: UInt64) -> None:
    assert one < two
    assert one <= two
    assert one == one  # noqa: PLR0124
    assert two > one
    assert two >= one
    assert one != two

@subroutine
def compare_uint64_vs_biguint(one: UInt64, two: BigUInt) -> None:
    assert one < two
    assert one <= two
    assert one == one  # noqa: PLR0124
    assert two > one
    assert two >= one
    assert one != two


from algopy import Bytes, Contract, Txn, UInt64, log, subroutine

class BytesContract(Contract):
    def approval_program(self) -> UInt64:
        base_64 = Bytes.from_base64("QmFzZSA2NCBlbmNvZGVk")
        assert base_64 == Bytes(b"Base 64 encoded")
        base_32 = Bytes.from_base32("IJQXGZJAGMZCAZLOMNXWIZLE")
        assert base_32 == Bytes(b"Base 32 encoded")
        base_16 = Bytes.from_hex("4261736520313620656E636F646564")
        assert base_16 == Bytes(b"Base 16 encoded")

        empty = Bytes(b"")
        assert base_64, "Non empty bytes should be Truthy"
        assert not empty, "Empty bytes should be Falsy"

        assert Bytes(b"a") + Bytes(b"b") == Bytes(b"ab")

        c = Bytes(b"c")
        c += b"d"
        assert c == Bytes(b"cd")

        abc = Bytes(b"abc")
        assert abc[0] == b"a"

        assert abc[1:] == b"bc"
        assert abc[1:1] == b""
        assert abc[:1] == b"a"
        assert abc[:-1] == b"ab"
        assert abc[-2:] == b"bc"
        assert abc[-2:-1] == b"b"
        assert Bytes(b"1234567")[1:-1] == b"23456"
        assert abc[-10:10] == b"abc"

        true = Bytes(b"1")
        false = Bytes(b"")

        x = (true and true) == true
        assert x
        assert (true and true) == true
        assert (true and false) == false
        assert (false and true) == false
        assert (false and false) == false
        assert (true or true) == true
        assert (true or false) == true
        assert (false or true) == true
        assert (false or false) == false

        a, b, c, d = (
            Bytes.from_hex("00"),
            Bytes.from_hex("0F"),
            Bytes.from_hex("F0"),
            Bytes.from_hex("FF"),
        )

        assert a & b == a
        assert b | c == d
        assert b ^ d == c

        y = a
        y &= d
        assert y == a
        y |= d
        assert y == d
        y ^= c
        assert y == b

        check_slicing_with_uint64(abc)
        check_end_before_start_slicing(abc)

        assert (
            Bytes(b"args" if Txn.num_app_args else b"no args") == b"no args"
        ), "constructor expressions supported"

        return UInt64(1)

    def clear_state_program(self) -> bool:
        return True

@subroutine
def check_slicing_with_uint64(abc: Bytes) -> None:
    one = UInt64(1)
    ten = UInt64(10)
    assert abc[one:] == b"bc"
    assert abc[one:one] == b""
    assert abc[:one] == b"a"
    assert one_to_seven()[one:-1] == b"23456"
    assert abc[UInt64(0) : ten] == b"abc"

@subroutine
def check_end_before_start_slicing(abc: Bytes) -> None:
    assert abc[10:1] == b""
    assert abc[-10:-12] == b""
    one = UInt64(1)
    ten = UInt64(10)
    assert abc[ten:one] == b""

@subroutine
def one_to_seven() -> Bytes:
    log("one_to_seven called")
    return Bytes(b"1234567")


from algopy import Contract, String, Txn

class StringContract(Contract):
    def approval_program(self) -> bool:
        empty = String()
        assert not empty, "Empty bytes should be False"
        non_empty = String(" ")
        assert non_empty, "Non-empty bytes should be True"

        assert String("a") + "b" == "ab"
        assert String("a") + "b" == String("ab")
        assert "a" + String("b") == String("ab")

        assert empty != non_empty

        c = String("c")
        c += "d"
        c += String("e")
        assert c == "cde"

        assert "brown fox" in String("The quick brown fox jumped over the lazy dog")
        assert String("red fox") not in String("The quick brown fox jumped over the lazy dog")

        alpha = String("abcdefg")
        assert alpha.startswith("")
        assert alpha.startswith("a")
        assert alpha.startswith("ab")
        assert not alpha.startswith("b")
        assert alpha.startswith(alpha)
        assert not alpha.startswith(alpha + "!")

        assert alpha.endswith("")
        assert alpha.endswith("g")
        assert alpha.endswith("fg")
        assert not alpha.endswith("f")
        assert alpha.endswith(alpha)
        assert not alpha.endswith("!" + alpha)

        d, e, f = String("d"), String("e"), String("f")
        assert String(".").join((d, e, f)) == "d.e.f"
        assert String(".").join((d, "e", f)) == "d.e.f"
        assert String(".").join(("d", "e", "f")) == "d.e.f"
        assert String(".").join(tuple("def")) == "d.e.f"
        assert String("").join((d, e, f)) == "def"
        assert String(".").join((d,)) == "d"
        assert String("").join((d,)) == "d"
        assert (
            String("args" if Txn.num_app_args else "no args") == "no args"
        ), "constructor expressions supported"

        return True

    def clear_state_program(self) -> bool:
        return True


from algopy import Contract, Txn, UInt64, op

class Uint64Contract(Contract):
    def approval_program(self) -> UInt64:
        zero = UInt64(0)
        one = UInt64(1)
        two = UInt64(2)
        five = UInt64(5)
        three = UInt64(3)
        sixty = UInt64(60)

        assert one, "Any non-zero number should be Truthy"
        assert not zero, "Zero should beFalsy"

        assert one < five
        assert five > one
        assert one <= one  # noqa: PLR0124
        assert five >= five  # noqa: PLR0124

        assert one + five == 6

        c = five
        c += sixty
        assert c == 65

        assert sixty - five == 55
        c -= five
        assert c == 60

        assert sixty // five == 12
        c //= five

        assert c == 12

        assert five * sixty == 300

        assert five**three == 125

        c **= 2

        assert c == 144

        assert one << two == 4
        c >>= 6
        assert c == 2
        c <<= 6
        assert c == 128
        assert five >> three == 0

        assert ~one == 0xFFFFFFFFFFFFFFFE

        true = UInt64(1)
        false = UInt64(0)
        assert (true and true) == true
        assert (true and false) == false
        assert (false and true) == false
        assert (false and false) == false
        assert (true or true) == true
        assert (true or false) == true
        assert (false or true) == true
        assert (false or false) == false

        assert one & five == one
        assert sixty | five == 61
        assert sixty ^ five == 57

        y = UInt64(0b11111110)
        y &= UInt64(0b00011111)
        assert y == 0b00011110
        y |= 0b00110110
        assert y == 0b00111110
        y ^= 0b11111111
        assert y == 0b11000001

        assert op.sqrt(UInt64(17)) == op.sqrt(16)

        assert one == +one

        assert UInt64(1 if Txn.num_app_args else 5) == 5, "constructor expressions supported"

        return UInt64(1)

    def clear_state_program(self) -> bool:
        assert UInt64() == 0
        assert UInt64(False) == 0
        return True


from algopy import BigUInt, Bytes, TemplateVar, UInt64, arc4
from algopy.arc4 import UInt512

class TemplateVariablesContract(arc4.ARC4Contract):
    @arc4.abimethod()
    def get_bytes(self) -> Bytes:
        return TemplateVar[Bytes]("SOME_BYTES")

    @arc4.abimethod()
    def get_big_uint(self) -> UInt512:
        x = TemplateVar[BigUInt]("SOME_BIG_UINT")
        return UInt512(x)

    @arc4.baremethod(allow_actions=["UpdateApplication"])
    def on_update(self) -> None:
        assert TemplateVar[bool]("UPDATABLE")

    @arc4.baremethod(allow_actions=["DeleteApplication"])
    def on_delete(self) -> None:
        assert TemplateVar[UInt64]("DELETABLE")


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

import algopy

class TemplateVariablesContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def get_bytes(
        self,
    ) -> algopy.arc4.DynamicBytes: ...

    @algopy.arc4.abimethod
    def get_big_uint(
        self,
    ) -> algopy.arc4.BigUIntN[typing.Literal[512]]: ...


from algopy import Bytes, Contract, op, subroutine

class MyContract(Contract):
    def approval_program(self) -> bool:
        a = op.Txn.application_args(0)
        b = op.Txn.application_args(1)
        c = op.Txn.application_args(2)
        d = op.Txn.application_args(3)

        assert (a != c) or (b != d)
        assert four_args(a, b, c, d)
        two_args(a, b)
        two_args(c, d)

        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def four_args(a: Bytes, b: Bytes, c: Bytes, d: Bytes) -> bool:
    return (a + b + c + d).length > 0

@subroutine
def two_args(a: Bytes, b: Bytes) -> None:
    assert a + b


from algopy import (
    Bytes,
    arc4,
    gtxn,
    op,
    subroutine,
    uenumerate,
)

class TransactionContract(arc4.ARC4Contract):
    @arc4.abimethod(create="require")
    def create(self) -> None:
        pass

    @subroutine
    def _common_checks(self, txn: gtxn.TransactionBase) -> None:
        assert txn.txn_id, "txn_id"
        assert txn.sender == op.Global.creator_address, "sender"
        assert txn.fee, "fee"
        assert txn.type, "type"
        assert txn.type_bytes, "type_bytes"
        assert txn.note == Bytes(b""), "note"
        assert txn.group_index == 0, "group_index"
        assert txn.first_valid, "first_valid"
        # assert txn.first_valid_time, "first_valid_time" # this value can be flaky in tests
        assert txn.last_valid, "last_valid"
        assert txn.lease, "lease"
        assert txn.rekey_to == op.Global.zero_address, "rekey_to"

    @arc4.abimethod
    def pay(self, txn: gtxn.PaymentTransaction) -> None:
        self._common_checks(txn)
        assert (
            txn.receiver == op.Global.current_application_address
        ), "Payment should be for this app"
        assert txn.amount > 1000, "Payment should be for >1000 micro algos"
        assert txn.close_remainder_to == op.Global.zero_address, "close_remainder_to"

    @arc4.abimethod
    def key(self, txn: gtxn.KeyRegistrationTransaction) -> None:
        self._common_checks(txn)
        assert txn.vote_key, "vote_key"
        assert txn.selection_key, "selection_key"
        assert txn.vote_key_dilution, "vote_key_dilution"
        assert txn.vote_first, "vote_first"
        assert txn.vote_last, "vote_last"
        assert txn.non_participation, "non_participation"
        assert txn.state_proof_key, "state_proof_key"

    @arc4.abimethod
    def asset_config(self, txn: gtxn.AssetConfigTransaction) -> None:
        self._common_checks(txn)

        assert txn.config_asset, "config_asset"
        assert txn.total, "total"
        assert txn.decimals, "decimals"
        assert txn.default_frozen, "default_frozen"
        assert txn.unit_name, "unit_name"
        assert txn.asset_name, "asset_name"
        assert txn.url, "url"
        assert txn.metadata_hash, "metadata_hash"
        assert txn.manager, "manager"
        assert txn.reserve, "reserve"
        assert txn.freeze, "freeze"
        assert txn.clawback, "clawback"

    @arc4.abimethod
    def asset_transfer(self, txn: gtxn.AssetTransferTransaction) -> None:
        self._common_checks(txn)
        assert txn.xfer_asset, "xfer_asset"
        assert txn.asset_amount, "asset_amount"
        assert txn.asset_sender, "asset_sender"
        assert txn.asset_receiver, "asset_receiver"
        assert txn.asset_close_to, "asset_close_to"

    @arc4.abimethod
    def asset_freeze(self, txn: gtxn.AssetFreezeTransaction) -> None:
        self._common_checks(txn)

        assert txn.freeze_asset, "freeze_asset"
        assert txn.freeze_account, "freeze_account"
        assert txn.frozen, "frozen"

    @arc4.abimethod
    def application_call(self, txn: gtxn.ApplicationCallTransaction) -> None:
        self._common_checks(txn)
        assert txn.app_id, "app_id"
        assert txn.on_completion, "on_completion"
        assert txn.num_app_args, "num_app_args"
        assert txn.num_accounts, "num_accounts"
        assert txn.approval_program, "approval_program"
        assert txn.clear_state_program, "clear_state_program"
        assert txn.num_assets, "num_assets"
        assert txn.num_apps, "num_apps"
        assert txn.global_num_uint, "global_num_uint"
        assert txn.global_num_bytes, "global_num_bytes"
        assert txn.local_num_uint, "local_num_uint"
        assert txn.local_num_bytes, "local_num_bytes"
        assert txn.extra_program_pages, "extra_program_pages"
        assert txn.last_log, "last_log"
        assert txn.num_approval_program_pages, "num_approval_program_pages"
        assert txn.num_clear_state_program_pages, "num_clear_state_program_pages"
        assert txn.app_args(0), "app_args(0)"
        assert txn.accounts(0), "accounts(0)"
        assert txn.assets(0), "assets(0)"
        assert txn.apps(0), "apps(0)"
        assert txn.approval_program_pages(0), "approval_program_pages(0)"
        assert txn.clear_state_program_pages(0), "clear_state_program_pages(0)"

    @arc4.abimethod
    def multiple_txns(
        self,
        txn1: gtxn.ApplicationCallTransaction,
        txn2: gtxn.ApplicationCallTransaction,
        txn3: gtxn.ApplicationCallTransaction,
    ) -> None:
        for index, app in uenumerate((txn1, txn2, txn3)):
            assert app.group_index == index

    @arc4.abimethod
    def any_txn(
        self,
        txn1: gtxn.Transaction,
        txn2: gtxn.Transaction,
        txn3: gtxn.Transaction,
    ) -> None:
        for index, txn in uenumerate((txn1, txn2, txn3)):
            assert txn.group_index == index

    @arc4.abimethod
    def group_init(
        self,
        txn1: gtxn.Transaction,
        txn2: gtxn.Transaction,
        txn3: gtxn.Transaction,
    ) -> None:
        for txn in (txn1, txn2, txn3):
            txn_from_index = gtxn.Transaction(txn.group_index)
            assert txn.txn_id == txn_from_index.txn_id
        assert txn1.txn_id == gtxn.Transaction(0).txn_id


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

import algopy

class TransactionContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod(create='require')
    def create(
        self,
    ) -> None: ...

    @algopy.arc4.abimethod
    def pay(
        self,
        txn: algopy.gtxn.PaymentTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def key(
        self,
        txn: algopy.gtxn.KeyRegistrationTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def asset_config(
        self,
        txn: algopy.gtxn.AssetConfigTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def asset_transfer(
        self,
        txn: algopy.gtxn.AssetTransferTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def asset_freeze(
        self,
        txn: algopy.gtxn.AssetFreezeTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def application_call(
        self,
        txn: algopy.gtxn.ApplicationCallTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def multiple_txns(
        self,
        txn1: algopy.gtxn.ApplicationCallTransaction,
        txn2: algopy.gtxn.ApplicationCallTransaction,
        txn3: algopy.gtxn.ApplicationCallTransaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def any_txn(
        self,
        txn1: algopy.gtxn.Transaction,
        txn2: algopy.gtxn.Transaction,
        txn3: algopy.gtxn.Transaction,
    ) -> None: ...

    @algopy.arc4.abimethod
    def group_init(
        self,
        txn1: algopy.gtxn.Transaction,
        txn2: algopy.gtxn.Transaction,
        txn3: algopy.gtxn.Transaction,
    ) -> None: ...


import typing

from algopy import ARC4Contract, Bytes, String, UInt64, arc4, op, subroutine

class Child(typing.NamedTuple):
    a: UInt64
    b: Bytes
    c: String

class Parent(typing.NamedTuple):
    foo: UInt64
    foo_arc: arc4.UInt64
    child: Child

class NestedTuples(ARC4Contract):
    def __init__(self) -> None:
        self.build_nested_call_count = UInt64(0)

    @arc4.abimethod()
    def run_tests(self) -> bool:
        x = (String("Hi"), String("There"))
        assert test_swap(x) == (String("There"), String("Hi"))
        y = (UInt64(1), x)
        z = (UInt64(0), UInt64(2), y)
        z2 = z[2]
        z2_1 = z2[1]
        _x, z2_1_1 = z2_1
        assert z2_1_1 == "There"

        (a, b, (c, d, (e,))) = test_rearrange(x[0], _b=x[1], args=z)
        assert (a, b) == (String("Hi"), UInt64(0))
        assert (c, d) == (UInt64(2), UInt64(1))
        assert e == String("There")

        test_intrinsics(UInt64(1), UInt64(2))
        test_nested_slicing()
        test_nested_singles(UInt64(1), reassign=True)
        test_nested_singles(UInt64(1), reassign=False)
        test_nested_mutation()

        assert z[2] == y

        test_nested_iteration()

        self.test_single_evaluation_nested()
        return True

    @arc4.abimethod()
    def nested_tuple_params(
        self, args: tuple[String, tuple[Bytes, tuple[UInt64]]]
    ) -> tuple[Bytes, tuple[String, UInt64]]:
        (s, (b, (u,))) = args
        return b, (s, u)

    @arc4.abimethod()
    def named_tuple(self, args: Child) -> Child:
        a, b, c = args
        return Child(a, b, c)

    @arc4.abimethod()
    def nested_named_tuple_params(self, args: Parent) -> Parent:
        foo, foo_arc, (a, b, c) = args
        return Parent(foo, foo_arc, Child(a, b, c))

    @subroutine
    def build_nested(self) -> tuple[tuple[String, UInt64], Bytes]:
        self.build_nested_call_count += 1
        return (String("hi"), UInt64(1)), Bytes(b"hmmm")

    @subroutine
    def test_single_evaluation_nested(self) -> None:
        self.build_nested_call_count = UInt64(0)
        result = self.build_nested() or self.build_nested()
        assert result[0][0] == "hi"
        assert self.build_nested_call_count == 1

@subroutine
def test_rearrange(
    _a: String, args: tuple[UInt64, UInt64, tuple[UInt64, tuple[String, String]]], _b: String
) -> tuple[String, UInt64, tuple[UInt64, UInt64, tuple[String]]]:
    (a, b, (c, (d, e))) = args

    return d, a, (b, c, (e,))

@subroutine
def test_swap(args: tuple[String, String]) -> tuple[String, String]:
    (a, b) = args
    return b, a

@subroutine
def test_intrinsics(num1: UInt64, num2: UInt64) -> None:
    nt = (UInt64(1), op.addw(num1, num2), UInt64(42))
    assert nt[0] == 1
    assert nt[-1] == 42
    assert nt[1] == (0, num1 + num2)  # type: ignore[comparison-overlap]
    assert nt[1][:1] == (0,)  # type: ignore[comparison-overlap]
    assert nt[1][1:] == (num1 + num2,)
    ((x, y),) = nt[1:2]
    assert x == 0
    assert y == num1 + num2

@subroutine
def test_nested_slicing() -> None:
    nt = (
        UInt64(1),
        UInt64(2),
        (
            UInt64(3),
            (
                String("a"),
                String("b"),
            ),
            UInt64(4),
        ),
        UInt64(5),
        UInt64(6),
    )
    (a, b, c) = nt[1:4]
    assert b[-1] == 4
    assert ((a, c),) == ((2, 5),)  # type: ignore[comparison-overlap]
    assert b[1][:] == ("a", "b")  # type: ignore[comparison-overlap]

@subroutine
def test_nested_singles(one: UInt64, *, reassign: bool) -> None:
    s = (
        (UInt64(0),),
        (one,),
        (UInt64(2),),
    )
    assert s[0][0] == 0
    assert s[0] == (0,)  # type: ignore[comparison-overlap]
    assert s[1][0] == 1
    assert s[1] == (one,)
    assert s[2][0] == 2
    assert s[2] == (2,)  # type: ignore[comparison-overlap]
    t = s[1]
    if reassign:
        s = (
            (UInt64(3),),
            (UInt64(4),),
            (UInt64(5),),
        )
    assert s[0][0] == (3 if reassign else 0)
    (tmp,) = s[2]
    assert tmp == (5 if reassign else 2)
    assert t == (one,)

    s0, (s1,), s2 = s
    s1 += one
    assert s1 == (5 if reassign else 2)
    assert s[1][0] == (4 if reassign else 1)

@subroutine
def test_nested_mutation() -> None:
    x = (
        (
            arc4.DynamicArray(
                arc4.UInt64(0),
            ),
        ),
    )
    x[0][0].append(arc4.UInt64(1))
    assert x[0][0].length == 2

@subroutine
def test_nested_iteration() -> None:
    x = UInt64(1)
    y = UInt64(2)
    total = UInt64(0)

    for t in ((x, y), (y, x), (x, x), (y, y)):
        a, b = t
        total += a + b

    for a, b in ((x, y), (y, x), (x, x), (y, y)):
        total += a + b

    assert total // 8 == 3


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

import algopy

class Child(algopy.arc4.Struct):
    a: algopy.arc4.UIntN[typing.Literal[64]]
    b: algopy.arc4.DynamicBytes
    c: algopy.arc4.String

class Parent(algopy.arc4.Struct):
    foo: algopy.arc4.UIntN[typing.Literal[64]]
    foo_arc: algopy.arc4.UIntN[typing.Literal[64]]
    child: Child

class NestedTuples(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def run_tests(
        self,
    ) -> algopy.arc4.Bool: ...

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

    @algopy.arc4.abimethod
    def named_tuple(
        self,
        args: Child,
    ) -> Child: ...

    @algopy.arc4.abimethod
    def nested_named_tuple_params(
        self,
        args: Parent,
    ) -> Parent: ...


from algopy import Contract, String, UInt64, log, subroutine

class TupleComparisons(Contract):
    def approval_program(self) -> bool:
        test_tuple_cmp_eval()
        test_tuple_cmp_empty()
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def test_tuple_cmp_eval() -> None:
    assert (
        String("abc"),
        log_and_return(UInt64(42)),
    ) != (String("abc"),)  # type: ignore[comparison-overlap]
    tmp = (
        String("abc"),
        log_and_return(UInt64(43)),
    ) == (String("abc"),)  # type: ignore[comparison-overlap]
    assert not tmp

    assert (String("abc"),) != (
        String("abc"),
        log_and_return(UInt64(44)),
    )  # type: ignore[comparison-overlap]
    tmp = (String("abc"),) == (
        String("abc"),
        log_and_return(UInt64(45)),
    )  # type: ignore[comparison-overlap]
    assert not tmp

    assert (UInt64(1), UInt64(2)) != (UInt64(3), log_and_return(UInt64(46)))
    tmp = (UInt64(1), UInt64(2)) == (UInt64(3), log_and_return(UInt64(47)))
    assert not tmp

@subroutine
def test_tuple_cmp_empty() -> None:
    assert () == ()
    tmp = () != ()
    assert not tmp

    assert () != ("a",)  # type: ignore[comparison-overlap]
    tmp = () == ("a",)  # type: ignore[comparison-overlap]
    assert not tmp

@subroutine
def log_and_return(val: UInt64) -> UInt64:
    log(val)
    return val


from algopy import Bytes, Contract, UInt64, log, op, subroutine, urange

class TupleSupport(Contract):
    def __init__(self) -> None:
        self.state = UInt64(0)

    def approval_program(self) -> UInt64:
        total = add_three_values((UInt64(101), UInt64(102), UInt64(103)))
        log(total)
        (a, b) = (UInt64(1), UInt64(2))
        (did_overflow, self.state) = op.addw(a, b)
        assert not did_overflow, "overflow!"
        ab = (a, b)
        assert ab[-1] == ab[1]
        result = op.addw(a, b)
        assert not result[0], "overflow!"
        c = d = UInt64(3)
        ((a2, b2), cd, ab2) = (ab, (c, d), ab)
        if a == b:
            tup = ab2
        else:
            tup = cd
        assert a2 == a
        assert b2 == b
        assert cd[0] == tup[0]
        assert cd[1] == tup[1]

        (m, n) = get_tuple(a, b) or get_tuple(b, a)
        assert (m, n) == (a, b)

        # assert ab2 == ab # TODO: support ths
        # foobar = ((a, b), (c, d)) # TODO: negative test for this
        log(bytes_combine((Bytes(b"Hello, "), Bytes(b"world!"))))
        max_uint64 = UInt64(2**64 - 1)
        hi, mid, lo = addw2(op.addw(max_uint64, max_uint64), op.addw(a, b))
        log(hi)
        log(mid)
        log(lo)
        log(bytes_multiply((Bytes(b"na"), UInt64(5))))
        test_tuple_swap(zero=UInt64(0))
        slicing(
            (
                UInt64(1),
                UInt64(2),
                UInt64(3),
                UInt64(4),
                UInt64(5),
                UInt64(6),
                UInt64(7),
                UInt64(8),
            )
        )
        bin_ops()
        if non_empty_tuple():
            log("not empty")
        if (get_uint_with_side_effect(),):  # noqa: F634
            log("not empty2")
        single_tuple()

        x = tuple[UInt64, Bytes]((UInt64(), Bytes()))
        assert x[0] == 0
        assert x[1] == b""
        test_empty()
        return a + b

    def clear_state_program(self) -> UInt64:
        return UInt64(0)

@subroutine
def get_tuple(a: UInt64, b: UInt64) -> tuple[UInt64, UInt64]:
    return a, b

@subroutine
def single_tuple() -> None:
    tup = (UInt64(1),)
    assert tup[0] == 1

@subroutine
def get_uint_with_side_effect() -> UInt64:
    log("get_uint_with_side_effect called")
    return UInt64(4)

@subroutine
def non_empty_tuple() -> tuple[UInt64, UInt64]:
    log("non_empty_tuple called")
    return UInt64(4), UInt64(2)

@subroutine
def bin_ops() -> None:
    a = (UInt64(1),) * 3
    assert a[0] == 1
    assert a[1] == 1
    assert a[2] == 1

    tup = (UInt64(1), UInt64(2))
    b = tup * 3
    assert b[0] == 1
    assert b[1] == 2
    assert b[2] == 1
    assert b[3] == 2
    assert b[4] == 1
    assert b[5] == 2

    c = (UInt64(1),) + (Bytes(b"2"), UInt64(3))  # noqa: RUF005
    assert c[0] == 1
    assert c[1] == b"2"
    assert c[2] == 3

    d = tup + tup
    assert d[0] == 1
    assert d[1] == 2
    assert d[2] == 1
    assert d[3] == 2

@subroutine
def bytes_combine(arg: tuple[Bytes, Bytes]) -> Bytes:
    a, b = arg
    result = a + b
    return result

@subroutine
def bytes_multiply(arg: tuple[Bytes, UInt64]) -> Bytes:
    b, count = arg
    result = Bytes()
    for _i in urange(count):
        result += b
    return result

@subroutine
def add_three_values(values: tuple[UInt64, UInt64, UInt64]) -> UInt64:
    total = UInt64(0)
    for value in values:
        total += value

    return total

@subroutine
def addw2(a: tuple[UInt64, UInt64], b: tuple[UInt64, UInt64]) -> tuple[UInt64, UInt64, UInt64]:
    a_hi, a_lo = a
    b_hi, b_lo = b
    lo_carry, c_lo = op.addw(a_lo, b_lo)
    hi_carry1, c_mid = op.addw(a_hi, b_hi)
    hi_carry2, c_mid = op.addw(c_mid, lo_carry)
    did_overflow, c_hi = op.addw(hi_carry1, hi_carry2)
    assert not did_overflow, "is such a thing even possible? 👽"
    return c_hi, c_mid, c_lo

@subroutine
def test_tuple_swap(zero: UInt64) -> None:
    a = zero + 1
    b = zero + 2
    (a, b) = (b, a)
    assert a == 2, "a should be two"
    assert b == 1, "b should be one"

@subroutine
def slicing(values: tuple[UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64, UInt64]) -> None:
    one_to_three = values[0:3]
    assert add_three_values(one_to_three) == values[0] + values[1] + values[2]

    assert one_to_three[-2:-1][0] == one_to_three[1]

    assert one_to_three == one_to_three[:]

@subroutine
def test_empty() -> None:
    empty = ()
    empty2 = empty
    () = empty
    () = ()
    assert not empty
    assert not empty2


import typing

from algopy import (
    Account,
    Application,
    ARC4Contract,
    Asset,
    BigUInt,
    Bytes,
    String,
    Txn,
    UInt64,
    arc4,
    log,
)

LOG_METHOD_NAME = "log"

class LogMessage(typing.NamedTuple):
    level: UInt64
    message: String

class Logger(ARC4Contract):
    @arc4.abimethod
    def is_a_b(self, a: Bytes, b: Bytes) -> None:
        assert a == b"a", "a is not a"
        assert b == b"b", "b is not b"

    @arc4.abimethod
    def echo(self, value: arc4.String) -> arc4.String:
        return "echo: " + value

    @arc4.abimethod
    def no_args(self) -> UInt64:
        return UInt64(42)

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_uint64(self, value: arc4.UInt64) -> None:
        log(value)

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_uint512(self, value: arc4.UInt512) -> None:
        log(value)

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_string(self, value: arc4.String) -> None:
        log(value.native)  # decode to remove header

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_bool(self, value: arc4.Bool) -> None:
        log(Bytes(b"True") if value.native else Bytes(b"False"))

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_bytes(self, value: arc4.DynamicBytes) -> None:
        log(value.native)

    @arc4.abimethod(name=LOG_METHOD_NAME)
    def log_asset_account_app(self, asset: Asset, account: Account, app: Application) -> None:
        log(asset.name, account.bytes, app.address)

    @arc4.abimethod
    def echo_native_string(self, value: String) -> String:
        return "echo: " + value

    @arc4.abimethod
    def echo_native_bytes(self, value: Bytes) -> Bytes:
        return b"echo: " + value

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

    @arc4.abimethod
    def echo_native_biguint(self, value: BigUInt) -> BigUInt:
        return value + 1

    @arc4.abimethod
    def echo_native_tuple(
        self, s: String, b: Bytes, u: UInt64, bu: BigUInt
    ) -> tuple[String, Bytes, UInt64, BigUInt]:
        return "echo: " + s, b"echo: " + b, u + 1, bu + 1

    @arc4.abimethod
    def echo_nested_tuple(
        self, tuple_of_tuples: tuple[tuple[String, arc4.String], tuple[UInt64, arc4.UInt64, Bytes]]
    ) -> tuple[tuple[String, arc4.String], tuple[UInt64, arc4.UInt64, Bytes]]:
        (string, arc4_string), (u64, arc4_u64, bytez) = tuple_of_tuples
        return ("echo: " + string, "echo: " + arc4_string), (
            u64 + 1,
            arc4.UInt64(arc4_u64.native + 1),
            b"echo: " + bytez,
        )

    @arc4.abimethod
    def return_args_after_14th(
        self,
        _a1: arc4.UInt64,
        _a2: arc4.UInt64,
        _a3: arc4.UInt64,
        _a4: arc4.UInt64,
        _a5: arc4.UInt64,
        _a6: arc4.UInt64,
        _a7: arc4.UInt64,
        _a8: arc4.UInt64,
        _a9: arc4.UInt64,
        _a10: arc4.UInt64,
        _a11: arc4.UInt64,
        _a12: arc4.UInt64,
        _a13: arc4.UInt64,
        _a14: arc4.UInt64,
        a15: arc4.UInt8,
        a16: arc4.UInt8,
        a17: arc4.UInt8,
        a18: arc4.UInt8,
        a19: arc4.Tuple[arc4.UInt8, arc4.UInt8, arc4.UInt8, arc4.UInt8],
        a20: arc4.UInt8,
    ) -> arc4.DynamicBytes:
        last_arg = arc4.Tuple((a15, a16, a17, a18, a19, a20))
        assert Txn.application_args(15) == last_arg.bytes
        return arc4.DynamicBytes(last_arg.bytes)

    @arc4.abimethod
    def logs_are_equal(self, log_1: LogMessage, log_2: LogMessage) -> bool:
        return log_1 == log_2

class LoggerClient(arc4.ARC4Client, typing.Protocol):
    @arc4.abimethod
    def echo(self, value: arc4.String) -> arc4.String: ...


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

import algopy

class Greeter(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def test_is_a_b(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_method_selector_kinds(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_method_overload(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_arg_conversion(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_15plus_args(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_void(
        self,
        app: algopy.Application,
    ) -> None: ...

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

    @algopy.arc4.abimethod
    def test_native_string(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_native_bytes(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_native_uint64(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_native_biguint(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_native_tuple(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_native_tuple_method_ref(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_nested_tuples(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_no_args(
        self,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_named_tuples(
        self,
        app: algopy.Application,
    ) -> None: ...


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

import algopy

class LogMessage(algopy.arc4.Struct):
    level: algopy.arc4.UIntN[typing.Literal[64]]
    message: algopy.arc4.String

class Logger(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def is_a_b(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
    ) -> None: ...

    @algopy.arc4.abimethod
    def echo(
        self,
        value: algopy.arc4.String,
    ) -> algopy.arc4.String: ...

    @algopy.arc4.abimethod
    def no_args(
        self,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod
    def log(
        self,
        value: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> None: ...

    @algopy.arc4.abimethod(name='log')
    def log2(
        self,
        value: algopy.arc4.BigUIntN[typing.Literal[512]],
    ) -> None: ...

    @algopy.arc4.abimethod(name='log')
    def log3(
        self,
        value: algopy.arc4.String,
    ) -> None: ...

    @algopy.arc4.abimethod(name='log')
    def log4(
        self,
        value: algopy.arc4.Bool,
    ) -> None: ...

    @algopy.arc4.abimethod(name='log')
    def log5(
        self,
        value: algopy.arc4.DynamicBytes,
    ) -> None: ...

    @algopy.arc4.abimethod(name='log')
    def log6(
        self,
        asset: algopy.Asset,
        account: algopy.Account,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def echo_native_string(
        self,
        value: algopy.arc4.String,
    ) -> algopy.arc4.String: ...

    @algopy.arc4.abimethod
    def echo_native_bytes(
        self,
        value: algopy.arc4.DynamicBytes,
    ) -> algopy.arc4.DynamicBytes: ...

    @algopy.arc4.abimethod
    def echo_native_uint64(
        self,
        value: algopy.arc4.UIntN[typing.Literal[64]],
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod
    def echo_native_biguint(
        self,
        value: algopy.arc4.BigUIntN[typing.Literal[512]],
    ) -> algopy.arc4.BigUIntN[typing.Literal[512]]: ...

    @algopy.arc4.abimethod
    def echo_native_tuple(
        self,
        s: algopy.arc4.String,
        b: algopy.arc4.DynamicBytes,
        u: algopy.arc4.UIntN[typing.Literal[64]],
        bu: algopy.arc4.BigUIntN[typing.Literal[512]],
    ) -> algopy.arc4.Tuple[algopy.arc4.String, algopy.arc4.DynamicBytes, algopy.arc4.UIntN[typing.Literal[64]], algopy.arc4.BigUIntN[typing.Literal[512]]]: ...

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

    @algopy.arc4.abimethod
    def return_args_after_14th(
        self,
        _a1: algopy.arc4.UIntN[typing.Literal[64]],
        _a2: algopy.arc4.UIntN[typing.Literal[64]],
        _a3: algopy.arc4.UIntN[typing.Literal[64]],
        _a4: algopy.arc4.UIntN[typing.Literal[64]],
        _a5: algopy.arc4.UIntN[typing.Literal[64]],
        _a6: algopy.arc4.UIntN[typing.Literal[64]],
        _a7: algopy.arc4.UIntN[typing.Literal[64]],
        _a8: algopy.arc4.UIntN[typing.Literal[64]],
        _a9: algopy.arc4.UIntN[typing.Literal[64]],
        _a10: algopy.arc4.UIntN[typing.Literal[64]],
        _a11: algopy.arc4.UIntN[typing.Literal[64]],
        _a12: algopy.arc4.UIntN[typing.Literal[64]],
        _a13: algopy.arc4.UIntN[typing.Literal[64]],
        _a14: algopy.arc4.UIntN[typing.Literal[64]],
        a15: algopy.arc4.UIntN[typing.Literal[8]],
        a16: algopy.arc4.UIntN[typing.Literal[8]],
        a17: algopy.arc4.UIntN[typing.Literal[8]],
        a18: algopy.arc4.UIntN[typing.Literal[8]],
        a19: algopy.arc4.Tuple[algopy.arc4.UIntN[typing.Literal[8]], algopy.arc4.UIntN[typing.Literal[8]], algopy.arc4.UIntN[typing.Literal[8]], algopy.arc4.UIntN[typing.Literal[8]]],
        a20: algopy.arc4.UIntN[typing.Literal[8]],
    ) -> algopy.arc4.DynamicBytes: ...

    @algopy.arc4.abimethod
    def logs_are_equal(
        self,
        log_1: LogMessage,
        log_2: LogMessage,
    ) -> algopy.arc4.Bool: ...


from algopy import (
    Application,
    ARC4Contract,
    Asset,
    BigUInt,
    Bytes,
    Global,
    String,
    UInt64,
    arc4,
    op,
)

from test_cases.typed_abi_call.logger import LOG_METHOD_NAME, Logger, LoggerClient, LogMessage

class Greeter(ARC4Contract):
    @arc4.abimethod
    def test_is_a_b(self, a: Bytes, b: Bytes, app: Application) -> None:
        arc4.abi_call(
            "is_a_b(byte[],byte[])void",
            a,
            b,
            app_id=app,
        )

    @arc4.abimethod()
    def test_method_selector_kinds(self, app: Application) -> None:
        result, _txn = arc4.abi_call(Logger.echo, arc4.String("test1"), app_id=app)
        assert result == "echo: test1"
        result, _txn = arc4.abi_call(LoggerClient.echo, "test2", app_id=app)
        assert result == "echo: test2"
        result, _txn = arc4.abi_call[arc4.String]("echo", "test3", app_id=app)
        assert result == "echo: test3"
        result, _txn = arc4.abi_call[arc4.String]("echo(string)", "test4", app_id=app)
        assert result == "echo: test4"
        result, _txn = arc4.abi_call[arc4.String]("echo(string)string", "test5", app_id=app)
        assert result == "echo: test5"

    @arc4.abimethod()
    def test_method_overload(self, app: Application) -> None:
        arc4.abi_call[arc4.String]("echo(string)string", "typed + ignore", app_id=app)
        assert arc4.String.from_log(op.ITxn.last_log()) == "echo: typed + ignore"

        arc4.abi_call("echo(string)string", "untyped + ignore", app_id=app)
        assert arc4.String.from_log(op.ITxn.last_log()) == "echo: untyped + ignore"

        result = arc4.abi_call[arc4.String]("echo(string)string", "tuple", app_id=app)
        assert result[0] == "echo: tuple"
        assert arc4.String.from_log(result[1].last_log) == "echo: tuple"

        txn_result = arc4.abi_call("echo(string)string", "untyped", app_id=app)
        assert arc4.String.from_log(txn_result.last_log) == "echo: untyped"

    @arc4.abimethod()
    def test_arg_conversion(self, app: Application) -> None:
        txn = arc4.abi_call(Logger.log_string, "converted1", app_id=app)
        assert txn.last_log == b"converted1"

        txn = arc4.abi_call(Logger.log_uint64, 2, app_id=app)
        assert txn.last_log == op.itob(2)

        txn = arc4.abi_call(Logger.log_uint512, 3, app_id=app)
        assert txn.last_log == (op.bzero(56) + op.itob(3))

        txn = arc4.abi_call(Logger.log_bytes, b"4", app_id=app)
        assert txn.last_log == b"4"

        txn = arc4.abi_call(Logger.log_bool, True, app_id=app)
        assert txn.last_log == b"True"

    @arc4.abimethod()
    def test_15plus_args(self, app: Application) -> None:
        result, txn = arc4.abi_call(
            Logger.return_args_after_14th,
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10,
            11,
            12,
            13,
            14,
            15,
            16,
            17,
            18,
            arc4.Tuple((arc4.UInt8(0xDE), arc4.UInt8(0xAD), arc4.UInt8(0xBE), arc4.UInt8(0xEF))),
            20,
            app_id=app,
        )
        assert result.native == Bytes.from_hex("0F101112DEADBEEF14")

    @arc4.abimethod()
    def test_void(self, app: Application) -> None:
        txn = arc4.abi_call(LOG_METHOD_NAME + "(string)void", "World1", app_id=app)
        assert txn.last_log == b"World1"

        txn = arc4.abi_call(LOG_METHOD_NAME + "(string)", "World2", app_id=app)
        assert txn.last_log == b"World2"

        txn = arc4.abi_call(LOG_METHOD_NAME, arc4.String("World3"), app_id=app)
        assert txn.last_log == b"World3"

        txn = arc4.abi_call(Logger.log_string, "World4", app_id=app)
        assert txn.last_log == b"World4"

    @arc4.abimethod()
    def test_ref_types(self, app: Application, asset: Asset) -> None:
        txn = arc4.abi_call(
            Logger.log_asset_account_app,
            asset,
            Global.current_application_address,
            app,
            app_id=app,
        )
        assert (
            txn.last_log
            == asset.name + Global.current_application_address.bytes + app.address.bytes
        )

    @arc4.abimethod()
    def test_native_string(self, app: Application) -> None:
        result1, _txn = arc4.abi_call(Logger.echo_native_string, "s", app_id=app)
        assert result1 == "echo: s"

        result2, _txn = arc4.abi_call(Logger.echo_native_string, String("s"), app_id=app)
        assert result2 == result1

        result3, _txn = arc4.abi_call(Logger.echo_native_string, arc4.String("s"), app_id=app)
        assert result3 == result1

    @arc4.abimethod()
    def test_native_bytes(self, app: Application) -> None:
        result1, _txn = arc4.abi_call(Logger.echo_native_bytes, b"b", app_id=app)
        assert result1 == b"echo: b"

        result2, _txn = arc4.abi_call(Logger.echo_native_bytes, Bytes(b"b"), app_id=app)
        assert result2 == result1

        result3, _txn = arc4.abi_call(
            Logger.echo_native_bytes, arc4.DynamicBytes(b"b"), app_id=app
        )
        assert result3 == result1

    @arc4.abimethod()
    def test_native_uint64(self, app: Application) -> None:
        result1, _txn = arc4.abi_call(Logger.echo_native_uint64, 1, app_id=app)
        assert result1 == 2

        result2, _txn = arc4.abi_call(Logger.echo_native_uint64, UInt64(1), app_id=app)
        assert result2 == result1

        result3, _txn = arc4.abi_call(Logger.echo_native_uint64, arc4.UInt64(1), app_id=app)
        assert result3 == result1

    @arc4.abimethod()
    def test_native_biguint(self, app: Application) -> None:
        result1, _txn = arc4.abi_call(Logger.echo_native_biguint, 2, app_id=app)
        assert result1 == 3

        result2, _txn = arc4.abi_call(Logger.echo_native_biguint, BigUInt(2), app_id=app)
        assert result2 == result1

        result3, _txn = arc4.abi_call(Logger.echo_native_biguint, arc4.UInt512(2), app_id=app)
        assert result3 == result1

    @arc4.abimethod()
    def test_native_tuple(self, app: Application) -> None:
        # python literals
        result1, txn = arc4.abi_call[
            arc4.Tuple[arc4.String, arc4.DynamicBytes, arc4.UInt64, arc4.UInt512]
        ](
            "echo_native_tuple(string,byte[],uint64,uint512)(string,byte[],uint64,uint512)",
            "s1",
            b"b1",
            1,
            2,
            app_id=app,
        )
        s, b, u, bu = result1.native
        assert s.native == "echo: s1"
        assert b.native == b"echo: b1"
        assert u.native == 2
        assert bu.native == 3

        # test again using native types in arguments
        result2, txn = arc4.abi_call[
            arc4.Tuple[arc4.String, arc4.DynamicBytes, arc4.UInt64, arc4.UInt512]
        ](
            "echo_native_tuple(string,byte[],uint64,uint512)(string,byte[],uint64,uint512)",
            String("s1"),
            Bytes(b"b1"),
            UInt64(1),
            BigUInt(2),
            app_id=app,
        )
        assert result1 == result2

        # test again using arc4 types in arguments
        result3, txn = arc4.abi_call[
            arc4.Tuple[arc4.String, arc4.DynamicBytes, arc4.UInt64, arc4.UInt512]
        ](
            "echo_native_tuple(string,byte[],uint64,uint512)(string,byte[],uint64,uint512)",
            arc4.String("s1"),
            arc4.DynamicBytes(b"b1"),
            arc4.UInt64(1),
            arc4.UInt512(2),
            app_id=app,
        )
        assert result1 == result3

        # test again using native result type
        result_native, txn = arc4.abi_call[tuple[String, Bytes, UInt64, BigUInt]](
            "echo_native_tuple(string,byte[],uint64,uint512)(string,byte[],uint64,uint512)",
            arc4.String("s1"),
            arc4.DynamicBytes(b"b1"),
            arc4.UInt64(1),
            arc4.UInt512(2),
            app_id=app,
        )
        assert result1.native[0].native == result_native[0]
        assert result1.native[1].native == result_native[1]
        assert result1.native[2].native == result_native[2]
        assert result1.native[3].native == result_native[3]

    @arc4.abimethod()
    def test_native_tuple_method_ref(self, app: Application) -> None:
        # test with literal args
        result, txn = arc4.abi_call(
            Logger.echo_native_tuple,
            "s1",
            b"b1",
            1,
            2,
            app_id=app,
        )
        (s, b, u, bu) = result
        assert s == "echo: s1"
        assert b == b"echo: b1"
        assert u == 2
        assert bu == 3

        # test with native args
        result_2, txn = arc4.abi_call(
            Logger.echo_native_tuple,
            String("s1"),
            Bytes(b"b1"),
            UInt64(1),
            BigUInt(2),
            app_id=app,
        )
        assert result_2 == result, "expected native arguments to give the same result"

        # test with arc4 args
        result_3, txn = arc4.abi_call(
            Logger.echo_native_tuple,
            arc4.String("s1"),
            arc4.DynamicBytes(b"b1"),
            arc4.UInt64(1),
            arc4.UInt512(2),
            app_id=app,
        )
        assert result_3 == result, "expected arc4 arguments to give the same result"

        # test again using native result type
        result_native, txn = arc4.abi_call[tuple[String, Bytes, UInt64, BigUInt]](
            "echo_native_tuple(string,byte[],uint64,uint512)(string,byte[],uint64,uint512)",
            arc4.String("s1"),
            arc4.DynamicBytes(b"b1"),
            arc4.UInt64(1),
            arc4.UInt512(2),
            app_id=app,
        )
        assert result_native == result

    @arc4.abimethod()
    def test_nested_tuples(self, app: Application) -> None:
        # literal args
        result, txn = arc4.abi_call(
            Logger.echo_nested_tuple,
            (("s1", "s2"), (1, 2, b"3")),
            app_id=app,
        )
        ((s1, s2), (u64_1, u64_2, bytez)) = result
        assert s1 == "echo: s1"
        assert s2 == "echo: s2"
        assert u64_1 == 2
        assert u64_2 == 3
        assert bytez == b"echo: 3"

        # native args
        result, txn = arc4.abi_call(
            Logger.echo_nested_tuple,
            ((String("s1"), arc4.String("s2")), (UInt64(1), arc4.UInt64(2), Bytes(b"3"))),
            app_id=app,
        )
        ((s1, s2), (u64_1, u64_2, bytez)) = result
        assert s1 == "echo: s1"
        assert s2 == "echo: s2"
        assert u64_1 == 2
        assert u64_2 == 3
        assert bytez == b"echo: 3"

        # arc4 args
        result, txn = arc4.abi_call(
            Logger.echo_nested_tuple,
            arc4.Tuple(
                (
                    arc4.Tuple((arc4.String("s1b"), arc4.String("s2b"))),
                    arc4.Tuple((arc4.UInt64(11), arc4.UInt64(21), arc4.DynamicBytes(b"3b"))),
                )
            ),
            app_id=app,
        )
        ((s1, s2), (u64_1, u64_2, bytez)) = result
        assert s1 == "echo: s1b"
        assert s2 == "echo: s2b"
        assert u64_1 == 12
        assert u64_2 == 22
        assert bytez == b"echo: 3b"

    @arc4.abimethod()
    def test_no_args(self, app: Application) -> None:
        result, _txn = arc4.abi_call(Logger.no_args, app_id=app)
        assert result == 42
        arc4_result, _txn = arc4.abi_call[arc4.UInt64]("no_args()uint64", app_id=app)
        assert arc4_result == 42

        arc4.abi_call(Logger.no_args, app_id=app)
        assert arc4.UInt64.from_log(op.ITxn.last_log()) == 42

    @arc4.abimethod()
    def test_named_tuples(self, app: Application) -> None:
        result, _txn = arc4.abi_call(
            Logger.logs_are_equal,
            (UInt64(1), String("log 1")),
            LogMessage(level=UInt64(1), message=String("log 1")),
            app_id=app,
        )
        assert result
        result, _txn = arc4.abi_call(
            Logger.logs_are_equal,
            (UInt64(2), String("log 2")),
            LogMessage(level=UInt64(1), message=String("log 1")),
            app_id=app,
        )
        assert not result


from algopy import (
    Application,
    ARC4Contract,
    Bytes,
    UInt64,
    arc4,
    itxn,
    op,
)

from test_cases.typed_abi_call_txn.txn_contract import TxnContract

class Caller(ARC4Contract):
    @arc4.abimethod
    def test_call_with_txn(self, a: Bytes, b: Bytes, app: Application) -> None:
        txn = itxn.AssetConfig(
            unit_name="TST",
            asset_name="TEST",
            total=1,
        )
        asset_id, _txn = arc4.abi_call(
            TxnContract.call_with_txn,
            a,
            txn,
            b,
            app_id=app,
        )
        assert asset_id, "expected asset id"

    @arc4.abimethod
    def test_call_with_acfg(self, a: Bytes, b: Bytes, app: Application) -> None:
        txn = itxn.AssetConfig(
            unit_name="TST",
            asset_name="TEST",
            total=1,
        )
        arc4.abi_call(
            TxnContract.call_with_acfg,
            a,
            txn,
            b,
            app_id=app,
        )

    @arc4.abimethod
    def test_call_with_infer(self, a: Bytes, b: Bytes, app: Application) -> None:
        txn = itxn.AssetConfig(
            unit_name="TST",
            asset_name="TEST",
            total=1,
        )
        arc4.abi_call[UInt64](
            "call_with_acfg",
            a,
            txn,
            b,
            app_id=app,
        )

    @arc4.abimethod
    def test_call_with_acfg_no_return(self, a: Bytes, b: Bytes, app: Application) -> None:
        acfg = itxn.AssetConfig(
            unit_name="TST",
            asset_name="TEST",
            total=1,
        )
        txn1 = arc4.abi_call(
            TxnContract.call_with_acfg_no_return, a, acfg, b, app_id=app, note=b"1"
        )
        assert txn1.note == b"1"
        txn1_copy1 = txn1
        assert txn1_copy1.note == txn1.note
        asset_id = op.GITxn.created_asset_id(0)
        assert asset_id, "expected asset to be created"


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

import algopy

class Caller(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def test_call_with_txn(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_call_with_acfg(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_call_with_infer(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
        app: algopy.Application,
    ) -> None: ...

    @algopy.arc4.abimethod
    def test_call_with_acfg_no_return(
        self,
        a: algopy.arc4.DynamicBytes,
        b: algopy.arc4.DynamicBytes,
        app: algopy.Application,
    ) -> None: ...


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

import algopy

class TxnContract(algopy.arc4.ARC4Client, typing.Protocol):
    @algopy.arc4.abimethod
    def call_with_txn(
        self,
        a: algopy.arc4.DynamicBytes,
        acfg: algopy.gtxn.Transaction,
        b: algopy.arc4.DynamicBytes,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod
    def call_with_acfg(
        self,
        a: algopy.arc4.DynamicBytes,
        acfg: algopy.gtxn.AssetConfigTransaction,
        b: algopy.arc4.DynamicBytes,
    ) -> algopy.arc4.UIntN[typing.Literal[64]]: ...

    @algopy.arc4.abimethod
    def call_with_acfg_no_return(
        self,
        a: algopy.arc4.DynamicBytes,
        acfg: algopy.gtxn.AssetConfigTransaction,
        b: algopy.arc4.DynamicBytes,
    ) -> None: ...


from algopy import (
    ARC4Contract,
    Bytes,
    TransactionType,
    UInt64,
    arc4,
    gtxn,
)

class TxnContract(ARC4Contract):
    @arc4.abimethod
    def call_with_txn(self, a: Bytes, acfg: gtxn.Transaction, b: Bytes) -> UInt64:
        assert a == b"a", "a is not a"
        assert b == b"b", "b is not b"
        assert acfg.type == TransactionType.AssetConfig, "expected asset config"
        assert acfg.created_asset.id, "expected asset id"
        return acfg.created_asset.id

    @arc4.abimethod
    def call_with_acfg(self, a: Bytes, acfg: gtxn.AssetConfigTransaction, b: Bytes) -> UInt64:
        assert a == b"a", "a is not a"
        assert b == b"b", "b is not b"
        assert acfg.created_asset.id, "expected asset id"
        return acfg.created_asset.id

    @arc4.abimethod
    def call_with_acfg_no_return(
        self, a: Bytes, acfg: gtxn.AssetConfigTransaction, b: Bytes
    ) -> None:
        assert a == b"a", "a is not a"
        assert b == b"b", "b is not b"
        assert acfg.created_asset.id, "expected asset id"


from algopy import (
    BigUInt,
    Bytes,
    Contract,
    UInt64,
    subroutine,
)

MAX_UINT64 = 2**64 - 1
MAX_BIGUINT = 2**512 - 1

class UnaryContract(Contract):
    def approval_program(self) -> bool:
        uint_unary()
        biguint_unary()
        bytes_unary()

        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def uint_unary() -> None:
    # test not
    assert not UInt64(0), "not uint"

    # test ~
    for i in (UInt64(1), UInt64(2), UInt64(150), UInt64(MAX_UINT64)):
        assert ~(MAX_UINT64 - i) == i, "~ uint"

@subroutine
def biguint_unary() -> None:
    # test not
    assert not BigUInt(0), "not biguint"

@subroutine
def bytes_unary() -> None:
    # test not
    assert not Bytes(b""), "not bytes"

    # test ~
    assert ~Bytes.from_hex("FF") == Bytes.from_hex("00"), "~ bytes"
    assert ~Bytes.from_hex("0000") == Bytes.from_hex("FFFF"), "~ bytes"


from algopy import ARC4Contract, Bytes, UInt64, arc4, op, subroutine

class Unassigned(ARC4Contract):
    @arc4.abimethod()
    def discard_op(self) -> None:
        op.bzero(10)

    @arc4.abimethod()
    def discard_subroutine(self) -> None:
        get_a_value()

    @arc4.abimethod()
    def discard_constants(self) -> None:
        Bytes()
        UInt64()
        True  # noqa: B018

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


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

import algopy

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

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

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


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

class Baddie(Contract):
    def approval_program(self) -> bool:
        test_case = op.Txn.application_args(0)
        invert_second_condition = (
            op.Txn.num_app_args > 1 and op.btoi(op.Txn.application_args(1)) > 0
        )

        if invert_second_condition:
            if test_case == b"uint":
                assert test_uint_undefined(True, False) == 10
                assert test_uint_undefined(False, True) == 8  # should fail
            elif test_case == b"bytes":
                assert test_bytes_undefined(True, False) == 10
                assert test_bytes_undefined(False, True) == 8  # should fail
            else:
                assert test_mixed_undefined(True, False) == 10
                assert test_mixed_undefined(False, True) == 8  # should fail
        else:  # noqa: PLR5501
            if test_case == b"uint":
                assert test_uint_undefined(True, True) == 8
                assert test_uint_undefined(False, False) == 10
            elif test_case == b"bytes":
                assert test_bytes_undefined(True, True) == 8
                assert test_bytes_undefined(False, False) == 10
            else:
                assert test_mixed_undefined(True, True) == 8
                assert test_mixed_undefined(False, False) == 10
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def test_uint_undefined(x: bool, y: bool) -> UInt64:
    if x:
        a = UInt64(7)
    if x:
        b = UInt64(11)
    else:
        b = UInt64(11)
    if y:
        c = a + 1  # 💥
    else:
        c = b - 1
    return c

@subroutine
def test_bytes_undefined(x: bool, y: bool) -> BigUInt:
    if x:
        a = BigUInt(7)
    if x:
        b = BigUInt(11)
    else:
        b = BigUInt(11)
    if y:
        c = a + 1  # 💥
    else:
        c = b - 1
    return c

@subroutine
def test_mixed_undefined(x: bool, y: bool) -> BigUInt:
    if x:
        a = UInt64(7)
    if x:
        b = BigUInt(11)
    else:
        b = BigUInt(11)
    if y:
        c = BigUInt(a) + BigUInt(1)  # 💥
    else:
        c = b - 1
    return c


from algopy import Contract, UInt64, log, op, subroutine, urange

class UnSSAContract(Contract):
    def approval_program(self) -> bool:
        test_self_ref_phi()
        result1 = test_swap(UInt64(1))
        log(op.itob(result1))
        assert 1 <= result1 <= 2
        result2 = test_swap(UInt64(2))
        log(op.itob(result2))
        assert 1 <= result2 <= 2
        test_swap_loop(UInt64(7), UInt64(11))
        assert test_param_update_with_reentrant_entry_block(UInt64(0)) == 10
        test_param_update_with_reentrant_entry_block_v2(UInt64(0))
        test_param_update_with_reentrant_entry_block_v3()
        test_swap_args()

        (a, b) = test_tuple_swap(UInt64(100), UInt64(200), UInt64(0))
        assert a == UInt64(100)
        assert b == UInt64(200)
        (a, b) = test_tuple_swap(UInt64(100), UInt64(200), UInt64(1))
        assert a == UInt64(200)
        assert b == UInt64(100)

        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def test_self_ref_phi() -> UInt64:
    a = UInt64(1)
    while a < 100:
        if a % 105 == 0:
            continue
        if not a % 21:
            break
        a += 1
    return a

@subroutine
def test_swap(i: UInt64) -> UInt64:
    x = UInt64(1)
    y = UInt64(2)
    while i > 0:
        tmp = x
        x = y
        y = tmp
        i = i - 1
    return x

@subroutine
def test_swap_loop(i: UInt64, j: UInt64) -> UInt64:
    x = UInt64(1)
    y = UInt64(2)
    while i > 0:
        while j > 0:
            tmp = x
            x = y
            y = tmp
            j = j - 1
        i = i - 1
    return x

@subroutine
def test_tuple_swap(a: UInt64, b: UInt64, i: UInt64) -> tuple[UInt64, UInt64]:
    for _item in urange(i):
        (a, b) = (b, a)
    return a, b

@subroutine
def test_param_update_with_reentrant_entry_block(x: UInt64) -> UInt64:
    while True:
        x = x + 1
        if x >= 10:
            break
    return x

@subroutine
def test_param_update_with_reentrant_entry_block_v2(x: UInt64) -> UInt64:
    x = x + 1
    while True:
        if x >= 1:
            break
    return x

@subroutine
def test_param_update_with_reentrant_entry_block_v3() -> None:
    while True:
        if one():
            break

@subroutine
def one() -> UInt64:
    return UInt64(1)

@subroutine
def swap_args(a: UInt64, b: UInt64) -> tuple[UInt64, UInt64]:
    return b, a

@subroutine
def test_swap_args() -> None:
    a = one() + 123
    b = one() + 234
    a, b = swap_args(a, b)
    assert a == 235, "a == 235"
    assert b == 124, "b = 124"

# TODO: lost copy problem example/test


from algopy import Bytes, Contract, UInt64, log, op, subroutine

class WithReentrancy(Contract):
    """My re-entrant contract"""

    def approval_program(self) -> bool:
        log(op.itob(fibonacci(UInt64(5))))
        silly(UInt64(2))
        return True

    def clear_state_program(self) -> bool:
        return True

@subroutine
def itoa(i: UInt64) -> Bytes:
    digits = Bytes(b"0123456789")
    radix = digits.length
    if i < radix:
        return digits[i]
    return itoa(i // radix) + digits[i % radix]

@subroutine
def fibonacci(n: UInt64) -> UInt64:
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

@subroutine
def silly(x: UInt64) -> UInt64:
    x = x + 1
    result = silly2(x)
    log(Bytes(b"silly = ") + itoa(x))
    return result

@subroutine
def silly2(x: UInt64) -> UInt64:
    x = x + 2
    result = silly3(x)
    log(Bytes(b"silly2 = ") + itoa(x))
    return result

@subroutine
def silly3(x: UInt64) -> UInt64:
    is_even = x % 2 == 0
    a = x + 2
    if is_even:
        result = a * 2
        a = result // 2 - 2
    else:
        result = silly(x)

    if is_even:
        result = a
    log(Bytes(b"silly3 = ") + itoa(x))
    return result


from pathlib import Path

VCS_ROOT = Path(__file__).parent.parent
EXAMPLES_DIR = VCS_ROOT / "examples"
TEST_CASES_DIR = VCS_ROOT / "test_cases"


from random import randbytes

import pytest
from _pytest.mark import ParameterSet
from algokit_utils import (
    Account,
    get_algod_client,
    get_default_localnet_config,
    get_localnet_default_account,
)
from algosdk import transaction
from algosdk.atomic_transaction_composer import AtomicTransactionComposer, TransactionWithSigner
from algosdk.v2client.algod import AlgodClient

from puya import log
from tests import EXAMPLES_DIR, TEST_CASES_DIR
from tests.utils import PuyaTestCase

def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
    # parametrize `test_case: PuyaTestCase` based on test cases in examples and test_cases dirs
    if "test_case" in metafunc.fixturenames:
        # only parametrize if not already parametrized
        mark = metafunc.definition.get_closest_marker("parametrize")
        if not mark or "test_case" not in mark.args[0]:
            params = [
                ParameterSet.param(
                    PuyaTestCase(item),
                    marks=[pytest.mark.slow] if item.name == "stress_tests" else [],
                )
                for root in (EXAMPLES_DIR, TEST_CASES_DIR)
                for item in root.iterdir()
                if item.is_dir() and not item.name.startswith((".", "_"))
            ]
            metafunc.parametrize("test_case", params, ids=lambda t: t.id)

@pytest.fixture(autouse=True, scope="session")
def _setup_logging() -> None:
    # configure logging for tests
    # note cache_logger should be False if calling configure_logging more than once
    log.configure_logging(min_log_level=log.LogLevel.info, cache_logger=False)

@pytest.fixture(scope="session")
def algod_client() -> AlgodClient:
    return get_algod_client(get_default_localnet_config("algod"))

@pytest.fixture(scope="session")
def account(algod_client: AlgodClient) -> Account:
    return get_localnet_default_account(algod_client)

@pytest.fixture(scope="session")
def asset_a(algod_client: AlgodClient, account: Account) -> int:
    return _create_asset(algod_client, account, "a")

@pytest.fixture(scope="session")
def asset_b(algod_client: AlgodClient, account: Account) -> int:
    return _create_asset(algod_client, account, "b")

def _create_asset(algod_client: AlgodClient, account: Account, asset_unit: str) -> int:
    sp = algod_client.suggested_params()
    atc = AtomicTransactionComposer()
    atc.add_transaction(
        TransactionWithSigner(
            transaction.AssetCreateTxn(
                account.address,
                sp,
                10_000_000,
                0,
                default_frozen=False,
                asset_name=f"asset {asset_unit}",
                unit_name=asset_unit,
                note=randbytes(8),
            ),
            signer=account.signer,
        )
    )
    response = atc.execute(algod_client, 4)
    txn_id = response.tx_ids[0]
    result = algod_client.pending_transaction_info(txn_id)
    assert isinstance(result, dict)
    asset_index = result["asset-index"]
    assert isinstance(asset_index, int)
    return asset_index