# SPDX-License-Identifier: MIT
events = []
def source():
    events.append("source")
    return [1, 2, 3, 4]
def keep(value):
    events.append("keep:" + str(value))
    return value % 2 == 1
def element(value):
    events.append("element:" + str(value))
    return value * 10
events_cursor = (element(value) for value in source() if keep(value))
print(type(events_cursor).__name__, events)
alias = iter(events_cursor)
print(events_cursor is alias, next(events_cursor), events)
print(next(alias), events)
print(list(events_cursor), next(alias, -1), events)

print(list(value * value for value in (3, 1, 2)))
print(list(character for character in "aé雪😀"))
print(list(value for value in b"\x00\x80\xff"))
print(list(value for value in reversed([1, 2, 3])))
print(list(pair for pair in enumerate("ab", 7)))
print(list(left + right for left, right in zip([1, 2], [10, 20])))
print(sum(value for value in range(5)))
print(all(value > 0 for value in [1, 2]), any(value < 0 for value in [1, -2]))
print(list(None for value in [1, 2]))
print(list(value for value in ()))

scale = 1
values = [1, 2]
original = values
scaled_cursor = (value * scale for value in values)
values = [99]
original.append(3)
scale = 10
print(next(scaled_cursor), values, original)
scale = 20
print(list(scaled_cursor))

def factory():
    offset = 1
    cursor = (value + offset for value in range(3))
    offset = 10
    return cursor
print(list(factory()))
def factory_with_setter():
    offset = 1
    def change(value):
        nonlocal offset
        offset = value
    return (value + offset for value in range(3)), change
setter_cursor, change = factory_with_setter()
print(next(setter_cursor))
change(50)
print(list(setter_cursor))

value = 999
closure_cursor = (lambda: value for value in [1, 2, 3])
first = next(closure_cursor)
second = next(closure_cursor)
print(first(), second(), value)
remaining = list(closure_cursor)
print(first(), second(), remaining[0](), value)
saved = []
def keep_callback(callback):
    saved.append(callback)
    return callback() % 2 == 0
filter_cursor = (value for value in range(6) if keep_callback(lambda: value))
print(next(filter_cursor), saved[0]())
print(next(filter_cursor), [callback() for callback in saved])
print(list(filter_cursor), [callback() for callback in saved])

nested_cursor = ((value + other for other in [10]) for value in [1, 2, 3])
first_inner = next(nested_cursor)
second_inner = next(nested_cursor)
print(list(first_inner), list(second_inner))
third_inner = next(nested_cursor)
print(len(list(nested_cursor)), list(third_inner))

calls = []
def failed(value):
    calls.append(value)
    if value == 2:
        raise ValueError("element failed")
    return value
error_cursor = (failed(value) for value in range(4))
print(next(error_cursor), next(error_cursor))
try:
    next(error_cursor)
except ValueError as error:
    print(type(error).__name__, str(error), calls)
print(list(error_cursor), next(error_cursor, -1), calls)
def stopped(value):
    raise StopIteration("from callback")
stop_element_cursor = (stopped(value) for value in [1])
try:
    next(stop_element_cursor)
except RuntimeError as error:
    print(type(error).__name__, str(error))
print(list(stop_element_cursor))
stop_filter_cursor = (value for value in [1] if stopped(value))
try:
    next(stop_filter_cursor)
except RuntimeError as error:
    print(type(error).__name__, str(error))
print(list(stop_filter_cursor))
