from functools import wraps


def _run(iter_n, func, initial_args, initial_kwargs):
    current_iter_params = [(initial_args, initial_kwargs)]
    for _ in range(iter_n):
        next_iter_params = []
        for args, kwargs in current_iter_params:
            next_iter_params.extend(
                next_iter_func_params for next_iter_func_params in func(*args, **kwargs))
        current_iter_params = next_iter_params


def run(iter_n):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            _run(iter_n, func, args, kwargs)
        return wrapper
    return decorator


if __name__ == '__main__':
    import math as m


    def line(x1, y1, x2, y2, stroke_width):
        return f'<line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" stroke-width="{stroke_width}"/>'


    def svg(svg_lines, width, height):
        return '\n'.join((
            '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" '
            f'width="{width}" height="{height}" stroke="white">',
            f'<rect x="0" y="0" width="{width}" height="{height}" />',
            '\n'.join(svg_lines),
            '</svg>'
        ))


    def draw_tree(filename, iter_n=12, fac=0.594, r=96):
        svg_lines = []

        @run(iter_n)
        def tree(x, y, r, a):
            x2 = x + m.cos(a) * r
            y2 = y + m.sin(a) * r
            svg_lines.append(line(x, y, x2, y2, r / 8))

            next_r = r * fac
            return [((x2, y2, next_r, a + m.pi / 4), {}), ((x2, y2, next_r, a - m.pi / 4), {})]

        tree(127, 256, r, -m.pi / 2)
        with open(f'{filename}.svg', 'w') as svg_file:
            svg_file.write(svg(svg_lines, 256, 256))


    draw_tree('tree_fractal')