import json
import os
from contextlib import suppress
from math import sqrt
from typing import Tuple

import numpy as np
import pandas as pd
from openpyxl import load_workbook, styles, utils
from PIL import Image

def to_excel(
    image: Image, path: str, lower_image_size_by: int = 10, **spreadsheet_kwargs
) -> None:
    """
    - Added on release 0.0.1;
    - Coded originally on https://github.com/Eric-Mendes/image2excel

    Saves an image as a `.xlsx` file by coloring its cells each pixel's color.

    ## Parameters
    * :param image: Your image opened using the `PIL.Image` module;
    * :param path: The path that you want to save your output file.
    Example: `/home/user/Documents/my_image.xlsx`;
    * :param lower_image_size_by: A factor that the function will divide
    your image's dimensions by. Defaults to `10`;
       * It is very important that you lower your image's dimensions because a big image might take the function a long time to process plus your spreadsheet will probably take a long time to load on any software that you use to open it;
    * :param **spreadsheet_kwargs: See below.

    ## Spreadsheet Kwargs
    Optional parameters to tweak the spreadsheet's appearance.

    * :param row_height (`float`): the rows' height. Defaults to `15`;
    * :param column_width (`float`): the columns' width. Defaults to `2.3`;
       * The default values on `row_height` and `column_width` were specifically thought out so that they make the cells squared, however - as any hardcoded value - they might not do the trick on your device. That is when you might want to tweak them a little bit.
    * :param delete_cell_value (`bool`): wheter to keep or not the text corresponding to that color. Defaults to `True`;
    * :param zoom_scale (`int`): how much to zoom in or out on the spreadsheet. Defaults to `20` which seems to be the default max zoom out on most spreadsheet softwares.

    ## Return
    * :return: `None`, but outputs a `.xlsx` file on the given `path`.
    """
    image = image.convert("RGB")

    # Resizing image
    image = image.resize(
        (image.size[0] // lower_image_size_by, image.size[1] // lower_image_size_by)
    )

    # OpenPyxl colors work in a weird way
    image_colors_processed = [
        ["%02x%02x%02x" % tuple(item) for item in row]
        for row in np.array(image).tolist()
    ]

    df = pd.DataFrame(image_colors_processed)
    image_name = os.path.splitext(os.path.split(path)[1])[0]

    # Saving a DataFrame where each cell has a text corresponding to the RGB color its background should be
    df.to_excel(path, index=False, header=False)

    # Loading the excel file, painting each cell with its color and saving the updates
    wb = load_workbook(path)

    ws = wb.active
    ws.title = image_name

    for row in range(1, df.shape[0] + 1):
        for col in range(1, df.shape[1] + 1):
            cell = ws.cell(row=row, column=col)
            # Makes cells squared
            ws.row_dimensions[row].height = spreadsheet_kwargs.get("row_height", 15)
            ws.column_dimensions[
                utils.get_column_letter(col)
            ].width = spreadsheet_kwargs.get("column_width", 2.3)

            # Painting the cell
            cell.fill = styles.PatternFill(
                start_color=cell.value, end_color=cell.value, fill_type="solid"
            )
            if spreadsheet_kwargs.get("delete_cell_value", True):
                cell.value = None  # Deletes the text from the cell

    # Saves spreadsheet already zoomed in or out
    ws.sheet_view.zoomScale = spreadsheet_kwargs.get("zoom_scale", 20)
    wb.save(path)

def to_minecraft(
    image: Image,
    path: str,
    lower_image_size_by: int = 10,
    player_pos: Tuple[int, int, int] = (0, 0, 0),
    minecraft_version: str = '1.18.2',

) -> None:
    """
    - Added on release 0.0.1;
    - Coded originally on https://github.com/Eric-Mendes/pixel-art-map

    Saves an image as a minecraft datapack that when loaded into your world will build a pixel art of it on the player's position.

    ## Parameters
    * :param image: Your image opened using the `PIL.Image` module;
    * :param path: The path that you want to save your datapack.
    Example: `/home/user/Documents/my_image_datapack`;
    * :param lower_image_size_by: A factor that the function will divide
    your image's dimensions by. Defaults to `10`;
    * :param player_pos: The player's (x, y, z) position. Defaults to `(0, 0, 0)`;
    * :param minecraft_version: The minecraft client version (x.xx.x). Default is `1.18.2`.

    ## Return
    * :return: `None`, but outputs a datapack on the given `path`.
    """
    image = image.convert("RGB")

    # Makes the commands that the datapack will run when loaded
    def script(df, **kwargs):
        player_pos = [
            kwargs.get("player_x", 0),
            kwargs.get("player_y", 0),
            kwargs.get("player_z", 0),
        ]
        z = (df != df.shift()).cumsum()
        zri = z.reset_index()
        ix_name = z.index.name
        co_name = z.columns.name
        for i in z:
            v = zri.groupby(i)[ix_name].agg(["first", "last"])
            s = {co_name: i}
            e = {co_name: i}
            for _, r in v.iterrows():
                s[ix_name] = r["first"]
                e[ix_name] = r["last"]
                material = df.loc[r["first"], i]
                yield f'fill {s["x"] + player_pos[0]} {0 + player_pos[1]} {s["z"] + player_pos[2]} {e["x"] + player_pos[0]} {0 + player_pos[1]} {e["z"] + player_pos[2]} {material.split(",")[0].strip()}'

    # Helper function. Loads the blocks an the colors they have when looked at via map,
    # and maps the pixels to the blocks
    blocks = [
        {
            "rgb": (127, 178, 56),
            "blocks": ("grass_block", "slime_block"),
        },
        {
            "rgb": (247, 233, 163),
            "blocks": ("sand", "birch_planks", "birch_log[axis=y]", "stripped_birch_log[axis=x]", "birch_wood", "stripped_birch_wood", "birch_sign", "birch_pressure_plate", "birch_trapdoor", "birch_stairs", "birch_slab", "birch_fence_gate", "birch_fence", "birch_door", "sandstone", "glowstone", "end_stone", "end_stone_brick_slab", "end_stone_brick_stairs", "end_stone_brick_wall", "bone_block", "turtle_egg", "scaffolding", "candle"),
        },
        {
            "rgb": (199, 199, 199),
            "blocks": ("mushroom_stem", "cobweb", "white_bed[part=head]", "white_candle"),
        },
        {
            "rgb": (255, 0, 0),
            "blocks": ("redstone_block", "tnt", "lava", "fire"),
        },
        {
            "rgb": (160, 160, 255),
            "blocks": ("ice", "frosted_ice", "packed_ice", "blue_ice"),
        },
        {
            "rgb": (167, 167, 167),
            "blocks": ("iron_block", "iron_door", "brewing_stand", "heavy_weighted_pressure_plate", "iron_trapdoor", "lantern", "anvil", "grindstone", "soul_lantern", "lodestone"),
        },
        {
            "rgb": (0, 124, 0),
            "blocks": ("oak_sapling", "spruce_sapling", "birch_sapling", "jungle_sapling", "acacia_sapling", "dark_oak_sapling", "dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", "red_tulip", "orange_tulip", "white_tulip", "pink_tulip", "oxeye_daisy", "cornflower", "lily_of_the_valley", "wither_rose", "sunflower", "lilac", "rose_bush", "peony", "wheat[age=7]", "sugar_cane[age=9]", "pumpkin_stem[age=7]", "melon_stem[age=7]", "lily_pad", "cocoa[age=2]", "carrots[age=7]", "potatoes[age=7]", "beetroots[age=7]", "sweet_berry_bush[age=3]", "grass", "fern", "vine", "oak_leaves", "spruce_leaves", "birch_leaves", "jungle_leaves", "acacia_leaves", "dark_oak_leaves", "azalea_leaves", "flowering_azalea_leaves", "cactus[age=9]", "bamboo[age=1]", "cave_vines", "spore_blossom", "flowering_azalea", "big_dripleaf", "small_dripleaf"),
        },
        {
            "rgb": (255, 255, 255),
            "blocks": ("snow", "snow_block", "white_bed[part=foot]", "white_wool", "white_stained_glass", "white_carpet", "white_shulker_box", "white_glazed_terracotta", "white_concrete", "white_concrete_powder", "powder_snow"),
        },
        {
            "rgb": (164, 168, 184),
            "blocks": ("clay", "infested_chiseled_stone_bricks", "infested_cobblestone", "infested_cracked_stone_bricks", "infested_mossy_stone_bricks", "infested_stone", "infested_stone_bricks"),
        },
        {
            "rgb": (151, 109, 77),
            "blocks": ("coarse_dirt", "dirt", "farmland", "dirt_path", "granite_slab", "granite_stairs", "granite_wall", "polished_granite_slab", "polished_granite_stairs", "jungle_planks", "jungle_log[axis=y]", "stripped_jungle_log[axis=x]", "jungle_wood", "stripped_jungle_wood", "jungle_sign", "jungle_pressure_plate", "jungle_trapdoor", "jungle_stairs", "jungle_slab", "jungle_fence_gate", "jungle_fence", "jungle_door", "jukebox", "brown_mushroom_block", "rooted_dirt", "hanging_roots"),
        },
        {
            "rgb": (112, 112, 112),
            "blocks": ("stone", "stone_slab", "stone_stairs", "andesite_slab", "andesite_stairs", "andesite_wall", "polished_andesite_slab", "polished_andesite_stairs", "cobblestone_slab", "cobblestone_stairs", "cobblestone_wall", "bedrock", "gold_ore", "iron_ore", "coal_ore", "lapis_lazuli_ore", "dispenser", "mossy_cobblestone_slab", "mossy_cobblestone_stairs", "mossy_cobblestone_wall", "spawner", "diamond_ore", "furnace", "stone_pressure_plate", "redstone_ore", "stone_bricks", "emerald_ore", "ender_chest", "dropper", "smooth_stone_slab", "observer", "smoker", "blast_furnace", "stonecutter", "sticky_piston", "piston", "piston_head", "gravel", "acacia_log[axis=z]", "cauldron", "hopper", "copper_ore"),
        },
        {
            "rgb": (64, 64, 255),
            "blocks": ("water", "kelp", "seagrass", "bubble_column"),
        },
        {
            "rgb": (143, 119, 72),
            "blocks": ("oak_planks", "oak_log[axis=y]", "stripped_oak_log[axis=x]", "oak_wood", "stripped_oak_wood", "oak_sign", "oak_pressure_plate", "oak_trapdoor", "oak_stairs", "oak_slab", "oak_fence_gate", "oak_fence", "oak_door", "note_block", "bookshelf", "chest", "crafting_table", "trapped_chest", "daylight_detector", "loom", "barrel", "cartography_table", "fletching_table", "lectern", "smithing_table", "composter", "bamboo_sapling", "dead_bush", "petrified_oak_slab", "beehive", "white_banner"),
        },
        {
            "rgb": (255, 252, 245),
            "blocks": ("quartz_block", "diorite_stairs", "diorite_slab", "diorite_wall", "polished_diorite_stairs", "polished_diorite_slab", "birch_log[axis=x]", "sea_lantern", "target"),
        },
        {
            "rgb": (216, 127, 51),
            "blocks": ("acacia_planks", "acacia_log[axis=y]", "stripped_acacia_log[axis=x]", "acacia_wood", "stripped_acacia_wood", "acacia_sign", "acacia_pressure_plate", "acacia_trapdoor", "acacia_stairs", "acacia_slab", "acacia_fence_gate", "acacia_fence", "acacia_door", "red_sand", "orange_wool", "orange_carpet", "orange_shulker_box", "orange_bed[part=foot]", "orange_stained_glass", "orange_glazed_terracotta", "orange_concrete", "orange_concrete_powder", "orange_candle", "pumpkin", "carved_pumpkin", "jack_o_lantern", "terracotta", "red_sandstone", "honey_block", "honeycomb_block", "copper_block", "lightning_rod", "raw_copper_block"),
        },
        {
            "rgb": (178, 76, 216),
            "blocks": ("magenta_wool", "magenta_carpet", "magenta_shulker_box", "magenta_bed[part=foot]", "magenta_stained_glass", "magenta_glazed_terracotta", "magenta_concrete", "magenta_concrete_powder", "magenta_candle", "purpur_block"),
        },
        {
            "rgb": (102, 153, 216),
            "blocks": ("light_blue_wool", "light_blue_carpet", "light_blue_shulker_box", "light_blue_bed[part=foot]", "light_blue_stained_glass", "light_blue_glazed_terracotta", "light_blue_concrete", "light_blue_concrete_powder", "light_blue_candle", "soul_fire"),
        },
        {
            "rgb": (229, 229, 51),
            "blocks": ("sponge", "wet_sponge", "yellow_wool", "yellow_carpet", "yellow_shulker_box", "yellow_bed[part=foot]", "yellow_stained_glass", "yellow_glazed_terracotta", "yellow_concrete", "yellow_concrete_powder", "yellow_candle", "hay_bale", "horn_coral_block[waterlogged=true]", "bee_nest"),
        },
        {
            "rgb": (127, 204, 25),
            "blocks": ("lime_wool", "lime_carpet", "lime_shulker_box", "lime_bed[part=foot]", "lime_stained_glass", "lime_glazed_terracotta", "lime_concrete", "lime_concrete_powder", "lime_candle", "melon"),
        },
        {
            "rgb": (242, 127, 165),
            "blocks": ("pink_wool", "pink_carpet", "pink_shulker_box", "pink_bed[part=foot]", "pink_stained_glass", "pink_glazed_terracotta", "pink_concrete", "pink_concrete_powder", "pink_candle", "brain_coral_block[waterlogged=true]"),
        },
        {
            "rgb": (76, 76, 76),
            "blocks": ("acacia_wood", "gray_wool", "gray_carpet", "gray_shulker_box", "gray_bed[part=foot]", "gray_stained_glass", "gray_glazed_terracotta", "gray_concrete", "gray_concrete_powder", "gray_candle", "dead_coral_block", "tinted_glass"),
        },
        {
            "rgb": (153, 153, 153),
            "blocks": ("light_gray_wool", "light_gray_carpet", "light_gray_shulker_box", "light_gray_bed[part=foot]", "light_gray_stained_glass", "light_gray_glazed_terracotta", "light_gray_concrete", "light_gray_concrete_powder", "light_gray_candle", "structure_block", "jigsaw"),
        },
        {
            "rgb": (76, 127, 153),
            "blocks": ("cyan_wool", "cyan_carpet", "cyan_shulker_box", "cyan_bed[part=foot]", "cyan_stained_glass", "cyan_glazed_terracotta", "cyan_concrete", "cyan_concrete_powder", "cyan_candle", "prismarine_slab", "prismarine_stairs", "prismarine_wall", "warped_roots", "warped_fungus", "twisting_vines", "nether_sprouts", "sculk_sensor"),
        },
        {
            "rgb": (127, 63, 178),
            "blocks": ("shulker_box", "purple_wool", "purple_carpet", "purple_shulker_box", "purple_bed[part=foot]", "purple_stained_glass", "purple_glazed_terracotta", "purple_concrete", "purple_concrete_powder", "purple_candle", "mycelium", "chorus_plant", "chorus_flower", "repeating_command_block", "bubble_coral_block", "amethyst_block", "budding_amethyst", "amethyst_cluster"),
        },
        {
            "rgb": (51, 76, 178),
            "blocks": ("blue_wool", "blue_carpet", "blue_shulker_box", "blue_bed[part=foot]", "blue_stained_glass", "blue_glazed_terracotta", "blue_concrete", "blue_concrete_powder", "blue_candle", "tube_coral_block"),
        },
        {
            "rgb": (102, 76, 51),
            "blocks": ("dark_oak_planks", "dark_oak_log[axis=y]", "stripped_dark_oak_log[axis=x]", "dark_oak_wood", "stripped_dark_oak_wood", "dark_oak_sign", "dark_oak_pressure_plate", "dark_oak_trapdoor", "dark_oak_stairs", "dark_oak_slab", "dark_oak_fence_gate", "dark_oak_fence", "dark_oak_door", "spruce_log[axis=x]", "brown_wool", "brown_carpet", "brown_shulker_box", "brown_bed[part=foot]", "brown_stained_glass", "brown_glazed_terracotta", "brown_concrete", "brown_concrete_powder", "brown_candle", "soul_sand", "command_block", "brown_mushroom", "soul_soil"),
        },
        {
            "rgb": (102, 127, 51),
            "blocks": ("green_wool", "green_carpet", "green_shulker_box", "green_bed[part=foot]", "green_stained_glass", "green_glazed_terracotta", "green_concrete", "green_concrete_powder", "green_candle", "end_portal_frame", "chain_command_block", "sea_pickle", "moss_carpet", "moss_block", "dried_kelp_block"),
        },
        {
            "rgb": (153, 51, 51),
            "blocks": ("red_wool", "red_carpet", "red_shulker_box", "red_bed[part=foot]", "red_stained_glass", "red_glazed_terracotta", "red_concrete", "red_concrete_powder", "red_candle", "brick_slab", "brick_stairs", "brick_wall", "red_mushroom_block", "nether_wart", "enchanting_table", "nether_wart_block", "fire_coral_block", "red_mushroom", "shroomlight"),
        },
        {
            "rgb": (25, 25, 25),
            "blocks": ("black_wool", "black_carpet", "black_shulker_box", "black_bed[part=foot]", "black_stained_glass", "black_glazed_terracotta", "black_concrete", "black_concrete_powder", "black_candle", "obsidian", "end_portal", "dragon_egg", "coal_block", "end_gateway", "basalt", "polished_basalt", "smooth_basalt", "netherite_block", "crying_obsidian", "respawn_anchor", "blackstone", "gilded_blackstone"),
        },
        {
            "rgb": (250, 238, 77),
            "blocks": ("gold_block", "light_weighted_pressure_plate", "bell", "raw_gold_block"),
        },
        {
            "rgb": (92, 219, 213),
            "blocks": ("diamond_block", "beacon", "prismarine_brick_slab", "prismarine_brick_stairs", "dark_prismarine_slab", "dark_prismarine_stairs", "conduit"),
        },
        {
            "rgb": (74, 128, 255),
            "blocks": ("lapis_lazuli_block"),
        },
        {
            "rgb": (0, 217, 58),
            "blocks": ("emerald_block"),
        },
        {
            "rgb": (129, 86, 49),
            "blocks": ("podzol", "spruce_planks", "spruce_log[axis=y]", "stripped_spruce_log[axis=x]", "spruce_wood", "stripped_spruce_wood", "spruce_sign", "spruce_pressure_plate", "spruce_trapdoor", "spruce_stairs", "spruce_slab", "spruce_fence_gate", "spruce_fence", "spruce_door", "oak_log[axis=x]", "jungle_log[axis=x]", "campfire", "soul_campfire"),
        },
        {
            "rgb": (112, 2, 0),
            "blocks": ("netherrack", "nether_brick_fence", "nether_brick_slab", "nether_brick_stairs", "nether_brick_wall", "nether_brick_chiseled", "nether_brick_cracked", "nether_gold_ore", "nether_quartz_ore", "magma_block", "red_nether_brick_slab", "red_nether_brick_stairs", "red_nether_brick_wall", "crimson_roots", "crimson_fungus", "weeping_vines"),
        },
        {
            "rgb": (209, 177, 161),
            "blocks": ("white_terracotta", "calcite"),
        },
        {
            "rgb": (159, 82, 36),
            "blocks": ("orange_terracotta"),
        },
        {
            "rgb": (149, 87, 108),
            "blocks": ("magenta_terracotta"),
        },
        {
            "rgb": (112, 108, 138),
            "blocks": ("light_blue_terracotta"),
        },
        {
            "rgb": (186, 133, 36),
            "blocks": ("yellow_terracotta"),
        },
        {
            "rgb": (103, 117, 53),
            "blocks": ("lime_terracotta"),
        },
        {
            "rgb": (160, 77, 78),
            "blocks": ("pink_terracotta"),
        },
        {
            "rgb": (57, 41, 35),
            "blocks": ("gray_terracotta", "tuff"),
        },
        {
            "rgb": (135, 107, 98),
            "blocks": ("light_gray_terracotta", "exposed_copper"),
        },
        {
            "rgb": (87, 92, 92),
            "blocks": ("cyan_terracotta"),
        },
        {
            "rgb": (122, 73, 88),
            "blocks": ("purple_terracotta", "purple_shulker_box"),
        },
        {
            "rgb": (76, 62, 92),
            "blocks": ("blue_terracotta"),
        },
        {
            "rgb": (76, 50, 35),
            "blocks": ("brown_terracotta", "pointed_dripstone", "dripstone_block"),
        },
        {
            "rgb": (76, 82, 42),
            "blocks": ("green_terracotta"),
        },
        {
            "rgb": (142, 60, 46),
            "blocks": ("red_terracotta"),
        },
        {
            "rgb": (37, 22, 16),
            "blocks": ("black_terracotta"),
        },
        {
            "rgb": (189, 48, 49),
            "blocks": ("crimson_nylium"),
        },
        {
            "rgb": (148, 63, 97),
            "blocks": ("crimson_planks", "crimson_log[axis=y]", "stripped_crimson_log[axis=x]", "crimson_wood", "stripped_crimson_wood", "crimson_sign", "crimson_pressure_plate", "crimson_trapdoor", "crimson_stairs", "crimson_slab", "crimson_fence_gate", "crimson_fence", "crimson_door"),
        },
        {
            "rgb": (92, 25, 29),
            "blocks": ("crimson_hyphae", "stripped_crimson_hyphae"),
        },
        {
            "rgb": (22, 126, 134),
            "blocks": ("warped_nylium", "oxidized_copper"),
        },
        {
            "rgb": (58, 142, 140),
            "blocks": ("warped_planks", "warped_log[axis=y]", "stripped_warped_log[axis=x]", "warped_wood", "stripped_warped_wood", "warped_sign", "warped_pressure_plate", "warped_trapdoor", "warped_stairs", "warped_slab", "warped_fence_gate", "warped_fence", "warped_door", "weathered_copper"),
        },
        {
            "rgb": (86, 44, 62),
            "blocks": ("warped_hyphae", "stripped_warped_hyphae"),
        },
        {
            "rgb": (20, 180, 133),
            "blocks": ("warped_wart_block"),
        },
        {
            "rgb": (100, 100, 100),
            "blocks": ("deepslate"),
        },
        {
            "rgb": (216, 175, 147),
            "blocks": ("raw_iron_block"),
        },
        {
            "rgb": (127, 167, 150),
            "blocks": ("glow_lichen"),
        },
    ]

    def to_minecraft_color(pxl):
        color = None
        min_distance = None
        for item in blocks:
            # Calculates the "distance" between two RGB colors as if they
            # were points in a 3-dimensional space.
            # The closer they are, the more they look like each other.
            euclidean_distance = sqrt(sum([pow(p - c, 2) for p, c in zip(item["rgb"], pxl)]))

            if min_distance is None or euclidean_distance < min_distance:
                min_distance = euclidean_distance
                color = ", ".join("minecraft:"+block for block in item["blocks"])
        return color

    # Resizing the image and mapping each pixel's color to a minecraft color
    image = image.resize(
        (image.size[0] // lower_image_size_by, image.size[1] // lower_image_size_by)
    )
    image_colors_processed = [
        [to_minecraft_color(pixel) for pixel in row] for row in np.array(image)
    ]

    # Getting the name that the image should have via the given path
    image_name = os.path.splitext(os.path.split(path)[1])[0]

    df = pd.DataFrame(image_colors_processed)

    # Creates - in an error proof manner - the folder structure of the datapack
    with suppress(FileExistsError):
        os.makedirs(f"{path}/data/minecraft/tags/functions")
        os.makedirs(f"{path}/data/pixelart-map/functions")

    if minecraft_version >= '1.13.0':
        if minecraft_version >= '1.13.0' and minecraft_version <= '1.14.4':
            datapack_version = 4
        elif minecraft_version >= '1.15.0' & minecraft_version <= '1.16.1':
            datapack_version = 5
        elif minecraft_version >= '1.16.2' & minecraft_version <= '1.16.5':
            datapack_version = 6
        elif minecraft_version >= '1.17.0' & minecraft_version <= '1.17.1':
            datapack_version = 7
        elif minecraft_version >= '1.18.0' & minecraft_version <= '1.18.1':
            datapack_version = 8
        elif minecraft_version >= '1.18.2':
            datapack_version = 9
    else:
        datapack_version = 4
        raise ValueError("This versions is incompatible with datapacks (below 1.13.0) or the version is writen wrong (correct: x.xx.x | wrong: x.x, x.xx)")

    pack_mcmeta = {
        "pack": {
            "pack_format": datapack_version,
            "description": f"This datapack will generate the image ({image_name}) in your world",
        }
    }
    load_json = {"values": ["pixelart-map:load"]}
    tick_json = {"values": ["pixelart-map:tick"]}

    with open(f"{path}/pack.mcmeta", "w") as file:
        file.write(json.dumps(pack_mcmeta, indent=4))
    with open(f"{path}/data/minecraft/tags/functions/load.json", "w") as file:
        file.write(json.dumps(load_json, indent=4))
    with open(f"{path}/data/minecraft/tags/functions/tick.json", "w") as file:
        file.write(json.dumps(tick_json, indent=4))

    with open(f"{path}/data/pixelart-map/functions/tick.mcfunction", "w") as file:
        file.write("")

    # Making the commands that when ran will build the image's pixel art.
    # This part's had a huge contribution from this thread: https://stackoverflow.com/questions/70512775/how-to-group-elements-in-dataframe-by-row/70546452#70546452
    df = df.rename_axis(index="z", columns="x")

    a = list(
        script(
            df,
            player_x=player_pos[0],
            player_y=player_pos[1],
            player_z=player_pos[2],
        )
    )
    b = list(
        script(
            df.T,
            player_x=player_pos[0],
            player_y=player_pos[1],
            player_z=player_pos[2],
        )
    )
    res = min([a, b], key=len)
    with open(f"{path}/data/pixelart-map/functions/load.mcfunction", "w") as file:
        file.write("\n".join(res))


"""
A collection of neural network code. The first part of the script includes
blocks, which are the building blocks of our models. The second part includes
the actual Pytorch models.
"""
import torch
import torchvision.transforms as transforms

class ConvBlock(torch.nn.Module):
    """
    A ConvBlock represents a convolution. It's not just a convolution however,
    as some common operations (dropout, activation, batchnorm, 2x2 pooling)
    can be set and run in the order mentioned.
    """

    def __init__(
        self,
        dim,
        n_out,
        kernel_size=3,
        stride=1,
        padding=1,
        batchnorm=False,
        dropout=0,
        activation=True,
    ):
        """ A convolution operation """
        super(ConvBlock, self).__init__()
        n_in = int(dim[0])
        self.conv2d = torch.nn.Conv2d(
            n_in, n_out, kernel_size=kernel_size, stride=stride, padding=padding
        )
        self.batchnorm = torch.nn.BatchNorm2d(n_out) if batchnorm else None
        self.activation = torch.nn.ReLU(inplace=True) if activation else None
        self.dropout = torch.nn.Dropout2d(dropout) if dropout else None
        dim[0] = n_out
        dim[1:] = 1 + (dim[1:] + padding * 2 - kernel_size) // stride
        self.n_params = n_out * (n_in * kernel_size * kernel_size + (3 if batchnorm else 1))
        print(
            "Conv2d in %4i out %4i h %4i w %4i k %i s %i params %9i"
            % (n_in, *dim, kernel_size, stride, self.n_params)
        )

    def forward(self, batch):
        """ Forward the 4D batch """
        out = self.conv2d(batch)
        if self.activation:
            out = self.activation(out)
        if self.batchnorm:
            out = self.batchnorm(out)
        if self.dropout:
            out = self.dropout(out)
        return out

class LinearBlock(torch.nn.Module):
    """
    A LinearBlock represents a fully connected layer. It's not just this, as
    some common operations (dropout, activation, batchnorm) can be set and run
    in the order mentioned.
    """

    def __init__(self, dim, n_out, batchnorm=False, dropout=0.0, activation=True):
        """ A fully connected operation """
        super(LinearBlock, self).__init__()
        n_in = int(dim[0])
        self.linear = torch.nn.Linear(n_in, n_out)
        dim[0] = n_out if type(n_out) in (int, float) else n_out[0]
        self.batchnorm = torch.nn.BatchNorm1d(dim[0]) if batchnorm else None
        self.dropout = torch.nn.Dropout(dropout) if dropout > 0.0 else None
        self.activation = torch.nn.ReLU(inplace=True) if activation else None
        self.n_params = n_out * (n_in + (3 if batchnorm else 1))
        print(
            "Linear in %4i out %4i                       params %9i" % (n_in, n_out, self.n_params)
        )

    def forward(self, batch):
        """ Forward the 2D batch """
        out = self.linear(batch)
        if self.activation:
            out = self.activation(out)
        if self.batchnorm:
            out = self.batchnorm(out)
        if self.dropout:
            out = self.dropout(out)
        return out

class PoolBlock(torch.nn.Module):
    """
    A PoolBlock is a pooling operation that happens on a matrix, often between
    convolutional layers, on each channel individually. By default only two are
    supported: max and avg.
    """

    def __init__(self, dim, pool="max", size=None, stride=None):
        """ A pooling operation """
        super(PoolBlock, self).__init__()

        stride = size if stride is None else stride
        if size:
            dim[1:] //= stride
        else:
            size = [int(x) for x in dim[1:]]
            dim[1:] = 1
        if pool == "max":
            self.pool = torch.nn.MaxPool2d(size, stride=stride, padding=0)
        elif pool == "avg":
            self.pool = torch.nn.AvgPool2d(size, stride=stride, padding=0)
        self.n_params = 0

    def forward(self, batch):
        """ Forward the 4D batch """
        out = self.pool(batch)
        return out

class ViewBlock(torch.nn.Module):
    """
    A ViewBlock restructures the shape of our activation maps so they're
    represented as 1D instead of 3D.
    """

    def __init__(self, dim, shape=-1):
        """ A reshape operation """
        super(ViewBlock, self).__init__()
        self.shape = shape
        if self.shape == -1:
            dim[0] = dim[0] * dim[1] * dim[2]
            dim[-2] = 0
            dim[-1] = 0
        else:
            dim[:] = shape

        self.n_params = 0
        print("View             d %4i h %4i w %4i" % (*dim,))

    def forward(self, batch):
        """ Forward the 4D batch into a 2D batch """
        return batch.view(batch.size(0), self.shape)

class Tiny(torch.nn.Module):
    """ A small and quick model """

    def __init__(self, in_dim, n_status, n_out):
        """
        Args:
            in_dim (list): The input size of each example
            n_status (int): Number of status inputs to add
            n_out (int): Number of values to predict
        """
        super(Tiny, self).__init__()
        self.n_status = n_status
        dim = in_dim.copy()
        self.feat = torch.nn.Sequential(
            ConvBlock(dim, 16),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 32),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 48),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 64),
            PoolBlock(dim, "max", 2),
        )
        self.view = ViewBlock(dim)
        dim[0] += n_status
        self.head = torch.nn.Sequential(LinearBlock(dim, n_out, activation=False))
        self.n_params = sum([x.n_params for x in self.feat]) + sum([x.n_params for x in self.head])
        print("Tiny                                          params %9i" % self.n_params)

    def forward(self, batch, status):
        """
        Args:
            batch (4D tensor): A batch of camera input.
            status (1D tensor): Status inputs indicating things like speed.
        """
        out = self.feat(batch)
        out = self.view(out)
        if self.n_status:
            out = torch.cat((out, status), 1)
        out = self.head(out)
        return out

class StarTree(torch.nn.Module):
    """
    A medium-sized model that uses layers with few activation maps to
    efficiently increase the number of layers, and therefore nonlinearities.
    """

    def __init__(self, in_dim, n_status, n_out):
        """
        Args:
            in_dim (list): The input size of each example
            n_status (int): Number of status inputs to add
            n_out (int): Number of values to predict
        """
        super(StarTree, self).__init__()
        self.n_status = n_status
        dim = in_dim.copy()
        self.feat = torch.nn.Sequential(
            ConvBlock(dim, 64, dropout=0.25),
            ConvBlock(dim, 16),
            ConvBlock(dim, 32),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 24),
            ConvBlock(dim, 48),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 32),
            ConvBlock(dim, 64),
            PoolBlock(dim, "max", 2),
            ConvBlock(dim, 40),
            ConvBlock(dim, 80, dropout=0.25),
            PoolBlock(dim, "max", 2),
        )
        self.view = ViewBlock(dim)
        dim[0] += n_status
        self.head = torch.nn.Sequential(
            LinearBlock(dim, 50), LinearBlock(dim, n_out, activation=False),
        )
        self.n_params = sum([x.n_params for x in self.feat]) + sum([x.n_params for x in self.head])
        print("StarTree                                  params %9i" % self.n_params)

    def forward(self, batch, status):
        """
        Args:
            batch (4D tensor): A batch of camera input.
            status (1D tensor): Status inputs indicating things like speed.
        """
        out = self.feat(batch)
        out = self.view(out)
        if self.n_status:
            out = torch.cat((out, status), 1)
        out = self.head(out)
        return out

def train_epoch(device, model, optimizer, criterion, loader):
    """ Run the optimzer over all batches in an epoch """
    model.train()
    epoch_loss = 0
    batch_index = 0
    for batch_index, (examples, statuses, labels) in enumerate(loader):
        optimizer.zero_grad()
        guesses = model(examples.to(device), statuses.to(device))
        loss = criterion(guesses, labels.to(device))
        loss.backward()
        optimizer.step()
        epoch_loss += loss.item()
    return epoch_loss / (batch_index + 1)

def test_epoch(device, model, criterion, loader):
    """ Run the evaluator over all batches in an epoch """
    model.eval()
    epoch_loss = 0
    batch_index = 0
    with torch.no_grad():
        for batch_index, (examples, statuses, labels) in enumerate(loader):
            guesses = model(examples.to(device), statuses.to(device))
            loss = criterion(guesses, labels.to(device))
            epoch_loss += loss.item()
    return epoch_loss / (batch_index + 1)

def compose_transforms(transform_config):
    """ Apply all image transforms """
    transform_list = []
    for perturb_config in transform_config:
        if perturb_config["name"] == "colorjitter":
            transform = transforms.ColorJitter(
                brightness=perturb_config["brightness"],
                contrast=perturb_config["contrast"],
                saturation=perturb_config["saturation"],
                hue=perturb_config["hue"],
            )
            transform_list.append(transform)
    transform_list.append(transforms.ToTensor())
    return transforms.Compose(transform_list)


from typing import final
import pandas as pd
import numpy as np
import os

final_df=pd.read_csv("prepared_final_data.csv")

print(final_df)
values=final_df["pollution"].values
print(values)
print(final_df.columns)
"""# Normalized the data"""

from sklearn.preprocessing import MinMaxScaler

# values = final_df.values
print(values)
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_dataset = scaler.fit_transform(values.reshape(-1,1))

scaled_dataset

# Creating a window for previous data
def to_supervised(window_size,train):
  X = []
  Y = []
  for i in range(window_size, len(train)):
    X.append(train[i-window_size:i,:])
    Y.append(train[i,0:1])

  return np.array(X), np.array(Y)

feature,label = to_supervised(window_size=5, train=scaled_dataset)

n_train = 24*365
X_train, X_test = feature[n_train:,] , feature[:n_train,]
print('X_train' ,X_train.shape)
print('X_test' ,X_test.shape)

Y_train, Y_test = label[n_train:,] , label[:n_train,]
print('Y_train' ,Y_train.shape)
print('Y_test' ,Y_test.shape)

import keras

from keras.models import Sequential
from keras.layers import Dense, Dropout,LSTM

model = Sequential()

model.add(LSTM(units = 50, return_sequences = True, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dropout(0.2))

model.add(LSTM(units = 50, return_sequences = True))
model.add(Dropout(0.2))

model.add(LSTM(units = 50))
model.add(Dropout(0.2))

model.add(Dense(units = 1))

model.compile(optimizer = 'adam', loss = 'mean_squared_error')

from keras.callbacks import EarlyStopping

es_callback = EarlyStopping(monitor='val_loss', patience=3,min_delta=0.01)
path = 'air_pollution_forecasting_model'

isdir = os.path.isdir(path)
print(isdir)
if isdir:
    reconstructed_model = keras.models.load_model("air_pollution_forecasting_model")
    model = reconstructed_model
else:
    model.fit(X_train, Y_train, validation_split = 0.1, epochs = 10, batch_size = 32, callbacks=[es_callback])
    model.save("air_pollution_forecasting_model")
breakpoint()
Y_pred = np.round(model.predict(X_test),2)

from sklearn.metrics import mean_squared_error
mse = mean_squared_error(Y_test, Y_pred)

rmse = np.sqrt(mse)
print(rmse)

# Scaling back to the original scale
d = scaled_dataset[:8760,:]
print('dummy',d.shape)
print('Y_pred',Y_pred.shape)
Y_predicted = np.concatenate((Y_pred,d[:8760,1:]), axis =1)
print('concat y_pred',Y_pred.shape)
Y_tested = np.concatenate((Y_test, d[:8760,1:]), axis = 1)
print('concat Y_test', Y_test.shape)

Y_predicted = scaler.inverse_transform(Y_predicted)
Y_tested = scaler.inverse_transform(Y_tested)
Y_predicted = Y_predicted[:,0:1]
Y_tested = Y_tested[:,0:1]
print('Y_tested', Y_tested.shape)
print('Y_predicted', Y_predicted.shape)

import matplotlib.pyplot as plt

plt.plot(Y_predicted[:100,:], color= 'green')
plt.plot(Y_tested[:100,:] , color = 'red')
plt.title("Air Pollution Prediction (Multivariate)")
plt.xlabel("Date")
plt.ylabel("Pollution level")
plt.savefig("results.png")

import pickle
pickle.dump(scaler, open('min_max_scaler.pkl','wb'))


#import networkx as nx
#import matplotlib.pyplot as plt
import json
import pprint
from TwitterModule import *
import time
from datetime import datetime
#Set up api and global variables
twitter_api = oauth_login()#twitter api for grabbing data
#dates = [330,331,401,402,403]
dates = [401,402,403,404,405,406,407]

for day in dates:
    print(day)
    names = ['@itsnotdrew','@davidhogg111','@IngrahamAngle','@sleepnumber','@ATT','@Allstate','@esurance','@Bayer','@RocketMortgage','@LibertyMutual','@Arbys','@TripAdvisor','@Nestle','@hulu','@Wayfair','@FoxNews','#BoycottIngramAdverts','#boycottLauraIngraham','#FireIngraham','#FireLauraIngraham']
    errorLogName = 'errorLog' + str(day) + '_4' +  '.txt'
    errorLog = open(errorLogName,'w')
    for q in names:
        try:
            dateStr = str(day)
            dateDay = dateStr[1:]
            dateDayPlusOne = str(int(dateDay)+1)
            dateMonth = dateStr[0]

            if (dateStr == '331'): #dirty code to fix a logic bug when switching months
                dateDayPlusOne = '01'
                dateMonth = '4'

            until = '2018-0' + dateMonth + '-' + dateDayPlusOne
            tweetsDicitonary = {}
            name = q[1:]
            nameFile = name + dateStr +'_4'+  '.json'
            file = open(nameFile,'w')

            '''
            First search call to twitter_api
            Parameters:
                q: is the search term
                result_type: is whether we want recent, popular or mixed tweets. Currently set to recent
                max_results: is how many results we wan to take in a single call. Is currently 10 for testing
                until: specifies the date that all tweets returned form this call should come before
                        (so all tweets from this call are from 3/28/2018)

            getMaxID parses the maxID from the appropriate string in the search return metadata
            maxid will then be used to call the next batch of tweets. More info on maxid is Available on the search api documentation
            '''
            print(q + 'at ' + str(datetime.now())) #prints twitter user being processed

            response = make_twitter_request(twitter_api.search.tweets,q=q,result_type='recent',count=5, until=until)
            try:
                next_results = response['search_metadata']['next_results']
                getMaxID = dict([ kv.split('=') for kv in next_results[1:].split("&") ])
                maxid = getMaxID['max_id']
            except:
                next_results = ""
                maxid = 0
                line = "\nretrieval error at " + str(datetime.now()) + " while processing beginning call of " + q
                errorLog.write(line)

            '''
            Parameters in response:
                most are the same
                -result_type is mixed (testing)
                -max_results is 100 (testing, but really it should be kept like this)
                -max_id field is at the end of the call, allowing each call of the function to retrieve older and older tweets

            time.sleep(5): Can only call the search api 180 times in 15 minutes, so ~5 seconds. Right now set to one because testing, but should probably be set to 10self.
                            Or, we can edit the make-twitter_request function to handle this error for us

            '''
            for i in range(1,101): #top possible tweets 10,000
                #print(i) #testing code
                try:
                    response = make_twitter_request(twitter_api.search.tweets,q=q,result_type='recent',count=100,until=until,max_id=maxid)
                    next_results = response['search_metadata']['next_results']
                    if (next_results == None):
                        break
                    getMaxID  = dict([ kv.split('=') for kv in next_results[1:].split("&") ])#to get the nextID
                    maxid = getMaxID['max_id']
                   # print(maxid)
                    time.sleep(5)
                except:
                    line = "\nretrieval error at " + str(datetime.now()) + " while processing " + q + ' at loop number ' + str(i)
                    errorLog.write(line)
                    break
                for tweet in response['statuses']:#add each tweet to a dictionary
                    try:
                        tweetsDicitonary[tweet['id']] = tweet
                    except:
                        line = "\ndicitonary error at " + str(datetime.now()) + " while processing " + str(tweet['id'])
                        errorLog.write(line)
                file.seek(0)
            file.seek(0)
            json.dump(tweetsDicitonary,file)
            file.close()
        except:
            line = "\nFatal error at " + str(datetime.now()) + " while processing " + q
            errorLog.write(line)
            json.dump(tweetsDicitonary,file)
            file.close()


import sys
input = sys.stdin.readline

def BFS(y, x, word):
    global ans

    ans = max(ans, len(word))

    for dy, dx in ((0, 1), (0, -1), (1, 0), (-1, 0)):
        ny = y + dy
        nx = x + dx

        if 0 <= ny < R and 0 <= nx < C and data[ny][nx] not in word:
            BFS(ny, nx, word+data[ny][nx])

R, C = map(int, input().split())
ans = 0
data = [input().rstrip() for _ in range(R)]

# print(data)
#
BFS(0, 0, data[0][0])
print(ans)


# coding=utf-8
from __future__ import absolute_import, division, print_function
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, Dataset
from util.custom_dataset import FaceLandmarksDataset, Rescale, ToTensor
import torchvision.models as models
from torchvision import transforms
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt
from torchvision import transforms, utils
import torchvision

class View(nn.Module):
    def __init__(self, shape):
        super(View, self).__init__()
        self.shape = shape

    def forward(self, x):
        return x.view(*self.shape)

class InnerSum(nn.Module):
    def __init__(self):
        super(InnerSum, self).__init__()
    def forward(self, x):
        y = torch.zeros_like(x)
        for i in range(x.size(0)):
            y[i] = x[i].mul(x[i])
        if len(y.shape) == 3:
            return y.sum(2)
        else:
            return y.sum(1)

class ACNN(nn.Module):
    def __init__(self):
        super(ACNN, self).__init__()
        self.inner = InnerSum()
        self.pretrain = models.vgg16(pretrained=True).features[:28]
        self.VGG16 = self.pretrain[:21]
        self.PG_base = nn.Sequential(nn.Conv2d(512, 512, kernel_size=3, padding=1), nn.BatchNorm2d(512), nn.ReLU(),
                                     nn.Conv2d(512, 512, kernel_size=3, padding=1), nn.BatchNorm2d(512), nn.ReLU(),)
        self.PG_attention = nn.Sequential(nn.MaxPool2d(2, stride=2), nn.Conv2d(512, 128, kernel_size=3, padding=1),
                                          nn.BatchNorm2d(128), nn.ReLU(), nn.AdaptiveAvgPool2d((1, 1)), View((-1, 128)),
                                          nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1), nn.Sigmoid())
        self.GG_base = self.pretrain[21:]
        self.GG_attention = nn.Sequential(nn.MaxPool2d(2, stride=2), nn.Conv2d(512, 128, kernel_size=3, padding=1),
                                          nn.BatchNorm2d(128), nn.ReLU(), nn.AdaptiveAvgPool2d((1, 1)), View((-1, 128)),
                                          nn.Linear(128, 64), nn.ReLU(), nn.Linear(64, 1), nn.Sigmoid())
        self.PG24_base = nn.ModuleList([self.PG_base for _ in range(24)])
        self.PG24_alpha = nn.ModuleList([self.PG_attention for _ in range(24)])

        self.pad = nn.ReflectionPad2d(6)
        # self.crop = batch_slice(40, 40, 6, 6)
        self.crop = torchvision.ops.roi_pool
        self.PG_fc = nn.Linear(512*6*6, 64)
        self.GG_fc = nn.Linear(512*14*14, 512)
        self.fc1 = nn.Linear(2048, 1024)
        self.dropout = nn.Dropout(0.5)
        self.fc2 = nn.Linear(1024, 7)

    # def crop_layer(self, img: '(B, C, H, W)', landmarks: '(B, 24, 2)'):
    #     # device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #     pad = nn.ReflectionPad2d(6)  # padding for cropping
    #     img = pad(img)  # (B, 512, 36, 36)
    #     total_crop = torch.zeros((img.size(0), landmarks.size(1), 512, 6, 6), device=self.device)
    #
    #     for i in range(landmarks.size(0)):  # Batch
    #         # crop_per_batch = []
    #         for patch in range(landmarks.size(1)):  # 24 landmarks
    #             total_crop[i, patch, :, :, :] = img[i, :, (int(landmarks[i, patch, 0]) - 3): (int(
    #                 landmarks[i, patch, 0]) + 3),
    #                                             (int(landmarks[i, patch, 1]) - 3): (int(
    #                                                 landmarks[i, patch, 1]) + 3)]  # crop_img: (512, 6, 6)
    #
    #     total_crop = total_crop.permute(1, 0, 2, 3, 4)  # output: (24, B, 512, 6, 6)
    #     return total_crop

    def _branch24(self, crop_img):
        PG_out = []
        for x, base, alpha in zip(crop_img, self.PG24_base, self.PG24_alpha):
            PG_conv2 = base(x)
            PG_reshape = PG_conv2.view(-1, 512*6*6)
            PG_reshape = self.PG_fc(PG_reshape)
            PG_per = PG_reshape * alpha(PG_conv2).view(x.size(0), 1)
            PG_out.append(PG_per)
        return PG_out

    def forward(self, img, landmarks):
        img_feature = self.VGG16(img)  # (B, 512, 28, 28)
        img_pad = self.pad(img_feature)
        # landmarks = landmarks.long()
        crop_img = self.crop(img_pad, landmarks, output_size=(6, 6))
        crop_img = crop_img.view(24, -1, 512, 6, 6)

        GG_conv2 = self.GG_base(img_feature)
        GG_reshape = GG_conv2.view(-1, 512*14*14)
        GG_reshape = self.GG_fc(GG_reshape)
        GG_out = GG_reshape * self.GG_attention(GG_conv2).view(img_feature.size(0), 1)

        # crop_img = self.crop_layer(img_feature, landmarks)
        PG_out = self._branch24(crop_img)
        PG_total = torch.cat(PG_out, dim=1)
        total_out = torch.cat([GG_out, PG_total], dim=1)

        out = self.fc1(total_out)
        out = F.relu(self.dropout(out))
        out = self.fc2(out)
        return out

def landmark_resize(landmarks:'(B, 24, 2)')->'(B*24, 4)':
    bs = landmarks.size(0)
    batch = list(range(bs))
    batch = np.array(batch * 24).reshape(24, -1).T

    point = np.array(list(range(24)) * bs).reshape(bs, -1)
    insert_point = np.insert(landmarks, 0, point, 2)
    insert_batch = np.insert(insert_point, 0, batch, 2)
    new_landmark = insert_batch.reshape(-1, 4)
    return new_landmark

def data_normal(origin_data, size):  # (-1, 1)
    size = size / 2
    norm_data = origin_data.true_divide(size) - 1
    return norm_data

def grid_field(landmarks, cropsize=6):  # landmarks: (B, 24, 2)
    total_crop = []
    landmarks = landmark_resize(landmarks)  # (B*24, 4)
    lm_batch = landmarks[:, 0].long()
    landmarks_x_l = landmarks[:, 2] - (cropsize / 2)
    landmarks_x_r = landmarks[:, 2] + (cropsize / 2)
    landmarks_y_l = landmarks[:, 3] - (cropsize / 2)
    landmarks_y_r = landmarks[:, 3] + (cropsize / 2)

    for i in range(landmarks.size(0)):
        new_h = torch.linspace(landmarks_x_l[i], landmarks_x_r[i] - 1, cropsize).view(-1, 1).repeat(1, cropsize)
        new_w = torch.linspace(landmarks_y_l[i], landmarks_y_r[i] - 1, cropsize).repeat(cropsize, 1)
        grid = torch.cat((new_w.unsqueeze(2), new_h.unsqueeze(2)), dim=2)
        grid = grid.unsqueeze(0)
        grid = data_normal(grid, size=28)
        total_crop.append(grid)
    total_crop = torch.cat(total_crop, dim=0)
    return lm_batch, total_crop

def roi_select(landmarks: '(B, 4, 2)') -> '(B*24, 5)':
    landmarks = landmark_resize(landmarks)
    landmarks_right = landmarks[:, 2:] + 3
    landmarks_left = landmarks[:, 2:] - 3
    landmarks = torch.cat([landmarks[:, 0].view(-1, 1), landmarks_left, landmarks_right], dim=1)

    return landmarks

# if __name__ == '__main__':
#     model = ACNN()
#     shuffle = False
#     device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu")
#     model.to(device)
#     train_set = FaceLandmarksDataset(csv_file='train_acnn.csv', root_dir='original/',
#                                      transform=ToTensor())
#     test_set = FaceLandmarksDataset(csv_file='test_acnn.csv', root_dir='original/',
#                                     transform=ToTensor())
#     train_loader = DataLoader(dataset=train_set, shuffle=shuffle, batch_size=4, num_workers=0,
#                               pin_memory=True)
#     test_loader = DataLoader(dataset=test_set, shuffle=shuffle, batch_size=4, num_workers=8,
#                              pin_memory=True)
#     for step, batch in enumerate(train_loader):
#         imgs, landmarks, targets = batch['image'], batch['landmarks'] / 8. + 6, batch['label']
#         landmarks = roi_select(landmarks)
#
#         imgs, landmarks, targets = imgs.to(device), landmarks.to(device), targets.to(device)
#         logits = model(imgs, landmarks)
#         print(logits.size())
#         break


import g2d
from boardgame import BoardGame
from time import time

W, H = 40, 40
LONG_PRESS = 0.5

class BoardGameGui:
    def __init__(self, g: BoardGame):
        self._game = g
        self._downtime = 0
        self.update_buttons()

    def tick(self):
        if g2d.key_pressed("LeftButton"):
            self._downtime = time()
        elif g2d.key_pressed("a"):
            self._game.automatism()
            self.update_buttons()
        elif g2d.key_pressed("h"):
            self._game.hint()
            self.update_buttons()
        elif g2d.key_pressed("u"):
            print(self._game.unsolvable())
        elif g2d.key_released("LeftButton"):
            mouse = g2d.mouse_position()
            x, y = mouse[0] // W, mouse[1] // H
            if time() - self._downtime > LONG_PRESS:
                self._game.flag_at(x, y)
            else:
                self._game.play_at(x, y)
            self.update_buttons()

    def update_buttons(self):
        g2d.clear_canvas()
        g2d.set_color((0, 0, 0))
        cols, rows = self._game.cols(), self._game.rows()
        for y in range(1, rows):
            g2d.draw_line((0, y * H), (cols * W, y * H))
        for x in range(1, cols):
            g2d.draw_line((x * W, 0), (x * W, rows * H))
        for y in range(rows):
            for x in range(cols):
                value = self._game.value_at(x, y)
                if value == '1': #Settaggio colori
                    g2d.set_color((200,200,200))
                    g2d.fill_rect((x*40, y*40, 39, 39))
                elif value == '2':
                    g2d.set_color((0,0,0))
                    g2d.fill_rect((x*40, y*40, 39, 39))
                center = x * W + W//2, y * H + H//2
                g2d.draw_text_centered(value, center, H//2)
        g2d.update_canvas()
        if self._game.finished():
            g2d.alert(self._game.message())
            g2d.close_canvas()

def gui_play(game: BoardGame):
    g2d.init_canvas((game.cols() * W, game.rows() * H))
    ui = BoardGameGui(game)
    g2d.main_loop(ui.tick)


import sys
import json
import time
import numpy as np
import argparse

from operator import itemgetter
from scipy.sparse import csc_matrix
from scipy.sparse import csr_matrix
from scipy.sparse import dok_matrix
from math import sqrt
from math import log

from upper_learning_corpus import LearningCorpus
from sparse_matrix import *
from ranking import *

def convert_counts_to_pmi2(matrix, rowSum, colSum):

    totalSum = sum(rowSum.values())
    sys.stderr.write('Converting to csc_matrix format... ')
    startTime = time.time()
    matrix = coo_matrix(matrix)
    sys.stderr.write('done. Time taken: '+str(time.time()-startTime)+' secs\n')
    totalEntries = len(matrix.row)
    sys.stderr.write('Num entries: '+str(totalEntries)+'\n')

    numEntries = 1.
    # symmetric matrix
    for r, c, val in zip(np.nditer(matrix.row), np.nditer(matrix.col), np.nditer(matrix.data, op_flags=['readwrite'])):
        pi, pj, pij = (1.*val/rowSum[str(r)], 1.*val/colSum[str(c)], 1.*val/totalSum)
        val[...] = log(pij/(pi*pj))

        if numEntries% 1000000 == 0: sys.stderr.write(str(numEntries)+' ')
        numEntries += 1

    sys.stderr.write('done!\n')
    return csc_matrix((matrix.data, (matrix.row, matrix.col)), shape=matrix.shape)

def convert_counts_to_pmi(matrix, rowSum, colSum):

    totalSum = sum(rowSum.values())
    sys.stderr.write('Converting to dok_matrix format... ')
    startTime = time.time()
    matrix = dok_matrix(matrix)
    sys.stderr.write('done. Time taken: '+str(time.time()-startTime)+' secs\n')
    totalEntries = len(matrix)
    sys.stderr.write('Num entries: '+str(totalEntries)+'\n')

    r, c = matrix.shape
    numEntries = 1.
    # symmetric matrix
    if r == c:
        for key, val in matrix.iteritems():
            i, j = key
            i, j = (str(i), str(j))
            if int(i) <= int(j):
                pi, pj, pij = (1.*val/rowSum[i], 1.*val/colSum[j], 1.*val/totalSum)
                pmi = log(pij/(pi*pj))
                matrix[int(i), int(j)] = pmi
                matrix[int(j), int(i)] = pmi
            else:
                pass

            if numEntries% 1000000 == 0: sys.stderr.write(str(numEntries)+' ')
            numEntries += 1
    else:
        for key, val in matrix.iteritems():
            i, j = key
            i, j = (str(i), str(j))
            pi, pj, pij = (1.*val/rowSum[i], 1.*val/colSum[j], 1.*val/totalSum)
            matrix[int(i), int(j)] = log(pij/(pi*pj))

            if numEntries% 1000000 == 0: sys.stderr.write(str(numEntries)+' ')
            numEntries += 1

    sys.stderr.write('done!\n')
    return csc_matrix(matrix)

if __name__=='__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--matrixfile", type=str, default=None, help="Matrix file name")
    parser.add_argument("-d", "--dictfile", type=str, help="Dictionary file name")
    parser.add_argument("-o", "--outputfile", type=str, default=None, help="Output file name")

    args = parser.parse_args()

    outFileName = args.outputfile
    dictFile = open(args.dictfile, 'r')
    values = dictFile.readline().strip().split()
    if len(values) == 3:
        colCutoff, rowCutoff, windowSize = values
    else:
        colCutoff, windowSize = values
        rowCutoff = 0.

    vocab = json.loads(dictFile.readline())
    wordFeatures = json.loads(dictFile.readline())
    rowSum = json.loads(dictFile.readline())
    colSum = json.loads(dictFile.readline())
    contextMat = load_sparse_matrix(args.matrixfile)

    sys.stderr.write("windowSize: "+str(windowSize)+" colCutoff: "+str(colCutoff)+" rowCutoff: "+str(rowCutoff)+'\n')
    sys.stderr.write("featLen: "+str(len(wordFeatures))+" vocabLen: "+str(len(vocab))+'\n')
    sys.stderr.write('Read the matrix!\n')

    ''' Convert the matrix here! '''
    contextMat = convert_counts_to_pmi(contextMat, rowSum, colSum)

    sys.stderr.write('Writing the matrix now... ')
    if outFileName is None: outFileName = args.dictfile.replace('.dict', '_pmi')
    save_sparse_matrix(outFileName, contextMat)
    sys.stderr.write('done!\n')


from flask import abort
from flask_restx import Resource, Namespace, Model, fields, reqparse
from infraestructura.alumnos_repo import AlumnosRepo
from api.cursos_api import modeloCurso

from flask_restx.inputs import date

repo = AlumnosRepo()

nsAlumno = Namespace('Alumnos', description='Administrador de Alumno')

modeloAlumnoSinID = Model('AlumnoSinID',{
    'nombre': fields.String(),
    'direccion': fields.String(),
    'sexo':fields.String(),
    'edad':fields.Integer(),
    'fecha_baja': fields.Date()
    })

modeloAlumno = modeloAlumnoSinID.clone('Alumno',{
    'id': fields.Integer(),
    #'cursos': fields.Nested(modeloCurso, skip_none=True)
})

# modeloBusqueda = Model('BusquedaFechas', {
#     'desde': fields.Date(),
#     'hasta': fields.Date()
# })
nsAlumno.models[modeloAlumno.name] = modeloAlumno
nsAlumno.models[modeloAlumnoSinID.name] = modeloAlumnoSinID
# nsAlumno.models[modeloBusqueda.name] = modeloBusqueda

nuevoAlumnoParser = reqparse.RequestParser(bundle_errors=True)
nuevoAlumnoParser.add_argument('nombre', type=str, required=True)
nuevoAlumnoParser.add_argument('direccion', type=str, required=True)
nuevoAlumnoParser.add_argument('sexo', type=str, required=True)
nuevoAlumnoParser.add_argument('edad', type=int, required=True)
nuevoAlumnoParser.add_argument('fecha_baja', type=date, required=False)

editarAlumnoParser = nuevoAlumnoParser.copy()
editarAlumnoParser.add_argument('id', type=int, required=True)

@nsAlumno.route('/')
class AlumnosResource(Resource):
    # @nsAlumno.marshal_list_with(modeloAlumno)
    # def get(self):
    #     return repo.get_all()

    @nsAlumno.marshal_list_with(modeloAlumno)
    def get(self):
        return repo.get_all()

    @nsAlumno.expect(modeloAlumnoSinID)
    @nsAlumno.marshal_with(modeloAlumno)
    def post(self):
        data = nuevoAlumnoParser.parse_args()
        Alumno = repo.agregar(data)
        if Alumno:
            return Alumno, 201
        abort(500)

@nsAlumno.route('/<int:id>')
class AlumnoResource(Resource):
    @nsAlumno.marshal_with(modeloAlumno)
    def get(self, id):
        Alumno = repo.get_by_id(id)
        if Alumno:
            return Alumno, 200
        abort(404)

    @nsAlumno.expect(modeloAlumno)
    def put(self, id):
        data = editarAlumnoParser.parse_args()
        if repo.modificar(id, data):
            return 'Alumno actualizado', 200
        abort(404)

# @nsAlumno.route('/buscar/<string:desde>/<string:hasta>/')
# class AlumnoResource(Resource):
#     @nsAlumno.marshal_list_with(modeloAlumno)
#     def get(self, desde, hasta):
#         l = repoLep.buscar(desde, hasta)
#         if l:
#              a = []
#              for x in l:
#               h = repo.get_by_id(x.Alumno_id)
#              a.append(h)
#              return l, 200
#         abort(404)

@nsAlumno.route('/baja/<int:id>')
class AlumnoResource(Resource):

    def put(self, id):
        if repo.baja(id):
            return 'Alumno dado de baja', 200
        abort(400)

@nsAlumno.route('/buscar/<int:curso>')
class AlumnoResource(Resource):
    @nsAlumno.marshal_list_with(modeloAlumno)
    def get(self, curso):
        l = repo.get_alumno_curso(curso)
        if l:

             return l, 200
        abort(404)


import pandas as pd

def read_fasta(file_path):
    sequences = {"Header": [], "Sequence": []}
    current_header = None
    current_sequence = ""

    with open(file_path, "r") as file:
        for line in file:
            line = line.strip()
            if line.startswith(">"):
                # New header found
                if current_header is not None:
                    sequences["Header"].append(current_header)
                    sequences["Sequence"].append(current_sequence)
                current_header = line[1:]
                current_sequence = ""
            else:
                # Continue building the sequence
                current_sequence += line

        # Add the last sequence
        if current_header is not None:
            sequences["Header"].append(current_header)
            sequences["Sequence"].append(current_sequence)

    return pd.DataFrame(sequences)

def extract_label(header):
    # Extract label after the "|" symbol
    parts = header.split("|")
    if len(parts) > 1:
        return parts[1].strip()
    else:
        return None

file_path = "data/pharos/pharos.fasta"
fasta_df = read_fasta(file_path)

fasta_df["Label"] = fasta_df["Header"].apply(extract_label)
tclin_df = fasta_df[fasta_df["Label"] == "Tclin"]
tdark_df = fasta_df[fasta_df["Label"] == "Tdark"]
length_tclin_df = len(tclin_df)

random_tdark_df = tdark_df.sample(n=length_tclin_df, random_state=42)

from sklearn.model_selection import train_test_split
import os

# Assuming tclin_df and tdark_df are already defined

# Define the test size
test_size = 0.2

# Split the positive sequences (Tclin) into train and test sets
tclin_train, tclin_test = train_test_split(
    tclin_df, test_size=test_size, random_state=42
)

# Split the negative sequences (Tdark) into train and test sets
tdark_train, tdark_test = train_test_split(
    random_tdark_df, test_size=test_size, random_state=42
)

# Create folders if they don't exist

train_folder = "data/pharos/fastadata/Train"
test_folder = "data/pharos/fastadata/Independent_Test"

# Create folders if they don't exist
for folder in [train_folder, test_folder]:
    if not os.path.exists(folder):
        os.makedirs(folder)

if not os.path.exists(train_folder):
    os.makedirs(train_folder)
if not os.path.exists(test_folder):
    os.makedirs(test_folder)

# Function to extract header before the '|' symbol
def extract_header(identifier):
    return identifier.split("|")[0]

# Function to write sequences to fasta file
def write_fasta(filename, dataframe):
    with open(filename, "w") as file:
        for index, row in dataframe.iterrows():
            header = extract_header(row["Header"])
            file.write(f">{header}\n{row['Sequence']}\n")

# Save the sequences to FASTA files in the train and test folders
write_fasta(os.path.join(train_folder, "positive_train_sequence.fasta"), tclin_train)
write_fasta(os.path.join(test_folder, "positive_test_sequence.fasta"), tclin_test)
write_fasta(os.path.join(train_folder, "negative_train_sequence.fasta"), tdark_train)
write_fasta(os.path.join(test_folder, "negative_test_sequence.fasta"), tdark_test)


from unicodedata import mirrored
import numpy as np
import inspect
import unittest

def select_alternating_columns(a: np.ndarray) -> np.ndarray:
    """
    Select alternating columns starting from the 0-th
    index of `a`. `a` will be at least 2 dimensions.

    >>> a = np.array([[0, 1, 2],
    ...               [3, 4, 5]])
    >>> select_alternating_columns(a)
    array([[0, 2],
           [3, 5]])
    """
    if a.shape[1]%2 == 0:
        valid_columns = np.array([True if i % 2 == 0 else False for i in range(a.shape[1])])
        return a[:, valid_columns]
    valid_columns = np.array([True if i % 2 == 0 else False for i in range(a.shape[1])])
    return a[:, valid_columns]

def popcount_rows(a: np.ndarray) -> np.ndarray:
    """
    Return an array containing the popcount of every row
    in `a`. `a` is 2d and consists of 0s and 1s only.

    >>> a = np.array([[0, 0, 1],
    ...               [0, 0, 0],
    ...               [1, 0, 1],
    ...               [1, 1, 1]])
    >>> popcount_rows(a)
    array([1, 0, 2, 3])
    """
    return np.array([np.sum(row) for row in a])

def remove_all_zero_rows(a: np.ndarray) -> np.ndarray:
    """
    Removes any rows that entirely consist of zeros from `a`.

    >>> a = np.array([[0, 0, 0],
    ...               [0, 0, 1]])
    >>> remove_all_zero_rows(a)
    array([[0, 0, 1]])
    """
    count_non_zero = np.array([np.sum(row) for row in a])
    return a[count_non_zero != 0]

def swap_halves(a: np.ndarray) -> np.ndarray:
    """
    Swaps the front and back halves of `a`, which
    is at least 2 dimensions. If the array's size
    is odd, includes the middle element as the
    first element of the back half.

    >>> swap_halves(np.array([0, 1, 2, 3]))
    array([2, 3, 0, 1])
    >>> swap_halves(np.array([0, 1, 2]))
    array([1, 2, 0])
    >>> a = np.reshape(range(8), [4, 2])
    >>> a
    array([[0, 1],
           [2, 3],
           [4, 5],
           [6, 7]])
    >>> swap_halves(a)
    array([[4, 5],
           [6, 7],
           [0, 1],
           [2, 3]])
    """
    firs_half = a[:len(a) // 2]
    second_half = a[len(a) // 2:]
    return np.concatenate([second_half, firs_half])

def trim_zeros_on_edges_2d(a: np.ndarray) -> np.ndarray:
    """
    Trims zeros around a rectangular 1-delimited section.
    The section delimited by 1s will always be
    rectangular and there is only one such section.
    `a` will be 2d and consist of 0s and 1s only.

    >>> a = np.array([[0, 0, 0, 0, 0, 0],
    ...               [0, 1, 1, 1, 0, 0],
    ...               [0, 1, 0, 1, 0, 0],
    ...               [0, 1, 1, 1, 0, 0],
    ...               [0, 0, 0, 0, 0, 0]])
    >>>
    >>> trim_zeros_on_edges_2d(a)
    array([[1, 1, 1],
           [1, 0, 1],
           [1, 1, 1]])
    """
    #rows with all zeros
    b = np.array([np.sum(row) for row in a])
    zero_columns = np.array([True if i == 0 else False for i in b])
    #columns with all zeros
    c = np.array([np.sum(column) for column in a.T])
    zero_rows = np.array([True if i == 0 else False for i in c])
    #select only rows and columns that are not all zeros
    v = a[~zero_columns, :]
    v = v[:, ~zero_rows]
    return v

def one_hot_encode_1d(a: np.ndarray) -> np.ndarray:
    """
    One hot encode every row in `a`. Values of `a` are
    unique positive whole numbers or zero in the range [0-n).

    >>> one_hot_encode_1d(np.array([3, 0, 1, 2]))
    array([[0, 0, 0, 1],
           [1, 0, 0, 0],
           [0, 1, 0, 0],
           [0, 0, 1, 0]])
    """
    #max value of a
    m = a.max() + 1
    return np.array([[ 1 if i == v else 0 for i in range(m)] for v in a])

def make_chessboard(size: int) -> np.ndarray:
    """
    Makes a 2d chessboard pattern with both dimensions
    equal to `size`. The top-left corner should be 0.
    `size` must be >= 0.

    >>> make_chessboard(3)
    array([[0, 1, 0],
           [1, 0, 1],
           [0, 1, 0]])
    >>> make_chessboard(4)
    array([[0, 1, 0, 1],
           [1, 0, 1, 0],
           [0, 1, 0, 1],
           [1, 0, 1, 0]])
    """
    return np.array([[0 if i % 2 == j % 2 else 1 for j in range(size)] for i in range(size)])

def quad(a: np.ndarray) -> np.ndarray:
    """
    Repeat the array horizontally and vertically.

    >>> quad(np.array([0, 1]))
    array([[0, 1, 0, 1],
           [0, 1, 0, 1]])
    >>> quad(np.array([[0, 1], [2, 3]]))
    array([[0, 1, 0, 1],
           [2, 3, 2, 3],
           [0, 1, 0, 1],
           [2, 3, 2, 3]])
    """
    return np.tile(a, (2, 2))

def reflect_quad(a: np.ndarray) -> np.ndarray:
    """
    Repeat the array horizontally and vertically but
    also flip/mirror/reflect the repeated array around
    the middle.

    >>> reflect_quad(np.array([0, 1, 2]))
    array([[0, 1, 2, 2, 1, 0],
           [0, 1, 2, 2, 1, 0]])
    >>> a = np.array([[0, 1],
    ...               [2, 3]])
    >>> reflect_quad(a)
    array([[0, 1, 1, 0],
           [2, 3, 3, 2],
           [2, 3, 3, 2],
           [0, 1, 1, 0]])
    """
    #flip vertical and join rows
    b = []
    if len(a.shape) > 1:
        b = np.flip(a, 0)
        b = np.concatenate([a, b])
    c = []
    #flip horizontal and join columns
    if a.shape[0] > 1:
        c = np.flip(b, 1)
        c = np.concatenate([b, c], 1)
    return c

def rows_where_bits_set_at_idxes(a: np.ndarray,
                                 set_idxes: np.ndarray) -> np.ndarray:
    """
    Return a list of indexes of rows with bits
    set at indexes specified by `set_idxes`.

    >>> a = np.array([[1, 0, 1, 0],
    ...               [0, 1, 1, 0],
    ...               [0, 1, 0, 1],
    ...               [0, 1, 1, 1]])
    >>> rows_where_bits_set_at_idxes(a, np.array([1, 3]))
    array([2, 3], dtype=int64)
    """
    return np.array([i for i in range(len(a)) if np.any(a[i, set_idxes])])

class Test(unittest.TestCase):
    def test_select_alternating_columns_even(self):
        """ select alternating columns (even length)
        """
        a = np.reshape(np.arange(16), [4, 4])
        actual = select_alternating_columns(a)
        expected = np.array([[0, 2], [4, 6], [8, 10], [12, 14]])
        np.testing.assert_equal(actual, expected)

    def test_select_alternating_columns_odd(self):
        """ select alternating columns (odd length)
        """
        a = np.reshape(np.arange(6), [2, 3])
        actual = select_alternating_columns(a)
        expected = np.array([[0, 2], [3, 5]])
        np.testing.assert_equal(actual, expected)

    def test_popcount_rows(self):
        """ popcount rows
        """
        a = np.array([[0, 0, 1],
                      [0, 0, 0],
                      [1, 0, 1],
                      [1, 1, 1]])
        actual = popcount_rows(a)
        expected = np.array([1, 0, 2, 3])
        np.testing.assert_equal(actual, expected)

    def test_remove_all_zero_rows(self):
        """ remove all zero rows (integer)
        """
        a = np.array([[0, 0, 0], [0, 0, 1]])
        actual = remove_all_zero_rows(a)
        expected = np.array([[0, 0, 1]])
        np.testing.assert_equal(actual, expected)

    def test_remove_all_zero_rows_float(self):
        """ remove all zero rows (float)
        """
        a = np.array([[0., 0., 0.], [0., 0., 1.]])
        actual = remove_all_zero_rows(a)
        expected = np.array([[0., 0., 1.]])
        np.testing.assert_equal(actual, expected)

    def test_swap_halves_even(self):
        """ swap halves (even length)
        """
        a = np.arange(8)
        actual = swap_halves(a)
        expected = np.array([4, 5, 6, 7, 0, 1, 2, 3])
        np.testing.assert_equal(actual, expected)

    def test_swap_halves_odd(self):
        """ swap halves (odd length)
        """
        a = np.arange(7)
        actual = swap_halves(a)
        expected = np.array([3, 4, 5, 6, 0, 1, 2])
        np.testing.assert_equal(actual, expected)

    def test_swap_halves_2d_arr(self):
        """ swap halves (2d array)
        """
        a = np.reshape(np.arange(8), [4, 2])
        actual = swap_halves(a)
        expected = np.array([[4, 5], [6, 7], [0, 1], [2, 3]])
        np.testing.assert_equal(actual, expected)

    def test_trim_zeroes_on_edges_2d(self):
        """ trim zeros on edges 2d
        """
        a = np.array([[0, 0, 0, 0, 0, 0],
                      [0, 1, 1, 1, 0, 0],
                      [0, 1, 0, 1, 0, 0],
                      [0, 1, 1, 1, 0, 0],
                      [0, 0, 0, 0, 0, 0]])
        expected = np.array([[1, 1, 1],
                             [1, 0, 1],
                             [1, 1, 1]])
        actual = trim_zeros_on_edges_2d(a)
        np.testing.assert_equal(actual, expected)

    def test_one_hot_encode_1d(self):
        """ one hot encode 1d
        """
        a = np.array([3, 0, 1, 2])
        expected = np.array([[0, 0, 0, 1],
                             [1, 0, 0, 0],
                             [0, 1, 0, 0],
                             [0, 0, 1, 0]])
        actual = one_hot_encode_1d(a)
        np.testing.assert_equal(actual, expected)

    def test_make_chessboard_even(self):
        """ make chessboard (even size)
        """
        expected = np.array([[0, 1, 0, 1],
                             [1, 0, 1, 0],
                             [0, 1, 0, 1],
                             [1, 0, 1, 0]])
        actual = make_chessboard(4)
        np.testing.assert_equal(actual, expected)

    def test_make_chessboard_odd(self):
        """ make chessboard (odd size)
        """
        expected = np.array([[0, 1, 0],
                             [1, 0, 1],
                             [0, 1, 0]])
        actual = make_chessboard(3)
        np.testing.assert_equal(actual, expected)

    def test_quad(self):
        """ quad
        """
        a = np.array([0, 1])
        expected = np.array([[0, 1, 0, 1],
                             [0, 1, 0, 1]])
        actual = quad(a)
        np.testing.assert_equal(actual, expected)

    def test_quad_larger(self):
        """ quad (larger array)
        """
        a = np.array([[0, 1],
                      [2, 3]])
        expected = np.array([[0, 1, 0, 1],
                             [2, 3, 2, 3],
                             [0, 1, 0, 1],
                             [2, 3, 2, 3]])
        actual = quad(a)
        np.testing.assert_equal(actual, expected)

    def test_reflect_quad(self):
        """ reflect quad
        """
        a = np.array([0, 1, 2])
        expected = np.array([[0, 1, 2, 2, 1, 0],
                             [0, 1, 2, 2, 1, 0]])
        actual = reflect_quad(a)
        np.testing.assert_equal(actual, expected)

    def test_reflect_quad_larger(self):
        """ reflect quad (larger array)
        """
        a = np.array([[0, 1],
                      [2, 3]])
        expected = np.array([[0, 1, 1, 0],
                             [2, 3, 3, 2],
                             [2, 3, 3, 2],
                             [0, 1, 1, 0]])
        actual = reflect_quad(a)
        np.testing.assert_equal(actual, expected)

    def test_rows_where_bits_set_at_idxes(self):
        """ rows where bits are set at indexes
        """
        a = np.array([[1, 0, 1, 0],
                      [0, 1, 1, 0],
                      [0, 1, 0, 1],
                      [0, 1, 1, 1]])
        expected = np.array([2, 3])
        set_idxes = np.array([1, 3])
        actual = rows_where_bits_set_at_idxes(a, set_idxes)
        np.testing.assert_equal(actual, expected)

test_src = inspect.getsource(Test)
unittest.TestLoader.sortTestMethodsUsing = lambda _, x, y: (
    test_src.index(f"def {x}") - test_src.index(f"def {y}")
)

#run tests
if __name__ == "__main__":
    unittest.main()


import numpy as np
from .utils import LinearAnnealer,ExponentialAnnealer
import tqdm
import torch
import torch.nn as nn
import wandb
from progress.bar import Bar
from array2gif import write_gif
import copy
from .utils import set_seed
from .utils import save_rewards_meanvar_plot,get_logger,MLP,ReplayMemory
import logging
import time
from torch.distributions.categorical import Categorical

# With spinning up help ;)

class VPG:
    def __init__(self, env, config):
        for k, v in config.items():
            setattr(self, k, v)
        print(config)
        self.env = env
        self.config = copy.deepcopy(config)
        self.reset(self.seed)

    def reset(self, seed):
        self.seed = seed
        set_seed(self.seed)
        self.env.seed(self.seed)
        self.env.action_space.seed(self.seed)
        obs_size = self.env.observation_space.n if 'n' in self.env.observation_space.__dict__ else self.env.observation_space._shape[0]
        self.policy = MLP(self.nUnits, obs_size,self.env.action_space.n).to(self.device)

        self.optimizer = torch.optim.Adam(self.policy.parameters(), lr=self.lr)

    def get_policy(self,s):
        return Categorical(logits=self.policy(s))

    def get_action(self,s):
        return self.get_policy(s).sample()

    def update_net(self,ep_mem):

        def r_to_go(rewards):
            return torch.cumsum(rewards.flip(dims=[0]),dim=0).flip(dims=[0])

        s = torch.stack([exp.s for exp in ep_mem.memory])
        a = torch.stack([exp.a for exp in ep_mem.memory])
        r = torch.stack([exp.r for exp in ep_mem.memory])
        r = r_to_go(r)
        self.optimizer.zero_grad()
        ep_loss = -(self.get_policy(s).log_prob(a) * r).mean()
        ep_loss.backward()
        self.optimizer.step()
        return ep_loss

    def train(self):
        bar = Bar('{}'.format('Training'), max=self.nepisodes)
        self.logger = get_logger("VPG",self.env.spec.name)

        episode_rewards = []
        eval_rewards = []

        n_experience = 0
        last_eval_mean = 0
        last_eval_std = 0
        step = 0

        for ep in (range(self.nepisodes)):
            self.policy.train()
            replaymem = ReplayMemory(10000,1)
            state = self.env.reset(seed=self.seed)
            ep_reward = 0
            for t in range(1,self.max_steps):

                action = self.get_action(torch.tensor(state).unsqueeze(0).float())
                new_state, reward, done, info = self.env.step(action.item())

                ep_reward += reward
                replaymem.add_exp(torch.tensor(state).unsqueeze(0).float(),action,reward,torch.tensor(new_state).unsqueeze(0).float(),int(done))

                state = new_state

                step += 1
                if done:
                    break

            self.update_net(replaymem)

            if self.num_eval_episodes > 0 and ((ep % self.eval_freq )==0):
                temp_eval_rewards = []
                for _ in range(self.num_eval_episodes):
                    temp_eval_rewards.append(self.evaluate())
                last_eval_mean = np.mean(temp_eval_rewards)
                last_eval_std = np.std(temp_eval_rewards)
                eval_rewards.append(temp_eval_rewards)

            if self.use_wandb:
                wandb.log({"episode_reward": ep_reward,'eval_reward_mean':last_eval_mean,'eval_reward_std':last_eval_std})

            episode_rewards.append(ep_reward)
            ep_info =  ('Episode '+str(ep)+' reward: ' + str(ep_reward) + ' Mean r over last 20 episodes :' + str(np.mean(episode_rewards[-20:]).item())+' last eval mean,std  ' +str(last_eval_mean)+' '+str(last_eval_std))

            if "cart" in self.env.spec.name.lower() and np.mean(episode_rewards[-20:]).item() > 480:
                print("Solved cartpole exiting early")
                bar.finish()
                self.logger.info(ep_info)
                return eval_rewards, np.mean(episode_rewards[-30:]).item()

            self.logger.info( ep_info)
            Bar.suffix = ep_info
            bar.next()

        bar.finish()

        return eval_rewards, np.mean(episode_rewards[-30:]).item()

    def show_results(self):
        self.evaluate(save_gif=True)

    def evaluate(self,save_gif = False):
        self.policy.eval()
        state = self.env.reset(seed=self.seed)
        total_reward = 0
        frames = []
        for t in range(1,self.max_steps):

            action = self.get_action(torch.tensor(state).unsqueeze(0).float())
            new_state, reward, done, info = self.env.step(action.item())
            if save_gif:
                img = self.env.render(mode="rgb_array")
                frames.append(img)

            total_reward += reward
            state = new_state
            if done :
                break

        if save_gif:
            write_gif([np.transpose(f, axes=[2,0, 1]) for f in frames], 'gifs/vpg_'+self.env.spec.name+'.gif', fps=30)
        if self.use_wandb:
            wandb.log({"loss": total_reward})
        return total_reward


import os
import re
import spotipy
from moviepy.editor import *
from urllib.parse import quote
from urllib import request as rq
from youtube_dl import YoutubeDL
from spotipy.oauth2 import SpotifyClientCredentials

## fix to skip use for PYTHONPATH
sys.path.append(os.getcwd())
sys.path.append(os.path.join(os.getcwd(),"..","common"))

from common.common import controller_common
common = controller_common()

class controller_spotify:

    def __init__(self,client_api,token_api,user):

        self.__CLIENT_ID = client_api
        self.__CLIENT_SECRET = token_api
        self.__USER_ID = user

        self.auth_manager = SpotifyClientCredentials(
            client_id=self.__CLIENT_ID, client_secret=self.__CLIENT_SECRET
        )
        self.sp = spotipy.Spotify(auth_manager=self.auth_manager)

    def get_ydl_opts(self, path):
        return {
            "format": "bestaudio/best",
            "outtmpl": f"{path}/%(id)s.%(ext)s",
            "ignoreerrors": True,
            "postprocessors": [
                {
                    "key": "FFmpegExtractAudio",
                    "preferredcodec": "mp3",
                    "preferredquality": "320",
                }
            ],
        }

    def get_user_playlists(self):
        return [
            {"value": pl.get("uri"), "name": pl.get("name")}
            for pl in self.sp.user_playlists(self.__USER_ID).get("items")
        ]

    def normalize_str(self, string):
        return string.translate(str.maketrans('\\/:*?"<>|', "__       "))

    def get_playlist_details(self, pl_uri):
        offset = 0
        fields = "items.track.track_number,items.track.name,items.track.artists.name,items.track.album.name,items.track.album.release_date,total,items.track.album.images"
        pl_name = self.sp.playlist(pl_uri)["name"]
        pl_items = self.sp.playlist_items(
            pl_uri,
            offset=offset,
            fields=fields,
            additional_types=["track"],
        )["items"]

        pl_tracks = []
        while len(pl_items) > 0:
            for item in pl_items:
                if item["track"]:
                    track_name = self.normalize_str(item["track"]["name"])
                    artist_name = self.normalize_str(
                        item["track"]["artists"][0]["name"]
                    )
                    pl_tracks.append(
                        {
                            "uri": quote(
                                f'{track_name.replace(" ", "+")}+{artist_name.replace(" ", "+")}'
                            ),
                            "file_name": f"{artist_name} - {track_name}",
                            "track_name": track_name,
                            "artist_name": artist_name,
                            "album_name": self.normalize_str(
                                item["track"]["album"]["name"]
                            ),
                            "album_date": item["track"]["album"]["release_date"],
                            "track_number": item["track"]["track_number"],
                            "album_art": item["track"]["album"]["images"][0]["url"],
                        }
                    )

            offset = offset + len(pl_items)
            pl_items = self.sp.playlist_items(
                pl_uri,
                offset=offset,
                fields=fields,
                additional_types=["track"],
            )["items"]

        return {"pl_name": pl_name, "pl_tracks": pl_tracks}

    def check_existing_tracks(self, playlist, path):
        existing_tracks = os.listdir(path)
        tracks = [
            track
            for track in playlist["pl_tracks"]
            if f"{track['file_name']}.mp3" not in existing_tracks
        ]
        return tracks

    def download_tracks(self, pl_uri):

        count = 0
        items = list()

        pl_details = self.get_playlist_details(pl_uri)
        path = common.create_download_directory(pl_details["pl_name"])
        tracks = self.check_existing_tracks(pl_details, path)
        print(
            f"\n\033[1m\033[33m[info] Downloading {len(tracks)} tracks from {pl_details['pl_name']}\033[0m"
        )
        with YoutubeDL(self.get_ydl_opts(path)) as ydl:
            for track in tracks:
                html = rq.urlopen(
                    f"https://www.youtube.com/results?search_query={track['uri']}"
                )
                video_ids = re.findall(r"watch\?v=(\S{11})", html.read().decode())

                if video_ids:
                    url = "https://www.youtube.com/watch?v=" + video_ids[0]

                    print ( f"Add [{count}] - {url}" )
                    count = count + 1
                    items.append(url)

        res = common.thread_pool(items,path,"download")

        if res:
            common.converterto_mp3(pl_details["pl_name"])


import cv2
import os

# Input folder containing the saved images
image_folder = '/Users/tobieabel/Desktop/video_frames/ConcatVideo/'

# Output video file path
output_video_path = '/Users/tobieabel/Desktop/video_frames/Youtube/v3_a demo.mp4'

# Get the list of image files in the input folder
image_files = os.listdir(image_folder)
image_files.remove('.DS_Store')
def file_sort_key(filename):
    # Extract the numeric portion of the filename
    number = int(os.path.splitext(filename)[0])

    return number

# Sort the files chronologically
sorted_files = sorted(image_files, key=file_sort_key)
print(sorted_files)
# Get the dimensions of the first image to initialize the video writer
first_image_path = os.path.join(image_folder, sorted_files[0])
first_image = cv2.imread(first_image_path)
height, width, _ = first_image.shape #need to be careful of this.  I scrapped a video from youtube whose resolution was an odd width 1740, height 988
#and these dimensions didn't work with cv2.VideoWriter so I had to use cv2.resize to change the images to 1920x1080 which is the closest accepatable format

# Define the codec and create the video writer
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
fps = 30  # Adjust as needed
video_writer = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))

# Loop through the image files and write them to the video
for i in sorted_files:
    image_path = os.path.join(image_folder, i)
    image = cv2.imread(image_path)

    # Write the image to the video writer
    video_writer.write(image)

# Release the video writer
video_writer.release()

print(f"Video saved to: {output_video_path}")


#!/usr/bin/python3
"""base geometry class"""

BaseGeometry = __import__('7-base_geometry').BaseGeometry
Rectangle = __import__('9-rectangle').Rectangle

"""class to represent a square"""

class Square(Rectangle):
    """square Class"""
    def __init__(self, size):
            """init"""
            self.integer_validator("size", size)
            super().__init__(size, size)
            self.__size = size


# 피보나치 수 - 재귀호출
def fib(n):
    if(n == 1 or n == 2):
        return 1
    else:
        global count
        count += 1
        return fib(n-1) + fib(n-2)

# 피보나치 수 - 동적 프로그래밍
def fibonacci(n):
    f = []
    f.append(1)
    f.append(1)
    cnt = 0
    for i in range(2, n):
        cnt += 1
        f.append(f[i-1] + f[i-2])
    return cnt

count = 1
n = int(input())
fib(n)
print(count, fibonacci(n))

python/동적프로그램/b1_24416_알고리즘 수업-피보나치 수1.py


import sys
import socket
import platform
import psutil
import wmi
import urllib.request
from PyQt5.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QPushButton, QTextEdit, QWidget
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont, QColor

class App(QMainWindow):
    def __init__(self, app):
        super().__init__()
        self.app = app
        self.initUI()

    def initUI(self):
        self.setWindowTitle('App')
        self.setGeometry(200, 200, 800, 600)

        central_widget = QWidget(self)
        self.setCentralWidget(central_widget)

        layout = QVBoxLayout()

        self.text_output = QTextEdit(self)
        self.text_output.setFont(QFont("Arial", 12))
        layout.addWidget(self.text_output)

        button_ipv4_info = QPushButton('Get My IPv4', self)
        button_proxy_info = QPushButton('Check Proxy Info', self)
        button_system_info = QPushButton('Retrieve System Info', self)
        button_bios_info = QPushButton('Fetch BIOS Info', self)
        button_hostname_info = QPushButton('Get Hostname', self)

        button_ipv4_info.setFont(QFont("Arial", 10))
        button_proxy_info.setFont(QFont("Arial", 10))
        button_system_info.setFont(QFont("Arial", 10))
        button_bios_info.setFont(QFont("Arial", 10))
        button_hostname_info.setFont(QFont("Arial", 10))

        button_ipv4_info.setStyleSheet("background-color: lightblue;")
        button_proxy_info.setStyleSheet("background-color: lightgreen;")
        button_system_info.setStyleSheet("background-color: lightcoral;")
        button_bios_info.setStyleSheet("background-color: lightsalmon;")
        button_hostname_info.setStyleSheet("background-color: lightyellow;")

        layout.addWidget(button_ipv4_info)
        layout.addWidget(button_proxy_info)
        layout.addWidget(button_system_info)
        layout.addWidget(button_bios_info)
        layout.addWidget(button_hostname_info)

        central_widget.setLayout(layout)

        button_ipv4_info.clicked.connect(self.fetch_ipv4_info)
        button_proxy_info.clicked.connect(self.check_proxy_info)
        button_system_info.clicked.connect(self.retrieve_system_info)
        button_bios_info.clicked.connect(self.fetch_bios_info)
        button_hostname_info.clicked.connect(self.get_host_name)

    def fetch_ipv4_info(self):
        hostname = socket.gethostname()
        ip = socket.gethostbyname(hostname)
        is_static = socket.gethostbyaddr(ip)
        interface = None
        if "Wi-Fi" in platform.platform():
            interface = "Wi-Fi"
        elif "Ethernet" in platform.platform():
            interface = "Ethernet"
        result = f"IPv4 Address: {ip}\nStatic: {is_static}\nNetwork Interface: {interface}"
        self.text_output.append(result)

    def check_proxy_info(self):
        proxy_handler = urllib.request.ProxyHandler()
        opener = urllib.request.build_opener(proxy_handler)

        try:
            opener.open("http://www.google.com", timeout=5)
            is_proxy_enabled = True
        except Exception:
            is_proxy_enabled = False

        proxy_status = "Proxy is enabled" if is_proxy_enabled else "Proxy is disabled"
        self.text_output.append(proxy_status)

    def retrieve_system_info(self):
        os_version = platform.platform()
        os_architecture = platform.architecture()
        num_cores = psutil.cpu_count(logical=False)
        ram = round(psutil.virtual_memory().total / (1024 ** 3), 2)
        result = f"Operating System Version: {os_version}\nArchitecture: {os_architecture}\nCPU Cores: {num_cores}\nRAM: {ram} GB"
        self.text_output.append(result)

    def fetch_bios_info(self):
        c = wmi.WMI()
        bios = c.Win32_BIOS()[0]
        result = f"BIOS Manufacturer: {bios.Manufacturer}\nBIOS Version: {bios.Version}\nBIOS Release Date: {bios.ReleaseDate}"
        self.text_output.append(result)

    def get_host_name(self):
        hostname = socket.gethostname()
        self.text_output.append(f"Hostname: {hostname}")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = App(app)
    window.show()
    sys.exit(app.exec_())


from typing import Any, Dict, List, Type, TypeVar, Union

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..types import UNSET, Unset

T = TypeVar("T", bound="FollowUpPriorityV2ResponseBody")

@_attrs_define
class FollowUpPriorityV2ResponseBody:
    """
    Example:
        {'description': 'A follow-up that requires immediate attention.', 'id': '01GNW4BAQ7XRMFF6FHKNXDFPRW', 'name':
            'Urgent', 'rank': 10}

    Attributes:
        id (str): Unique identifier for the follow-up priority option Example: 01GNW4BAQ7XRMFF6FHKNXDFPRW.
        name (str): Name of the follow-up priority option Example: Urgent.
        rank (int): Rank is used to order the follow-up priority options correctly Example: 10.
        description (Union[Unset, str]): Description of the follow-up priority option Example: A follow-up that requires
            immediate attention..
    """

    id: str
    name: str
    rank: int
    description: Union[Unset, str] = UNSET
    additional_properties: Dict[str, Any] = _attrs_field(init=False, factory=dict)

    def to_dict(self) -> Dict[str, Any]:
        id = self.id
        name = self.name
        rank = self.rank
        description = self.description

        field_dict: Dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update(
            {
                "id": id,
                "name": name,
                "rank": rank,
            }
        )
        if description is not UNSET:
            field_dict["description"] = description

        return field_dict

    @classmethod
    def from_dict(cls: Type[T], src_dict: Dict[str, Any]) -> T:
        d = src_dict.copy()
        id = d.pop("id")

        name = d.pop("name")

        rank = d.pop("rank")

        description = d.pop("description", UNSET)

        follow_up_priority_v2_response_body = cls(
            id=id,
            name=name,
            rank=rank,
            description=description,
        )

        follow_up_priority_v2_response_body.additional_properties = d
        return follow_up_priority_v2_response_body

    @property
    def additional_keys(self) -> List[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties


from jinja2 import Environment, BaseLoader
from io import BytesIO
import plotly
import base64

'''
export = ExportHTML('testclass.html')
export.render()
'''

class ExportHTML:
    __template_vars = {'title':'Hello World','body':'Hello World !!!'}
    __template_html = '''

            <meta charset="UTF-8">
            <title>{{ title }}</title>

Header

             {{ body }}

Next Page

        '''

    def encode_graph(self, fig):
        tmpfile = BytesIO()
        fig.savefig(tmpfile, format='png', bbox_inches='tight')
        encoded = base64.b64encode(tmpfile.getvalue()).decode('utf-8')

        fig_html = ''.format(encoded)
        return fig_html

    def plotly_img_uri(self, fig, height=300, width=1200, orca_path='C:/Users/Administrator/anaconda3/orca_app/orca.exe'):
        plotly.io.orca.config.executable = orca_path

        img_uri = base64.b64encode(plotly.io.to_image(fig, width=width, height=height)).decode('ascii')
        return ''.format(width=width, height=height, img_uri=img_uri)

    @property
    def template_vars(self):
        return self.__template_vars

    @template_vars.setter
    def template_vars(self, var_dict):
        self.__template_vars = var_dict

    @property
    def template_html(self):
        return self.__template_html

    @template_html.setter
    def template_html(self, htmlString):
        self.__template_html = htmlString

    def render(self, output_file):
        template = Environment(loader=BaseLoader()).from_string(self.template_html)
        template_vars = self.template_vars
        html_out = template.render(template_vars)

        with open(output_file, "w") as fh:
            fh.write(html_out)


#the code partial borrowed from
# "Neural Network-based Reconstruction in Compressed Sensing
#MRI Without Fully-sampled Training Data"

import torch
import torch.nn as nn
import numpy as np
import torch.nn.functional as F
import util_torch as util_torch
def absval(arr):
    """
    Takes absolute value of last dimension, if complex.
    Input dims:  (N, l, w, 2)
    Output dims: (N, l, w)
    """
    # Expects input of size (N, l, w, 2)
    assert arr.shape[-1] == 2
    return torch.norm(arr, dim=3)

def scale(y, y_zf):
    """Scales inputs for numerical stability"""
    flat_yzf = torch.flatten(absval(y_zf), start_dim=1, end_dim=2)
    max_val_per_batch, _ = torch.max(flat_yzf, dim=1, keepdim=True)
    y = y / max_val_per_batch.view(len(y), 1, 1, 1)
    y_zf = y_zf / max_val_per_batch.view(len(y), 1, 1, 1)
    return y, y_zf

class Upsample(nn.Module):
    """Upsamples input multi-channel image"""
    def __init__(self, scale_factor, mode, align_corners):
        super(Upsample, self).__init__()
        self.scale_factor = scale_factor
        self.mode = mode
        self.align_corners = align_corners
    def forward(self, x):
        return F.interpolate(x, scale_factor=self.scale_factor, mode=self.mode, align_corners=self.align_corners)

class ResBlock(nn.Module):
    '''5-layer CNN with residual output'''
    def __init__(self, n_ch_in=2, n_ch_out=2, nf=64, ks=3):

        super(ResBlock, self).__init__()
        self.n_ch_out = n_ch_out

        self.conv1 = nn.Conv2d(n_ch_in, nf, ks, padding = ks//2)
        self.conv2 = nn.Conv2d(nf, nf, ks, padding = ks//2)
        self.conv3 = nn.Conv2d(nf, nf, ks, padding = ks//2)
        self.conv4 = nn.Conv2d(nf, nf, ks, padding = ks//2)
        self.conv5 = nn.Conv2d(nf, n_ch_out, ks, padding = ks//2)
        self.relu = nn.ReLU(inplace=True)

    def forward(self, x):
        conv1_out = self.conv1(x)
        conv1_out = self.relu(conv1_out)

        conv2_out = self.conv2(conv1_out)
        conv2_out = self.relu(conv2_out)

        conv3_out = self.conv3(conv2_out)
        conv3_out = self.relu(conv3_out)

        conv4_out = self.conv4(conv3_out)
        conv4_out = self.relu(conv4_out)

        conv5_out = self.conv5(conv4_out)

        x_res = x[:,:self.n_ch_out,:,:] + conv5_out
        return x_res

class Net(nn.Module):
    def __init__(self, K, lmbda, device, n_hidden=64):
        super(Net, self).__init__()

        #self.mask = mask
        self.lmbda = lmbda
        self.resblocks = nn.ModuleList()
        self.device = device

        for i in range(K):
            resblock = ResBlock(n_ch_in=2, nf=n_hidden)
            self.resblocks.append(resblock)

        self.block_final = ResBlock(n_ch_in=2, nf=n_hidden)

    def forward(self, ksp_input, sensemap, window = 1, mask = None):

        if mask is None:
           mask=torch.not_equal(ksp_input, 0)
           dtype=torch.complex64
           mask = mask.type(dtype)
        x = util_torch.transpose_model(ksp_input * window, sensemap)
        x = util_torch.complex_to_channels(x)#;print(x.shape);quit()
        #ksp_input, x = scale(ksp_input, x)
        for i in range(len(self.resblocks)):
            # z-minimization
            x = x.permute(0, 3, 1, 2)

            z = self.resblocks[i](x)
            z = z.permute(0, 2, 3, 1)
            z = util_torch.channels_to_complex(z)
            # x-minimization
            #z_ksp = utils.fft(z)
            z_ksp = util_torch.model_forward(z, sensemap)
            #x_ksp = losslayer.data_consistency(z_ksp, y, self.mask, self.lmbda)
            x_ksp = (1 - mask) * z_ksp + mask * (self.lmbda*z_ksp + ksp_input) / (1 + self.lmbda)
            #x = utils.ifft(x_ksp)
            x = util_torch.transpose_model(x_ksp, sensemap)
            x = util_torch.complex_to_channels(x)
        x = x.permute(0, 3, 1, 2)
        x = self.block_final(x)
        return x