#     Copyright 2025, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file


"""Nested loop variable type inference and post-loop operations."""

from __future__ import print_function

# nuitka-project: --python-flag=no_warnings


# for+for: None -> list, iterate after nested loops


def functionForForListIterate():
    data = [[1, 2, 3]]
    collected = None
    for group in data:
        for item in group:
            if collected is None:
                collected = []
            collected.append(item)
    out = []
    if collected is not None:
        for x in collected:
            out.append(x)
    print("for+for list iterate:", out)


# for+while: None -> list, iterate after nested loops


def functionForWhileListIterate():
    data = [[3, 2, 1]]
    collected = None
    for group in data:
        while group:
            item = group.pop()
            if collected is None:
                collected = []
            collected.append(item)
    out = []
    if collected is not None:
        for x in collected:
            out.append(x)
    print("for+while list iterate:", sorted(out))


#  while+for: None -> list, iterate after nested loops


def functionWhileForListIterate():
    data = [[1, 2, 3]]
    collected = None
    idx = 0
    while idx < len(data):
        for item in data[idx]:
            if collected is None:
                collected = []
            collected.append(item)
        idx += 1
    out = []
    if collected is not None:
        for x in collected:
            out.append(x)
    print("while+for list iterate:", out)


# for+for: None -> dict, iterate keys after nested loops


def functionForForDictIterate():
    data = [["a", "b"], ["c"]]
    mapping = None
    for group in data:
        for ch in group:
            if mapping is None:
                mapping = {}
            mapping[ch] = ord(ch)
    result = []
    if mapping is not None:
        for k in mapping:
            result.append(k)
    print("for+for dict iterate:", sorted(result))


# for+for: None -> set, iterate members after nested loops


def functionForForSetIterate():
    data = [[1, 2, 2], [3, 1]]
    seen = None
    for group in data:
        for item in group:
            if seen is None:
                seen = set()
            seen.add(item)
    result = []
    if seen is not None:
        for x in seen:
            result.append(x)
    print("for+for set iterate:", sorted(result))


# Closure with nested loops and callback iteration


def functionClosureCallbackIterate():
    output = []
    tasks = [[lambda: output.append("a"), lambda: output.append("b")]]

    def run():
        ready = None
        for batch in tasks:
            while batch:
                fn = batch.pop()
                if ready is None:
                    ready = []
                ready.append(fn)
        if ready is not None:
            for f in ready:
                f()

    run()
    print("closure callback iterate:", sorted(output))


# Single loop (no nesting) with iteration - control case


def functionSingleLoopIterate():
    data = [1, 2, 3]
    collected = None
    for item in data:
        if collected is None:
            collected = []
        collected.append(item)
    out = []
    if collected is not None:
        for x in collected:
            out.append(x)
    print("single loop iterate:", out)


# Chained inner loops (canary): inner loop 1 builds list, inner loop 2
# iterates it, within the same outer body.


def functionChainInnerLoops():
    groups = [[1, 2], [3, 4]]
    total = 0
    for group in groups:
        bucket = None
        for val in group:
            if bucket is None:
                bucket = []
            bucket.append(val * 10)
        for item in bucket:
            total += item
    print("chain inner loops:", total)


# Variable used between outer iterations (canary): shape must propagate
# to outer loop body after inner loop completes.


def functionUseBetweenOuterIters():
    groups = [[1, 2], [3, 4], [5]]
    result = None
    for group in groups:
        for val in group:
            if result is None:
                result = []
            result.append(val)
        result.append(0)
    print("between outer iters:", result)


# ---------------------------------------------------------------------------
# 1. Attribute / method calls (canary)
# ---------------------------------------------------------------------------


def functionMethodAppend():
    """list.append() on variable built in nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    result.append(99)
    print("method_append:", result)


# ---------------------------------------------------------------------------
# 2. Subscript / indexing (canary)
# ---------------------------------------------------------------------------


def functionListIndexing():
    """Indexing a list built in nested loop."""
    data = [[10, 20], [30]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    print("indexing:", result[0], result[-1])


# ---------------------------------------------------------------------------
# 3. Binary / augmented operations
#    Wrong type -> wrong operator specialization or TypeError.
# ---------------------------------------------------------------------------


def functionListConcatenation():
    """List + list where left operand is from nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    combined = result + [4, 5]
    print("list_concat:", combined)


def functionListMultiply():
    """List * int where list is from nested loop."""
    data = [[1], [2]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    doubled = result * 2
    print("list_mult:", doubled)


def functionListIadd():
    """List += on variable from nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    result += [4]
    print("list_iadd:", result)


def functionIntArithmetic():
    """Arithmetic on int accumulated in nested loop."""
    data = [[1, 2], [3, 4]]
    total = None
    for group in data:
        for val in group:
            if total is None:
                total = 0
            total = total + val
    result = total * 2 + 1
    print("int_arith:", result)


def functionFloatArithmetic():
    """Float arithmetic on value from nested loop."""
    data = [[1.5, 2.5], [3.0]]
    total = None
    for group in data:
        for val in group:
            if total is None:
                total = 0.0
            total = total + val
    result = total / 2.0
    print("float_arith:", result)


def functionStrConcatenation():
    """String + string where left operand is from nested loop."""
    data = [["ab", "cd"], ["ef"]]
    text = None
    for group in data:
        for w in group:
            if text is None:
                text = ""
            text = text + w
    full = text + "!"
    print("str_concat:", full)


def functionStrMultiply():
    """String * int where string is from nested loop."""
    data = [["a"], ["b"]]
    text = None
    for group in data:
        for w in group:
            if text is None:
                text = ""
            text = text + w
    print("str_mult:", text * 3)


# ---------------------------------------------------------------------------
# 4. Boolean context / truthiness (canary)
# ---------------------------------------------------------------------------


def functionTruthinessIf():
    """if <var>: where var is built in nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    if result:
        print("truthiness_if: non-empty", len(result))
    else:
        print("truthiness_if: empty")


# ---------------------------------------------------------------------------
# 5. Membership testing (canary)
# ---------------------------------------------------------------------------


def functionInList():
    """'val in list' where list is from nested loop."""
    data = [[1, 2], [3, 4]]
    items = None
    for group in data:
        for val in group:
            if items is None:
                items = []
            items.append(val)
    print("in_list:", 3 in items, 99 in items)


# ---------------------------------------------------------------------------
# 6. Unpacking
#    Optimizer must know the variable is iterable to allow unpacking.
# ---------------------------------------------------------------------------


def functionUnpackList():
    """Unpack a 2-element list built in nested loop."""
    data = [[1], [2]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    a, b = result
    print("unpack_list:", a, b)


def functionUnpackInLoop():
    """Unpack variable from nested loop inside a for loop."""
    data = [[(1, "a")], [(2, "b")]]
    pairs = None
    for group in data:
        for val in group:
            if pairs is None:
                pairs = []
            pairs.append(val)
    nums = []
    for n, c in pairs:
        nums.append(n)
    print("unpack_in_loop:", nums)


# ---------------------------------------------------------------------------
# 7. len() and builtins (canary)
# ---------------------------------------------------------------------------


def functionLenList():
    """len() on list from nested loop."""
    data = [[1, 2], [3, 4, 5]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    print("len_list:", len(result))


# ---------------------------------------------------------------------------
# 8. Comparison specialization
# ---------------------------------------------------------------------------


def functionEqComparison():
    """== comparison on variable from nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    print("eq:", result == [1, 2, 3], result == [])


def functionListOrdering():
    """< > comparisons on lists from nested loop."""
    data1 = [[1, 2], [3]]
    data2 = [[4, 5], [6]]
    r1 = None
    r2 = None
    for group in data1:
        for val in group:
            if r1 is None:
                r1 = []
            r1.append(val)
    for group in data2:
        for val in group:
            if r2 is None:
                r2 = []
            r2.append(val)
    print("ordering:", r1 < r2, r1 > r2)


# ---------------------------------------------------------------------------
# 9. Iteration protocol (canary)
# ---------------------------------------------------------------------------


def functionIterNext():
    """iter() + next() on variable from nested loop."""
    data = [[10, 20], [30]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    it = iter(result)
    print("iter_next:", next(it), next(it))


# ---------------------------------------------------------------------------
# 10. Comprehension iteration
# ---------------------------------------------------------------------------


def functionListCompOnLoopVar():
    """List comprehension over variable from nested loop."""
    data = [[1, 2], [3]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    doubled = [x * 2 for x in result]
    print("list_comp:", doubled)


def functionSetCompOnLoopVar():
    """Set comprehension over variable from nested loop."""
    data = [[1, 2, 2], [3, 1]]
    result = None
    for group in data:
        for val in group:
            if result is None:
                result = []
            result.append(val)
    uniq = {x for x in result}
    print("set_comp:", sorted(uniq))


# Execute all tests.
functionForForListIterate()
functionForWhileListIterate()
functionWhileForListIterate()
functionForForDictIterate()
functionForForSetIterate()
functionClosureCallbackIterate()
functionSingleLoopIterate()
functionChainInnerLoops()
functionUseBetweenOuterIters()

functionMethodAppend()
functionListIndexing()
functionListConcatenation()
functionListMultiply()
functionListIadd()
functionIntArithmetic()
functionFloatArithmetic()
functionStrConcatenation()
functionStrMultiply()
functionTruthinessIf()
functionInList()
functionUnpackList()
functionUnpackInLoop()
functionLenList()
functionEqComparison()
functionListOrdering()
functionIterNext()
functionListCompOnLoopVar()
functionSetCompOnLoopVar()

#     Python tests originally created or extracted from other peoples work. The
#     parts were too small to be protected.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
