from collections import defaultdict
from copy import deepcopy
import os
import re
import warnings

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import transforms
from matplotlib.path import Path
from matplotlib.patches import PathPatch
import matplotlib.patheffects
from matplotlib.text import TextPath
import pkg_resources
from plotly.offline import download_plotlyjs, plot
import plotly.graph_objs as go
import seaborn as sns
import tabix

from ..sequences import Genome

_SVG_PATHS = {'T': "M 0,100 l 100, 0 l 0,-25 l -37.5, 0 l 0,-75 l -25, 0 " +
                   "l 0,75 l -37.5,0 l 0,25 z",
              'C': ("M 100,12.5 l 0,25 c 0,0 -25,-15 -50,-12.5 " +
                    "c 0,0 -25,0 -25,25 c 0,0 0,25 25,25 c 0,0 25,2.5 50,-15" +
                    " l 0, 25 C 100,87.5 75,100 50,100 C 50,100 0,100 0,50 " +
                    "C 0,50 0,0 50,0 C 50,0 75,0 100,12.5 z"),
              'G': ("M 100,12.5 l 0,25 c 0,0 -25,-15 -50,-12.5 " +
                    "c 0,0 -25,0 -25,25 c 0,0 0,25 25,25 c 0,0 25,2.5 50,-15" +
                    " l 0, 25 C 100,87.5 75,100 50,100 C 50,100 0,100 0,50 " +
                    "C 0,50 0,0 50,0 C 50,0 75,0 100,12.5 M 100,37.5 " +
                    "l 0,17.5 l -50,0 l 0,-17 l 25,0 l 0,-25 l 25,0 z"),
              'A': ("M 0,0 l 37.5,100 l 25,0 l 37.5,-100 l -25,0 l -9.375,25" +
                    " l -31.25,0 l -9.375,-25 l -25,0 z 0,0 M 43.75, 50 " +
                    "l 12.5,0 l -5.859375,15.625 l -5.859375,-15.625 z"),
              'U': ("M 0,100 l 25,0 l 0,-50 C 25,50 25,25 50,25" +
                    " C 50,25 75,25 75,50 l 0,50 l 25,0 L 100,50 " +
                    "C 100,50 100,0, 50,0 C 50,0 0,0 0,50 l 0,50 z")}

def _svg_parse(path_string):
    commands = {'M': (Path.MOVETO,),
                'L': (Path.LINETO,),
                'Q': (Path.CURVE3,) * 2,
                'C': (Path.CURVE4,) * 3,
                'Z': (Path.CLOSEPOLY,)}
    path_re = re.compile(r'([MLHVCSQTAZ])([^MLHVCSQTAZ]+)', re.IGNORECASE)
    float_re = re.compile(r'(?:[\s,]*)([+-]?\d+(?:\.\d+)?)')
    vertices = []
    codes = []
    last = (0, 0)
    for cmd, values in path_re.findall(path_string):
        points = [float(v) for v in float_re.findall(values)]
        points = np.array(points).reshape((len(points) // 2, 2))
        if cmd.islower():
            points += last
        cmd = cmd.capitalize()
        if len(points) > 0:
            last = points[-1]
        codes.extend(commands[cmd])
        vertices.extend(points.tolist())
    return np.array(vertices), codes

for k in _SVG_PATHS.keys():
    _SVG_PATHS[k] = _svg_parse(_SVG_PATHS[k])

class _TextPathRenderingEffect(matplotlib.patheffects.AbstractPathEffect):
    def __init__(self, bar, x_translation=0., y_translation=0.,
                 x_scale=1., y_scale=1.):
        self._bar = bar
        self._x_translation = x_translation
        self._y_translation = y_translation
        self._x_scale = x_scale
        self._y_scale = y_scale

    def draw_path(self, renderer, gc, tpath, affine, rgbFace=None):
        b_x, b_y, b_w, b_h = self._bar.get_extents().bounds
        t_x, t_y, t_w, t_h = tpath.get_extents().bounds
        translation = [b_x - t_x, b_y - t_y]
        translation[0] += self._x_translation
        translation[1] += self._y_translation
        scale = [b_w / t_w, b_h / t_h]
        scale[0] *= self._x_scale
        scale[1] *= self._y_scale
        affine = affine.identity().scale(*scale).translate(*translation)
        renderer.draw_path(gc, tpath, affine, rgbFace)

def sequence_logo(score_matrix, order="value", width=1.0, ax=None,
                  sequence_type=Genome, font_properties=None,
                  color_scheme=None,
                  **kwargs):
    # Note that everything will break if we do not deepcopy.
    score_matrix = deepcopy(score_matrix)

    score_matrix = score_matrix.transpose()
    if font_properties is not None:
        warnings.warn(
            "Specifying a value for `font_properties` (other than `None`) "
            "will use the `matplotlib`-based character paths, and causes "
            "distortions in the plotted motif. We recommend leaving "
            "`font_properties=None`. See the documentation for details.",
            UserWarning)

    if color_scheme is None:
        color_scheme = sns.color_palette("Set1",
                                         n_colors=len(sequence_type.BASES_ARR))
        color_scheme = color_scheme.as_hex()
    if len(color_scheme) < len(sequence_type.BASES_ARR):
        raise ValueError(
            "Color scheme is shorter than number of bases in sequence.")

    if score_matrix.shape[0] != len(sequence_type.BASES_ARR):
        raise ValueError(
            "Got score with {0} bases for sequence with {1} bases.".format(
                score_matrix.shape[0], len(sequence_type.BASES_ARR)))
    if ax is None:
        _, ax = plt.subplots(figsize=score_matrix.shape)

    # Determine offsets depending on sort order.
    positive_offsets = np.zeros_like(score_matrix)
    negative_offsets = np.zeros_like(score_matrix)
    bases = np.empty(score_matrix.shape, dtype=object)
    bases[:, :] = "?"  # This ensures blanks are visually obvious.

    # Change ordering of things based on input arguments.
    if order == "alpha":
        for i in range(score_matrix.shape[0]):
            bases[i, :] = sequence_type.BASES_ARR[i]

    elif order == "value":
        if np.sum(score_matrix < 0) != 0:
            sorted_scores = np.zeros_like(score_matrix)
            for j in range(score_matrix.shape[1]):
                # Sort the negative values and put them at bottom.
                div = np.sum(score_matrix[:, j] < 0.)
                negative_idx = np.argwhere(score_matrix[:, j] < 0.).flatten()
                negative_sort_idx = np.argsort(score_matrix[negative_idx, j],
                                               axis=None)
                sorted_scores[:div, j] = score_matrix[
                    negative_idx[negative_sort_idx], j]
                bases[:div, j] = sequence_type.BASES_ARR[
                    negative_idx[negative_sort_idx]].flatten()

                # Sort the positive values and stack atop the negatives.
                positive_idx = np.argwhere(score_matrix[:, j] >= 0.).flatten()
                positive_sort_idx = np.argsort(score_matrix[positive_idx, j],
                                               axis=None)
                sorted_scores[div:, j] = score_matrix[
                    positive_idx[positive_sort_idx], j]
                bases[div:, j] = sequence_type.BASES_ARR[
                    positive_idx[positive_sort_idx]].flatten()
            score_matrix = sorted_scores
        else:
            for j in range(score_matrix.shape[1]):
                sort_idx = np.argsort(score_matrix[:, j], axis=None)[::-1]
                bases[:, j] = sequence_type.BASES_ARR[sort_idx]
                score_matrix[:, j] = score_matrix[sort_idx, j]

    # Create offsets for each bar.
    for i in range(score_matrix.shape[0] - 1):
        y_coords = score_matrix[i, :]
        if i > 0:
            negative_offsets[i + 1, :] = negative_offsets[i, :]
            positive_offsets[i + 1, :] = positive_offsets[i, :]
        neg_idx = np.argwhere(y_coords < 0.)
        pos_idx = np.argwhere(y_coords >= 0.)
        negative_offsets[i + 1, neg_idx] += y_coords[neg_idx]
        positive_offsets[i + 1, pos_idx] += y_coords[pos_idx]

    for i in range(score_matrix.shape[0]):
        x_coords = np.arange(score_matrix.shape[1]) + 0.5
        y_coords = score_matrix[i, :]

        # Manage negatives and positives separately.
        offsets = np.zeros(score_matrix.shape[1])
        negative_idx = np.argwhere(y_coords < 0.)
        positive_idx = np.argwhere(y_coords >= 0.)
        offsets[negative_idx] = negative_offsets[i, negative_idx]
        offsets[positive_idx] = positive_offsets[i, positive_idx]
        bars = ax.bar(x_coords, y_coords, color="black", width=width,
                      bottom=offsets)
        for j, bar in enumerate(bars):
            base = bases[i, j]
            bar.set_color(color_scheme[sequence_type.BASE_TO_INDEX[base]])
            bar.set_edgecolor(None)

    # Iterate over the barplot's bars and turn them into letters.
    new_patches = []
    for i, bar in enumerate(ax.patches):
        base_idx = i // score_matrix.shape[1]
        seq_idx = i % score_matrix.shape[1]
        base = bases[base_idx, seq_idx]
        # We construct a text path that tracks the bars in the barplot.
        # Thus, the barplot takes care of scaling and translation,
        #  and we just copy it.
        if font_properties is None:
            text = Path(_SVG_PATHS[base][0], _SVG_PATHS[base][1])
        else:
            text = TextPath((0., 0.), base, fontproperties=font_properties)
        b_x, b_y, b_w, b_h = bar.get_extents().bounds
        t_x, t_y, t_w, t_h = text.get_extents().bounds
        scale = (b_w / t_w, b_h / t_h)
        translation = (b_x - t_x, b_y - t_y)
        text = PathPatch(text, facecolor=bar.get_facecolor(), lw=0.)
        bar.set_facecolor("none")
        text.set_path_effects([_TextPathRenderingEffect(bar)])
        transform = transforms.Affine2D().translate(*translation).scale(*scale)
        text.set_transform(transform)
        new_patches.append(text)

    for patch in new_patches:
        ax.add_patch(patch)
    ax.set_xlim(0, score_matrix.shape[1])
    ax.set_xticks(np.arange(score_matrix.shape[1]) + 0.5)
    ax.set_xticklabels(np.arange(score_matrix.shape[1]))
    return ax

def rescale_score_matrix(score_matrix, base_scaling="identity",
                         position_scaling="identity"):
    # Note that things can break if we do not deepcopy.
    score_matrix = deepcopy(score_matrix)

    score_matrix = score_matrix.transpose()
    rescaled_scores = score_matrix

    # Scale individual bases.
    if base_scaling == "identity" or base_scaling == "probability":
        pass
    elif base_scaling == "max_effect":
        rescaled_scores = score_matrix - np.min(score_matrix, axis=0)
    else:
        raise ValueError(
            "Could not find base scaling \"{0}\".".format(base_scaling))

    # Scale each position
    if position_scaling == "max_effect":
        max_effects = np.max(score_matrix, axis=0) - np.min(score_matrix,
                                                            axis=0)
        rescaled_scores /= rescaled_scores.sum(axis=0)[np.newaxis, :]
        rescaled_scores *= max_effects[np.newaxis, :]
    elif position_scaling == "probability":
        rescaled_scores /= np.sum(score_matrix, axis=0)[np.newaxis, :]
    elif position_scaling != "identity":
        raise ValueError(
            "Could not find position scaling \"{0}\".".format(
                position_scaling))
    return rescaled_scores.transpose()

def heatmap(score_matrix, mask=None, sequence_type=Genome, **kwargs):
    # Note that some things can break if we do not deepcopy.
    score_matrix = deepcopy(score_matrix)

    # This flipping is so that ordering is consistent with ordering
    # in the sequence logo.
    if mask is not None:
        mask = mask.transpose()
        mask = np.flip(mask, axis=0)
    score_matrix = score_matrix.transpose()
    score_matrix = np.flip(score_matrix, axis=0)

    if "yticklabels" in kwargs:
        yticklabels = kwargs.pop("yticklabels")
    else:
        yticklabels = sequence_type.BASES_ARR[::-1]
    if "cbar_kws" in kwargs:
        cbar_kws = kwargs.pop("cbar_kws")
    else:
        cbar_kws = dict(use_gridspec=False, location="bottom", pad=0.2)
    if "cmap" in kwargs:
        cmap = kwargs.pop("cmap")
    else:
        cmap = "Blues"
    ret = sns.heatmap(score_matrix, mask=mask, yticklabels=yticklabels,
                      cbar_kws=cbar_kws, cmap=cmap, **kwargs)
    ret.set_yticklabels(labels=ret.get_yticklabels(), rotation=0)
    return ret

def load_variant_abs_diff_scores(input_path):
    features = []
    labels = []
    diffs = []
    with open(input_path, 'r') as file_handle:
        colnames = file_handle.readline()
        features = colnames.strip().split('\t')[5:]
        for line in file_handle:
            cols = line.strip().split('\t')
            scores = [float(f) for f in cols[5:]]
            label = tuple(cols[:5])
            diffs.append(scores)
            labels.append(label)
    diffs = np.array(diffs)
    return diffs, labels, features

def sort_standard_chrs(chrom):
    chrom = chrom[3:]
    if chrom.isdigit():
        return int(chrom)
    if chrom == 'X':
        return 23
    elif chrom == 'Y':
        return 24
    elif chrom == 'M':
        return 25
    else:  # unknown chr
        return 26

def ordered_variants_and_indices(labels):
    labels_dict = defaultdict(list)
    for i, l in enumerate(labels):
        chrom, pos, name, ref, alt = l
        pos = int(pos)
        info = (i, pos, ref, alt)
        labels_dict[chrom].append(info)
    for chrom, labels_list in labels_dict.items():
        labels_list.sort(key=lambda tup: tup[1:])
    ordered_keys = sorted(labels_dict.keys(),
                          key=sort_standard_chrs)

    ordered_labels = []
    ordered_label_indices = []
    for chrom in ordered_keys:
        for l in labels_dict[chrom]:
            index, pos, ref, alt = l
            ordered_label_indices.append(index)
            ordered_labels.append((chrom, pos, ref, alt))
    return ordered_labels, ordered_label_indices

def _label_tuple_to_text(label, diff, genes=None):
    chrom, pos, ref, alt = label
    if genes is not None:
        if len(genes) == 0:
            genes_str = "none found"
        else:
            genes_str = ', '.join(genes)
        text = ("max diff score: {0}
{1} {2}, {3}/{4}
"
                "closest protein-coding gene(s): {5}").format(
            diff, chrom, pos, ref, alt, genes_str)
    else:
        text = "max diff score: {0}
{1} {2}, {3}/{4}".format(
            diff, chrom, pos, ref, alt)
    return text

def _variant_closest_genes(label, tabix_fh, chrs_gene_intervals):
    chrom = label[0]
    pos = label[1]
    closest_genes = []
    try:
        overlaps = tabix_fh.query(chrom, pos, pos + 1)
        for o in overlaps:
            closest_genes.append(o[-1])
    except tabix.TabixError:
        pass
    if len(closest_genes) != 0:
        return closest_genes
    gene_intervals = chrs_gene_intervals[chrom]
    closest = None
    for (start, end, strand, gene) in gene_intervals:
        if start < pos and closest and closest == pos - end:
            closest_genes.append(gene)
        elif start < pos and (closest is None
                              or closest > pos - end):
            closest = pos - end
            closest_genes = [gene]
        elif start > pos and closest and closest == start - pos:
            closest_genes.append(gene)
        elif start > pos and (closest is None
                              or closest > start - pos):
            closest = start - pos
            closest_genes = [gene]
    return closest_genes

def _load_chrs_gene_intervals(gene_intervals_bed):
    chrs_gene_intervals = defaultdict(list)
    with open(gene_intervals_bed, 'r') as file_handle:
        for line in file_handle:
            cols = line.strip().split('\t')
            chrom = cols[0]
            start = int(cols[1])
            end = int(cols[2])
            strand = cols[3]
            gene = cols[4]
            chrs_gene_intervals[chrom].append((start, end, strand, gene))
    return chrs_gene_intervals

def _variants_closest_protein_coding_gene(labels, version="hg38"):
    gene_intervals_tabix = pkg_resources.resource_filename(
        "skylar",
        ("interpret/data/gencode_v28_{0}/"
         "protein_coding_l12_genes.bed.gz").format(version))
    gene_intervals_bed = pkg_resources.resource_filename(
        "skylar",
        ("interpret/data/gencode_v28_{0}/"
         "protein_coding_l12_genes.bed").format(version))

    tabix_fh = tabix.open(gene_intervals_tabix)
    chrs_gene_intervals = _load_chrs_gene_intervals(gene_intervals_bed)

    labels_gene_information = []
    for l in labels:
        labels_gene_information.append(_variant_closest_genes(
            l, tabix_fh, chrs_gene_intervals))

    return labels_gene_information

def variant_diffs_scatter_plot(data,
                               labels,
                               features,
                               output_path,
                               filter_features=None,
                               labels_sort_fn=ordered_variants_and_indices,
                               nth_percentile=None,
                               hg_reference_version=None,
                               threshold_line=None,
                               auto_open=False):
    labels_ordered, label_indices = labels_sort_fn(labels)
    variant_closest_genes = None
    if hg_reference_version is not None:
        variant_closest_genes = _variants_closest_protein_coding_gene(
            labels_ordered, version=hg_reference_version)
    ordered_data = data[label_indices, :]

    feature_indices = None
    if filter_features is not None:
        feature_indices = filter_features(features)
        ordered_data = data[:, feature_indices]
    variants_max_diff = np.amax(ordered_data, axis=1)

    display_labels = None
    if nth_percentile:
        p = np.percentile(variants_max_diff, nth_percentile)
        keep = np.where(variants_max_diff >= p)[0]
        print("{0} variants with max abs diff score above {1} are in the "
              "{2}th percentile.".format(len(keep), p, nth_percentile))
        variants_max_diff = variants_max_diff[keep]
        display_labels = []
        for i, l in enumerate(labels_ordered):
            if i not in keep:
                continue
            display_labels.append(l)
    else:
        display_labels = labels_ordered

    if variant_closest_genes:
        text_labels = [
            _label_tuple_to_text(l, d, g) for l, d, g in
            zip(display_labels, variants_max_diff, variant_closest_genes)]
    else:
        text_labels = [_label_tuple_to_text(l, d) for l, d in
                       zip(display_labels, variants_max_diff)]

    label_x = [' '.join([l[0], str(l[1])]) for l in display_labels]
    data = [go.Scatter(x=label_x,
                       y=variants_max_diff,
                       mode='markers',
                       marker=dict(
                           color="#39CCCC",
                           line=dict(width=1)),
                       text=text_labels,
                       hoverinfo="text")]

    go_layout = {
        "title": "Max probability difference scores",
        "hovermode": "closest",
        "hoverlabel": {
            "font": {"size": 16}
        },
        "xaxis": {
            "title": "Genome coordinates",
            "showticklabels": True,
            "tickangle": 35,
            "titlefont": {"family": "Arial, sans-serif",
                          "size": 16},
            "nticks": 25,
            "tickmode": "auto",
            "automargin": True
        },
        "yaxis": {
            "title": "Absolute difference",
            "titlefont": {"family": "Arial, sans-serif",
                          "size": 16}
        }
    }
    if isinstance(threshold_line, float):
        layout = go.Layout(
            **go_layout,
            shapes=[
                {"type": "line",
                 "x0": label_x[0],
                 "y0": threshold_line,
                 "x1": label_x[-1],
                 "y1": threshold_line,
                 "line": {
                     "color": "rgba(255, 99, 71, 1)",
                     "width": 2
                 }
                 }
            ])
    else:
        layout = go.Layout(**go_layout)
    fig = go.Figure(data=data, layout=layout)
    path, filename = os.path.split(output_path)
    os.makedirs(path, exist_ok=True)
    plot(fig, filename=output_path, auto_open=auto_open)
    return fig


from typing import Tuple, Optional, Sequence

import torch

from deep_opt import NeuralNetwork, Property
from nn_repair.counterexamples import CounterexampleGenerator, Counterexample

class FastGradientSignMethod(CounterexampleGenerator):
    """
    Generates counterexamples/adversarial examples by walking into the direction
    of the signs of the gradients until hitting the bounds.

    This attack has been introduced in [Goodfellow2015]_.
    Here it is generalized in a way to work for arbitrary properties
    using the satisfaction function.

    .. [Goodfellow2015] Goodfellow, Ian J. / Shlens, Jonathon / Szegedy, Christian
     Explaining and Harnessing Adversarial Examples
     2015
     ICLR (Poster)
    """

    def __init__(self):
        """
        Creates a new FastGradientSignMethod (FGSM) for generating counterexamples.
        """
        pass

    @property
    def name(self) -> str:
        return 'FGSM'

    def find_counterexample(self, target_network: NeuralNetwork, target_property: Property) \
            -> Tuple[Optional[Sequence[Counterexample]], str]:
        if target_property.input_constraint is not None:
            raise ValueError("FGSM can not handle Properties with additional input constraints.")

        # start at the center point of the property
        bounds = target_property.input_bounds(target_network)
        lower = torch.tensor([lower for lower, _ in bounds])
        upper = torch.tensor([upper for _, upper in bounds])

        x = (lower + (upper-lower)/2).clone().detach().requires_grad_()
        # network also accepts flat inputs (but satisfaction function requires a batch dimension)
        violation = target_property.satisfaction_function(x.unsqueeze(0), target_network)
        violation.backward(inputs=[x])
        gradient_sign = -x.grad.sign()

        cx_candidate = torch.where(gradient_sign < 0, lower, upper)
        # reset places where the gradient is zero
        cx_candidate = gradient_sign.abs() * cx_candidate + (1 - gradient_sign.abs()) * x

        _, network_outputs, satisfaction, is_sat = \
            target_property.full_witness(cx_candidate.unsqueeze(0), target_network)
        if not is_sat:
            cx = Counterexample(inputs=cx_candidate.detach().numpy(),
                                network_outputs=network_outputs,
                                property_satisfaction=satisfaction.item(),
                                property=target_property)
            return (cx, ), 'Violated'
        else:
            return (), 'Unknown'


from fabric.api import *
from fabric.contrib.files import exists
from fabric.contrib.project import *
from time import time
from datetime import datetime
from sys import exit

env.hosts = [
    '176.31.251.100'
]
env.user = 'root' # should be changed asap

def check_environment(env_name):
    existing_environments = ['staging', 'prod']
    if env_name not in existing_environments:
        puts('%s is not a valid environment' % env_name)
        puts('Allowed environments: %s' % ', '.join(existing_environments))
        exit(1)

def deploy(env_name):
    check_environment(env_name)

    deploy_timestamp = datetime.utcfromtimestamp(int(time())).strftime('%Y-%b-%d-at-%H:%M:%S')

    deployment_directory='/home/deploy/%s/releases/release-%s' % (env_name, deploy_timestamp)

    upload_code(deployment_directory)
    enable_project(env_name, deployment_directory)

def upload_code(deployment_directory):
    if not exists(deployment_directory):
       puts('creating deployment directory')
       run('mkdir -p %s' % deployment_directory)

    puts('Deploying project in %s' % deployment_directory)
    rsync_project(
        local_dir='./app/*',
        remote_dir=deployment_directory,
        default_opts='-azcrSh --stats',
        exclude=['.git', 'var', 'node_modules', 'vendor']
    )

def enable_project(env_name, deployment_directory):
    # Copy of the environment file
    run('cp /home/deploy/%s/shared/env %s/.env' % (env_name, deployment_directory))

    # warmup cache
    with cd(deployment_directory):
        run('time composer install --optimize-autoloader')
        run('chmod a+w var/ -R')
        if env_name == "staging":
            run('bin/console cache:clear --env=dev --no-debug')
            run('bin/console cache:warmup --env=dev --no-debug')
        else:
            run('bin/console cache:clear --env=prod --no-debug')
            run('bin/console cache:warmup --env=prod --no-debug')
        run('chmod a+w var/ -R')
        run('time bin/console doctrine:migrations:migrate --no-interaction')
        run('cp assets/static public/build/static -R')

    # Create symlink
    run('ln -sfn %s /var/www/pc-%s' % (deployment_directory, env_name))
    # Restart the necessary services
    run('service php7.3-fpm restart')
    run('service nginx restart')

def list_releases(env_name):
    check_environment(env_name)
    run('find /home/deploy/%s/releases -maxdepth 1 -printf "%%f\n"' % (env_name))

def set_release(env_name, release):
    # Create symlink
    deployment_directory='/home/deploy/%s/releases/%s' % (env_name, release)

    run('ln -sfn %s /var/www/pc-%s' % (deployment_directory, env_name))
    # Restart the necessary services
    run('service php7.3-fpm restart')
    run('service nginx restart')


import math
def numSquarefulPerms(A):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums = A
        nums.sort()
        res = []
        def backtrack(nums, tmp):
            if not nums:
                res.append(tmp)
                return
            for i in range(len(nums)):
                # 去重
                if i and nums[i]==nums[i-1]:
                    continue
                # 剪枝
                if not tmp or math.sqrt(tmp[-1]+nums[i]) % 1 == 0:
                    backtrack(nums[:i] + nums[i+1:], tmp + [nums[i]])
        backtrack(nums, [])
        return len(res)
A=list(map(int,input().split(",")))
print(numSquarefulPerms(A))


from flask import Flask
from flask import request

app=Flask(__name__)

@app.route("/operBas",methods=["GET","POST"])
def operacion():
    if request.method == "POST":
        num1 = request.form.get("num1")
        num2 = request.form.get("num2")
        if request.form.get('button') == 'sum':
            return "
 La suma es: {}
".format(str(int(num1)+int(num2)))
        elif request.form.get('button') == 'res':
            return "
 La resta es: {}
".format(str(int(num1)-int(num2)))
        elif request.form.get('button') == 'mult':
            return "
 La multiplicacion es: {}
".format(str(int(num1)*int(num2)))
        elif request.form.get('button') == 'div':
            return "
 La division es: {}
".format(str(int(num1)/int(num2)))
    else:
        return'''
        <form action = "/operBas" method="POST">
        Ingrese los números para su operación

        N1:
        <input type="text" name="num1"/>

        N2:
        <input type="text" name="num2"/>

        Eligue la operacion

        Suma
        <input type="radio" name="button" value="sum">

        Resta
        <input type="radio" name="button" value="res">

        Multiplicacion
        <input type="radio" name="button" value="mult">

        Division
        <input type="radio" name="button" value="div">

        <button type="submit" name="Calcular">Calcular</button>
        </form>
        '''

if __name__ == "__main__":
    app.run(debug=True,port=3000)


#https://github.com/llSourcell/how_to_deploy_a_keras_model_to_production/blob/master/model/load.py
#https://machinelearningmastery.com/save-load-keras-deep-learning-models/

import keras
from __future__ import print_function
from keras.callbacks import LambdaCallback
from keras.models import Sequential
from keras.callbacks import ModelCheckpoint
from keras.layers import Dense
from keras.layers import LSTM
from keras.optimizers import RMSprop
from keras.utils.data_utils import get_file
import matplotlib.pyplot as plt
import numpy as np
import h5py
import random
import sys
import io
from keras.models import model_from_json
from scipy.misc import imread, imresize,imshow
import os
#get file
path = get_file('romeo+juliet.txt', origin='http://www.awesomefilm.com/script/romeo+juliet.txt')
#reads file
with io.open(path, encoding='latin-1') as f:
 #makes everything lowercase
    text = f.read().lower()
#makes variables important to establish input shape
chars = sorted(list(set(text)))
char_indices = dict((c, i) for i, c in enumerate(chars))
indices_char = dict((i, c) for i, c in enumerate(chars))
maxlen = 40
step = 3
sentences = []
next_chars = []
for i in range(0, len(text) - maxlen, step):
    sentences.append(text[i: i + maxlen])
    next_chars.append(text[i + maxlen])
#load model
json_file = open('model.json', 'r')
loaded_model_json = json_file.read()
json_file.close()
loaded_model = model_from_json(loaded_model_json)
# load weights into new model
loaded_model.load_weights("model.h5")

def sample(preds, temperature=1.0):
    # helper function to sample an index from a probability array
    preds = np.asarray(preds).astype('float64')
    preds = np.log(preds) / temperature
    exp_preds = np.exp(preds)
    preds = exp_preds / np.sum(exp_preds)
    probas = np.random.multinomial(1, preds, 1)
    return np.argmax(probas)

start_index = random.randint(0, len(text) - maxlen - 1)
f= open("output.txt","w+") #makes output text file
for diversity in [0.2, 0.5, 1.0, 1.2]:
        print('----- diversity:', diversity)

        generated = ''
        sentence = text[start_index: start_index + maxlen]
        #sentence = "What did the viola say?"
        generated += sentence
        print('----- Generating with seed: "' + sentence + '"')
        sys.stdout.write(generated)

        for i in range(10000):
            x_pred = np.zeros((1, maxlen, len(chars)))
            for t, char in enumerate(sentence):
                x_pred[0, t, char_indices[char]] = 1.

            preds = loaded_model.predict(x_pred, verbose=0)[0]
            next_index = sample(preds, diversity)
            next_char = indices_char[next_index]

            generated += next_char
            sentence = sentence[1:] + next_char

            sys.stdout.write(next_char)
            sys.stdout.flush()
        print()


#!/usr/bin/env python3

import unittest
import knn

class TestKnn(unittest.TestCase):

    learner = knn.KnnLearner()

    # Train
    def test_train(self):
        x = [
            [1, 2, 3, 4, 5],
            [5, 4, 3, 2, 1]
        ]
        y = [1/5, 2/25, 3/125, 4/625, 5/3125]
        self.learner.train(x, y)
        self.assertTrue(True)

    # Query
    def test_query(self):
        return 6
        x = [1, 2, 3, 4, 5]
        y = [2, 4, 6, 8, 10]
        self.learner.train(x, y)
        self.assertEqual(self.learner.query([x[2]]), y[2])

if __name__ == '__main__':
    unittest.main()


import sys

sys.stdin = open("input.txt")

for _ in range(10):
    tc = int(input())
    # 8개의 숫자 입력받기
    nums = list(map(int, input().split()))
    # num: 뺄 수
    num = 0
    idx = -1

    while True:
        # 대상 숫자 인덱스 세팅
        idx = (idx + 1) % 8

        # 뺄 수를 세팅
        num += 1
        if num > 5:
            num = 1

        nums[idx] -= num
        if nums[idx] <= 0:
            nums[idx] = 0
            break

    result = []
    while len(result) < len(nums):
        idx = (idx + 1) % 8
        result.append(nums[idx])

    print(f"#{tc}", *result)


class ArrayRotations():
	'''
	This class contains all the different ways to rotate
	an array. For the time-being purpose, we will consider the
	array to be a list
	'''

	def _gcd(self, a, b):

		if(b == 0):
			return a

		else:
			return self._gcd(b, a%b)

	def method_1(self, original_array, rotation_count):
		'''Creates another array and then using slicing it rotates'''
		'''Time complexity O(n) and space complexity O(2N)'''
		temp = original_array[:rotation_count]
		original_array = original_array[rotation_count:] + temp
		return original_array

	def method_2(self, original_array, rotation_count):

		'''This method rotates by using a loop of the rotation_count'''
		for i in range(rotation_count):
				original_array = original_array[1:] + [original_array[0]]

		return original_array

	def method_3(self, original_array, rotation_count):
		'''This method is implemented using the juggling method'''
		'''refer geeksforgeeks.org python array problems for reference'''
		gcd = self._gcd(rotation_count, len(original_array))
		for i in range(gcd):

			# store the initial value of the set in a temporary variable
			temp = original_array[i]
			current_element = i

			while True:
				next_element = current_element + rotation_count

				if next_element >= len(original_array):
					next_element = next_element - len(original_array)

				if next_element == i:
					## it means it has reached the end of iteration for the current set
					break

				original_array[current_element] = original_array[next_element]
				current_element = next_element

			original_array[current_element] = temp

		print(original_array)

rotations_obj = ArrayRotations()
print(rotations_obj.method_1([1,2,3,4,5], 2))
print(rotations_obj.method_2([1,2,3,4,5], 2))
rotations_obj.method_3([1,2,3,4,5,6], 2)


# 6007 -> print('\"C:Download\\\'hello\'.py\"') -> \출력 시 \\ 사용!

""" a = input()
    b = input()
    print(b)
    print(a) -> 6013 - 문자열 거꾸로 출력 """

""" a, b = input().split()
    a = int(a)
    b = int(b)
    print(a)
    print(b) -> 6015 - split() 사용 시 공백 기준으로 입력 값 나누어 자름 """

""" a, b = input().split(':')
    print(a, b, sep=':') -> 6018 - split(':') 사용 시 : 기준으로 자름, sep=':' 사용 시 : 사이에 두고 값 출력(sep은 seperator 의미) """

""" a = input()
    b = int(a, 16) - 입력받은 a를 16진수로 변환
    print('%o' % b) -> 6029 """

"""a = ord(input()) - ord()는 어떤 문자의 순서 위치 값 의미, ord(c)는 문자 c를 10진수로 변환한 값
    print(a) -> 6030 """

""" a = int(input())
    print(chr(a)) - chr()은 정수값을 문자 형태로 바꿔줌 -> 6031 """

""" a = ord(input()) - 입력받은 문자 a를 10진수 형태로 변환
    print(chr(a+1)) - ord 변환 후 그 다음 숫자를 chr 통해 다시 문자로 변환 -> 6033 """

""" a = input()
    a = float(a)
    print(format(a, ".2f")) - format(수, ".2f") 사용 시 원하는 자리까지 반올림 -> 6042 """

""" a, b = input().split()
    a= int(a)
    b= int(b)

    print(a+b)
    print(a-b)
    print(a*b)
    print(a//b)
    print(a%b)
    print(format(a/b, ".2f")) - print(round(a/b,2)) 가 모범답안, -> 6044 """

""" a = int(input())
    print(bool(a)) - bool()을 이용하면 True/False 출력, not a 는 반대로 출력 -> 6052 """

""" a, b = map(int, input().split())
    print(a ^ b) - 비트연산자 xor ^ 은 숫자가 달라야 True -> 6062 """

""" a, b = map(int, input().split())
    c = (a if (a>b) else b) - x if C else y 의 형태로 작성
    print(c) -> 6063 """

""" a, b, c = map(int, input().split())
    n = (a if a<b else b) if ((a if a<b else b)<c) else c - 정수 중 가장 작은 값 출력
    print(n) -> 6064 """

""" a, b, c = map(int, input().split())
    if a%2 == 0 : - if 조건식
        print(a)
    if b%2 == 0 :
        print(b)
    if c%2 == 0 :
        print(c) -> 6065 """

""" a= 1 - 처음 조건 검사 통과하기 위해 임의 값 설정
    while a!=0 :
        a = int(input())
        if a!=0 :
            print(a) -> 6071 """

""" a = int(input())
    start = 0

    while start<=a : - 정수 1개 입력받아 그 수까지 출력하기 EX) 4 입력 시 0~4 출력
        print(start)
        start += 1 -> 6075 """

""" while True: - 원하는 문자 입력 될 때 까지 반복 출력 / 1.제일 먼저 무한 루프 생성 후
        a = input() - 2. 무한루프 안에서 입력 받고
        print(a) - 3. 입력 받은 값 그대로 출력
        if a=='q': - 4. 만약 입력 받은 값이 q이면
            break - 반복문 종료 -> 6078 """

""" a = int(input())
    s = 0

    for i in range(a+1) :
        s += i
        if s>=a : - 정수의 합 s가 입력값 a 보다 커지면 i 출력 후 break
            print(i)
            break - 입력 한 정수보다 같거나 작을 때 까지만 계속 더하는 프로그램 -> 6079 """

""" a = input()
    a = int(a, 16) # 입력 값 a를 16진수로 변환

    for i in range(1,16) :
        print('%X'%a,'*%X'%i,'=%X'%(a*i), sep='' ) # '%X'%a 는 a에 저장되어 있는 값을 16진수로 출력 한다는 말
        # 또는 print("%X*%X=%X"%(n,i,n*i)) 로 작성 가능
        -> 6081 """

""" a = int(input())

    for i in range(1,a+1) :
     if i%10==3 or i%10==6 or i%10==9 : # 3,6,9 들어간 숫자 판별
            print("X", end='') # 3,6,9 들어간 숫자 출력 후 공백으로 끝냄
        else :
            print(i, end='') # 3,6,9 안 들어간 숫자도 출력 후 공백으로 끝냄 -> 6082 """

""" r, b, g = map(int, input().split())
# 빛 섞어 색 만들기
    for i in range(0, r) :
        for j in range(0, b) :
            for k in range(0, g) :
                print(i,j,k)

    print(r*b*g) # 마지막 개수 출력
    -> 6083 """

# 1초동안 마이크로 소리 강약 체크하는 횟수 h (<= 48,000)
# 한 번 체크한 값을 저장할 때 사용하는 비트수를 b (<= 32, 단 8의 배수)
# 좌우 등 소리를 저장할 트랙 개수인 채널 개수를 c (<= 5)
# 녹음할 시간(초) s (<= 6,000)
# 필요한 저장용량 계산 프로그램 작성, MB단위로 바꾸어 출력하며 소수점 첫째자리까지 공백 두고 출력

""" h, b, c, s = map(int,input().split())
    mb = (h*b*c*s)/8/1024/1024

    print("%.1f MB" %mb ) -> 6084 """

# 이미지의 가로해상도 w, 세로해상도 h,한 픽셀을 저장하기 위한 비트 b
# 압축하지 않고 저장하기 위해 필요한 저장용량 계산

""" w, h, b = map(int, input().split())
    mb = (w*h*b)/8/1024/1024

    print("%.2f MB" %mb) -> 6085 """

""" n = int(input()) #입력
    sum = 0  #자연수 합계 초기화
    i = 0  #1+2+3+ ... 더할 자연수 0으로 초기화

    while True:
        sum += i
        i += 1
        if (sum>=n) :
            break

    print(sum) -> 6086 """

""" n = int(input()) #정수 입력 , 추후 출력 시 3의 배수는 제외해야 함

    for i in range(1,n+1) :
        if i%3 == 0:
            continue
        else:
            print(i, end=' ') -> 6087 """

""" a, d, n = map(int, input().split()) # 시작값 a, 등차의 값 d, 몇번째 수 인 지 의미하는 정수 n
    # 1 3 5 입력 시 - 1-4-7-10-13 이므로 13 출력
    # 등차수열의 일반항 = a1 + (n-1)d

    print(a+(n-1)*d) -> 6088 """

""" a, r, n = map(int, input().split())
    # 등비수열 일반항 공식 = a1*r^(n-1)

    print(a*r**(n-1)) -> 6089 """

""" a, m, d, n = map(int, input().split())
    # 시작값, 곱할값, 더할값, 몇번째인지 나타내는 정수 입력

    for i in range(1,n):
        a = a*m+d
    print(a) -> 6090 """

""" # 최소공배수 구하기
    # day는 날 수, a/b/c 는 방문 주기
    a, b, c  = map(int, input().split())
    d = 1

    while d%a!=0 or d%b!=0 or d%c!=0:
        d+=1
    print(d) -> 6091 """

""" # 출석번호 n 번 무작위로 불렀을 때 각 번호(1~23)가 불린 횟수 각각 출력
    n = int(input())
    a = map(int, input().split())

    d=[] # 불린 번호 카운트 위해 리스트 변수 생성

    a = list(a) # map 함수로는 []에 접근할 수 없으므로 list 자료형으로 캐스팅

    for i in range (24):
        d.append(0)

    for i in range (n):
        d[a[i]] += 1 # 불린 번호에 대한 카운트 +1

    for i in range (1,24):
        print(d[i], end=' ') # 카운트한 값 공백두고 출력 -> 6092 """

""" # 출석번호 n번 무작위로 불렀을 때 부른 번호 거꾸로 출력, range(시작,끝,증감)
    n = int(input())
    a = map(int, input().split())

    a = list(a)

    for i in range (n-1,-1,-1): # n-2 n-1 ,,,, 2 1 0 까지
        print(a[i], end = ' ') -> 6093 """

""" # 출석번호 n번 무작위로 불렀을 때 가장 먼저 작은 번호 출력
    n = int(input())
    a = map(int, input().split())

    a = list(a)

    print(min(a)) -> 6094 """

""" # 바둑판에 올려놓을 흰 돌의 개수 n 입력
    # 둘째 줄 부터 n+1번째 줄까지 흰 돌을 놓을 좌표(x,y)가 n줄 입력

    d=[] # 마지막 바둑판 상황 출력 위해 리스트 생성

    for i in range(20):
        d.append([]) # 리스트 안에 다른 리스트 추가
        for j in range(20):
            d[i].append(0) # 리스트 안에 들어있는 리스트 안에 0 채워넣기

    n = int(input()) # 바둑판에 올려놓을 흰 돌 개수

    for i in range(n):
        x, y = input().split() # 흰 돌 좌표 입력
        d[int(x)][int(y)] = 1 # 입력된 좌표는 1로 출력

    for i in range(1, 20): # x좌표
        for j in range(1, 20): # y좌표
            print(d[i][j], end = ' ')
        print() -> 6095 """

""" # 바둑판 십자뒤집기 문제 어렵;;
    # 바둑판 깔려있는 상황 19*19 정수값 입력
    # 십자 뒤집기 횟수(n) 입력
    # 십자 뒤집기 좌표 횟수(n)만큼 입력

    d = [] # 입력할 바둑판 리스트, 깔려있는 상황

    for i in range(19):
        d.append([])  # 리스트 안에 다른 리스트 추가
        for j in range(19):
            d[i].append(0)  # 리스트 안에 들어있는 리스트 안에 0 채워넣기

    for i in range(19): # 생성된 리스트 d에 바둑판 현황 입력
        d[i] = list(map(int, input().split()))

    n = int(input()) # 뒤집기 횟수 입력

    for i in range(n):
        x, y = map(int, input().split()) # n번 만큼 x,y 좌표 입력
        for j in range(19): # x,y 한 번 입력 받을 때 마다 좌표값에 따라 조건문 통해 1/0 넣어줌
            if d[j][y-1] == 0 :
                d[j][y-1] = 1
            else :
                d[j][y-1] = 0

            if d[x-1][j] ==0 :
                d[x-1][j] = 1
            else :
                d[x-1][j] = 0

    for i in range(19): # 위 반복문 종료시 리스트에 속한 좌표 출력
        for j in range(19):
            print(d[i][j], end=' ')
        print() # j 반복문 종료되면 줄바꿈 -> 6096 """

""" # 설탕과자 뽑기
    # 격자판 세로 h, 가로 w, 막대의 개수 n, 각 막대의 길이 l, 막대를 놓는 방향 d, 위치 x,y
    # 첫 줄에 격자판 세로,가로 공백 입력 / 둘째 줄 막대 개수 입력 / 셋째 줄 막대 길이,방향,좌표 입력
    # 격자판 채운 막대의 모양 출력 / 막대에 의해 가려지면 1 아닌경우 0

    h, w = map(int, input().split()) # 격자판 세로,가로 입력
    n = int(input()) # 막대 개수 입력

    ans = [[0]*w for _ in range(h)] # list comprension 사용, 조건만족하는 것만 리스트 생성 가능
    # 가로 w X 세로 h 배열 생성

    for i in range(n):
        l, d, x, y = map(int, input().split()) # 막대 길이,방향,좌표(x,y) 입력
        for j in range(l):
            if d == 0: # 방향 d: 가로는 0 세로는 1
                ans[x-1][y-1+j] = 1
            else:
                ans[x-1+j][y-1] = 1 # -1 하는 이유는 입력좌표 x,y가 1,1부터 시작하기 때문임

    for i in range(w):
        for j in range(h):
            print(ans[i][j], end = ' ')
        print() -> 6097 """

# 성실한 개미, 먹이 미로에서 오른쪽, 아래로만 이동 가능
# 미로 상자의 구조 0(갈 수 있는 곳), 1(벽 또는 장애물), 2(먹이)
# 10*10 미로상자의 구조와 먹이 위치 입력
# 개미가 이동한 경로 9로 표시해 출력

array = [] # 2차원 배열
for i in range(10): # 10*10 배열 입력 받음
    array.append(list(map(int, input().split())))

x, y = 1, 1 # 좌표는 1,1부터 시작

while True:
    if array[x][y] == 0 :
        array[x][y] = 9 # 1.현재 칸 0이라면 9로 변환
    elif array[x][y] == 2 :
        array[x][y] = 9 # 2.현재 칸 2라면 반복문 종료
        break

    if array[x][y + 1] == 1 and array[x + 1][y] == 1 : # 3.오른쪽,아래 모두 1(벽)이면 반복문 종료
        break

    if array[x][y+1] != 1 :
        y = y+1
    elif array[x+1][y] != 1 :
        x = x+1
# 4.오른쪽 1 아니면 x 증가, 아래 1 아니면 y 증가

for i in range (10):
    for j in range(10):
        print(array[i][j], end=' ')
    print()


"""Solution for day 1"""

import pandas as pd

# Part 1

# Load data into dataframe
depth_df = pd.read_csv('depths.csv')
# Calculate differences
depth_df['diff'] = depth_df['depth'].diff()
print(f"Number of measurements larger than the previous: {depth_df[depth_df['diff'] > 0].count()[1]}")

# Part 2

# Calculate rolling average and difference
depth_df['depth_window'] = depth_df['depth'].rolling(3).sum()
depth_df['diff_window'] = depth_df['depth_window'].diff()
print(f"Number of measurements larger than the previous (window mode):"
      f"{depth_df[depth_df['diff_window'] > 0].count()[1]}")


import re
from itertools import islice

INPUT_LOC = "/Users/Alex/Desktop/Carnegie/15-421/Datasets/xfactor-database.txt"
OUTPUT_LOC = "/Users/Alex/Desktop/TestOut.txt"

def clean_Xfactor():
	#I removed every line before 10 | Nathan ... before running this

	myfile =  open(INPUT_LOC, "r")
	f2 = open(OUTPUT_LOC, "w")
	while True:
		#Pythons internal buffer is 4k
		#Lets assume average of 65 char lines
		#61 lines pulled in per read
		#Guess 3 pages given to python
		head = list(islice(myfile, 61*3))
		if not head:
			break

		for item in head:
			temp = item.split(" | ",14)
			if(len(temp) != 14):
				continue
			#id | firstname | lastname | dob | phone | email | auditiontype | numberofperformers | location | optin_1 | optin_2 | gender | zip | timestamp
			if not re.match(r"^[A-Za-z0-9\.\+_-]+@[A-Za-z0-9\._-]+\.[a-zA-Z]*$",temp[5]):
				temp[5] = "NULL"
			for x in range(1,14):
				if(temp[x] == "" or temp[x] == "0"):
					temp[x] = "NULL"

			if(temp[1] == "NULL" and temp[2] == "NULL" and temp[5] == "NULL"):
				continue

			final = ""
			for x in range(0,13):
				final = final + temp[x] + ":"
			final = final + temp[13]
			f2.write(final)

	myfile.close()
	f2.close()


#selenium & firefox(cli)
import io
from selenium import webdriver
from selenium.webdriver.firefox.options import Options

sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding = 'utf-8')
sys.stderr = io.TextIOWrapper(sys.stderr.detach(), encoding = 'utf-8')

firefox_options = Options()
firefox_options.add_argument("--headless")

driver = webdriver.Firefox(firefox_options=firefox_options,executable_path='D:/Atom_Workspace/section3/webdriver/firefox/geckodriver')
#driver.set_window_size(1920, 1280)

driver.implicitly_wait(5)
driver.get('https://google.com')
driver.save_screenshot("D:/Atom_Workspace/section3/website_ff1.png")

driver.implicitly_wait(5)
driver.get('https://www.daum.net')
driver.save_screenshot("D:/Atom_Workspace/section3/website_ff2.png")

driver.quit()


import re

import boto3
import pytest
from moto import mock_s3

from pylaprof import FS, S3, Profiler

from .sleepy import sleepy

def test_profiler_with_fs_storer(tmpcwd):
    """Check profiler with FS storer.

    We set a period of 0.01s and run `sleepy` for 0.105s, so we should get a report with
    10 hits of this function.
    """
    with Profiler(period=0.01, storer=FS(path=lambda: "report.txt")):
        sleepy(0.105)

    regex = re.compile(r".*;sleepy \(\/[^)]+\/sleepy\.py:28\) 10")
    with open("report.txt", "r") as fp:
        report = fp.read()
    for line in report.split("\n"):
        if regex.match(line):
            return  # Ok!
    else:
        pytest.fail("something is wrong with the report:\n" + report)

@mock_s3
def test_profiler_with_s3_storer():
    """Check profiler with S3 storer.

    We set a period of 0.01s and run `sleepy` for 0.105s, so we should get a report with
    10 hits of this function.
    """
    boto3.resource("s3").Bucket("pylaprof").create()

    with Profiler(period=0.01, storer=S3(key=lambda: "report.txt")):
        sleepy(0.105)

    regex = re.compile(r".*;sleepy \(\/[^)]+\/sleepy\.py:28\) 10")
    report = (
        boto3.resource("s3")
        .Object("pylaprof", "report.txt")
        .get()["Body"]
        .read()
        .decode("utf-8")
    )
    for line in report.split("\n"):
        if regex.match(line):
            return  # Ok!
    else:
        pytest.fail("something is wrong with the report:\n" + report)


from collections import defaultdict
import Id3_dubbed
import copy

def get_valid_examples(attributes):
    examples = defaultdict(list)
    loop = 0
    with open("../Top5000Words/training-set.feat", 'r') as file:
        for line in file:
            # print(line)
            loop = loop + 1
            words = line.split(' ')
            # print(words[0:1])
            comb = words[1:]
            comb = [int(i.split(':', 1)[0]) for i in comb]
            # print(comb)
            # attributes.sort()
            # print(attributes)
            subt = [x for x in comb if x in attributes]
            # print(subt)
            attr = words[0:1] + subt
            # print(words[0:1][0])
            # print(attr)
            for i in range(len(attr)):
                examples[loop].append(attr[i])
            if int(words[0:1][0]) >= 7:
                examples[loop].append('y')
            else:
                examples[loop].append('n')
    for k, v in examples.items():
        examples[k] = examples[k][1:]
    # print(examples)
    return examples

def accuracy(test,node):
    accurate = 0
    sizes = len(test)
    for k,v in test.items():
        temp = copy.copy(node)
        while temp.feature != -1:
            if temp.feature in v:
                temp = temp.right
            else:
                temp = temp.left
        if v[len(v)-1] == 'y':
            if temp.sample == 1:
                accurate = accurate+1
        else:
            if temp.sample == 0:
                accurate = accurate+1
    # print(accurate)
    return accurate/sizes

def accuracy2(test,node, node1, node2):
    accurate = 0
    sizes = len(test)
    for k,v in test.items():
        temp = copy.copy(node)
        temp1 = copy.copy(node1)
        temp2 = copy.copy(node2)
        while temp.feature != -1:
            if temp.feature in v:
                temp = temp.right
            else:
                temp = temp.left
        while temp1.feature != -1:
            if temp1.feature in v:
                temp1 = temp1.right
            else:
                temp1 = temp1.left
        while temp2.feature != -1:
            if temp2.feature in v:
                temp2 = temp2.right
            else:
                temp2 = temp2.left
        count1 = 0
        count2 = 0
        if temp.sample == 1:
            count1 = count1+1
        else:
            count2 = count2+1
        if temp1.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1
        if temp2.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1

        if count1 >= count2:
            temp.sample = 1
        else:
            temp.sample = 0
        if v[len(v)-1] == 'y':
            if temp.sample == 1:
                accurate = accurate+1
        else:
            if temp.sample == 0:
                accurate = accurate+1
    # print(accurate)
    return accurate/sizes

def accuracy3(test,node, node1, node2, node3, node4):
    accurate = 0
    sizes = len(test)
    for k,v in test.items():
        temp = copy.copy(node)
        temp1 = copy.copy(node1)
        temp2 = copy.copy(node2)
        temp3 = copy.copy(node3)
        temp4 = copy.copy(node4)
        while temp.feature != -1:
            if temp.feature in v:
                temp = temp.right
            else:
                temp = temp.left
        while temp1.feature != -1:
            if temp1.feature in v:
                temp1 = temp1.right
            else:
                temp1 = temp1.left
        while temp2.feature != -1:
            if temp2.feature in v:
                temp2 = temp2.right
            else:
                temp2 = temp2.left
        while temp3.feature != -1:
            if temp3.feature in v:
                temp3 = temp3.right
            else:
                temp3 = temp3.left

        while temp4.feature != -1:
            if temp4.feature in v:
                temp4 = temp4.right
            else:
                temp4 = temp4.left
        count1 = 0
        count2 = 0
        if temp.sample == 1:
            count1 = count1+1
        else:
            count2 = count2+1
        if temp1.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1
        if temp2.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1
        if temp3.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1
        if temp4.sample == 1:
            count1 = count1 + 1
        else:
            count2 = count2 + 1

        if count1 >= count2:
            temp.sample = 1
        else:
            temp.sample = 0
        if v[len(v)-1] == 'y':
            if temp.sample == 1:
                accurate = accurate+1
        else:
            if temp.sample == 0:
                accurate = accurate+1
    # print(accurate)
    return accurate/sizes

def main(test_file):
    loop = 0
    lst = []
    with open("../Top5000Words/selected-features-indices.txt", 'r') as file:
        for line in file:
            lst.append([int(x) for x in line.split()])
        attributes = [x[0] for x in lst]
    attributes2_1 = []
    attributes2_1[0:1250] = attributes[0:1250]
    attributes2_1[1250:2500] = attributes[2500:3750]
    attributes2_2 = []
    attributes2_2[0:1250] = attributes[1250:2500]
    attributes2_2[1250:2500] = attributes[3750:5000]
    attributes2_3 = []
    attributes2_3[0:1250] = attributes[0:1250]
    attributes2_3[1250:2500] = attributes[3750:5000]

    attributes3_1 = []
    attributes3_1[0:500] = attributes[0:500]
    attributes3_1[500:1000] = attributes[2500:3000]
    attributes3_2 = []
    attributes3_2[0:500] = attributes[500:1000]
    attributes3_2[500:1000] = attributes[3000:3500]
    attributes3_3 = []
    attributes3_3[0:500] = attributes[1000:1500]
    attributes2_1[500:1000] = attributes[3500:4000]
    attributes3_4 = []
    attributes3_4[0:500] = attributes[1500:2000]
    attributes3_4[500:1000] = attributes[4000:4500]
    attributes3_5 = []
    attributes3_5[0:500] = attributes[2000:2500]
    attributes3_5[500:1000] = attributes[4500:5000]
    # print(attributes2_1)

    examples = get_valid_examples(attributes)
    examples2_1 = get_valid_examples(attributes2_1)
    examples2_2 = get_valid_examples(attributes2_2)
    examples2_3 = get_valid_examples(attributes2_3)
    examples3_1 = get_valid_examples(attributes3_1)
    examples3_2 = get_valid_examples(attributes3_2)
    examples3_3 = get_valid_examples(attributes3_3)
    examples3_4 = get_valid_examples(attributes3_4)
    examples3_5 = get_valid_examples(attributes3_5)
    testExamples = defaultdict(list)
    loop = 0
    with open(test_file, 'r') as file:
        for line in file:
            loop = loop + 1
            words = line.split(' ')
            # print(words[0:1])
            comb = words[1:]
            comb = [int(i.split(':', 1)[0]) for i in comb]
            subt = [x for x in comb if x in attributes]
            # print(subt)
            attr = words[0:1] + subt
            # print(words[0:1][0])
            # print(attr)
            for i in range(len(attr)):
                testExamples[loop].append(attr[i])
            if int(words[0:1][0]) >= 7:
                testExamples[loop].append('y')
            else:
                testExamples[loop].append('n')
    for k, v in testExamples.items():
        testExamples[k] = testExamples[k][1:]
    # print(testExamples)

    # Run ID3
    print("1 decision tree of 5k attributes")
    node = Id3_dubbed.decisionTree(examples, attributes, 0)
    # temp = copy.copy(node)
    # print("No. of nodes",tree_size(temp))
    print("generated decision tree")
    # print("Accuracy on test set is ", accuracy(testExamples, node)*100)
    # print("Accuracy on training set is ", accuracy(examples, node)*100)
    print("1    ",accuracy(examples, node)*100,"    ", accuracy(testExamples, node)*100)
    print("3 decision trees of 2.5k attributes each")
    node = Id3_dubbed.decisionTree(examples2_1, attributes2_1, 0)
    node1 = Id3_dubbed.decisionTree(examples2_2, attributes2_2, 0)
    node2 = Id3_dubbed.decisionTree(examples2_3, attributes2_3, 0)
    # temp = copy.copy(node)
    # print("No. of nodes", tree_size(temp))
    print("generated decision tree")
    # print("Accuracy on test set is ", accuracy2(testExamples, node, node1, node2)*100)
    # print("Accuracy on training set is ", accuracy2(examples, node, node1, node2)*100)
    print("3    ", accuracy2(examples, node, node1, node2)*100, "    ", accuracy2(testExamples, node, node1, node2)*100)
    print("5 decision trees of 1k attributes each")
    node = Id3_dubbed.decisionTree(examples3_1, attributes3_1, 0)
    node1 = Id3_dubbed.decisionTree(examples3_2, attributes3_2, 0)
    node2 = Id3_dubbed.decisionTree(examples3_3, attributes3_3, 0)
    node3 = Id3_dubbed.decisionTree(examples3_4, attributes3_4, 0)
    node4 = Id3_dubbed.decisionTree(examples3_5, attributes3_5, 0)
    # temp = copy.copy(node)
    # print("No. of nodes", tree_size(temp))
    print("generated decision tree")
    # print("Accuracy on test set is ", accuracy3(testExamples, node, node1, node2, node3, node4)*100)
    # print("Accuracy on training set is ", accuracy3(examples, node, node1, node2, node3, node4)*100)
    print("5    ", accuracy3(examples, node, node1, node2, node3, node4)*100,"  ",accuracy3(testExamples, node, node1, node2, node3, node4)*100)
if __name__ == '__main__':
    import sys
    testing_data = sys.argv[1]
    print("Learning Random Forest using Feature Bagging and calculating accuracy on training and test data")
    print("result will be displayed as")
    print("No. of Trees     Training Accuracy      Testing Accuracy")
    main(testing_data)


#coding:utf-8
#Artur Brito Souza - 118210056
#Laboratorio de Progamacao 1, 2018.2
#Conta Alertas do Açude

def tem_afinidade(l1, l2):
	cont = 0
	for n in range(len(l1)):
		for e in range(len(l2)):
			if l1[n] == l2[e]:
				cont += 1
	if cont >= 3:
		return True
	else:
		return False


import pyverilog.vparser.ast as vast
from pyverilog.ast_code_generator.codegen import ASTCodeGenerator as VGen

import sys
sys.setrecursionlimit(100000)

import logging

logger = logging.getLogger("ABG_VHandle")
logger.setLevel(logging.INFO)
logHandler = logging.StreamHandler()
logHandler.setFormatter(logging.Formatter(logging.BASIC_FORMAT, None))
logger.addHandler(logHandler)

class VHandle:
    def __init__(self, vnode_root, vnodes_pe, vnodes_fifo, vnodes_fifoInst):
        self.vnode_root      = vnode_root
        self.vnode_moduleDef = vnode_root.children()[0].children()[1]
        self.vnodes_pe       = vnodes_pe
        self.vnodes_fifo     = vnodes_fifo
        self.vnodes_fifoInst = vnodes_fifoInst
    def toCustomizedNames(self):
        logger.debug(f"Original Top Module Name: {self.vnode_moduleDef.name}")
        self.vnode_moduleDef.name += "_postABG"
        logger.debug(f"Modified Top Module Name: {self.vnode_moduleDef.name}")
        for vnode_fifoInst in self.vnodes_fifoInst:
            vnode_fifo = next(filter(lambda node : isinstance(node, vast.Instance), vnode_fifoInst.children()))
            assert(vnode_fifoInst.module == vnode_fifo.module)
            logger.debug(f"Original FIFO Module Name for Instance {vnode_fifo.name}: {vnode_fifo.module}")
            vnode_fifoInst.module = vnode_fifo.name + "_M"
            vnode_fifo.module = vnode_fifo.name + "_M"
            assert(vnode_fifoInst.module == vnode_fifo.module)
            logger.debug(f"Modified FIFO Module Name for Instance {vnode_fifo.name}: {vnode_fifo.module}")
    def toRTL(self):
        # may involve deep recursion...
        # due to impl. of pyverilog ASTCodeGenerator ...
        # which is a recursive DFS
        top_module_name = self.vnode_moduleDef.name
        rtl = VGen().visit(self.vnode_moduleDef)
        return top_module_name, rtl


#CSV 中文可以叫做都好分隔符或祖父分割值，是纯文本形式储存表格数据。
#就是特定字符分割的纯文本

import csv

with open('writ.csv', 'w') as csvfile:
    #初始化一个对象
    #delimiter='*' 修改列与列之间的分隔符为 ‘*’
    writer = csv.writer(csvfile, delimiter='*')
    #写入语句
    writer.writerow(['id', 'name', 'age'])
    writer.writerow(['10001', 'Mike', 10])
    writer.writerow(['40050', 'mary', 55])
    writer.writerow(['10005', 'bob', 40])

#使用字典写入
#中文的话要指定表那格式
with open('dis.csv', 'w', encoding='utf-8') as text:
    first = ['id', 'name', 'age']
    #fieldnames=first, 指定字典键名
    #DictWriter 初始化一个字典写入对象
    writer = csv.DictWriter(text, fieldnames=first)
    #调用writeheader() 先写入头信息
    writer.writeheader()
    writer.writerow({'id':'10001','name':'杨', 'age':10})
    writer.writerow({'id':'10002','name':'Mary', 'age':12})
    writer.writerow({'id':'10003','name':'Bob', 'age':34})


import socket
import sys
import threading
import fcntl, os
from time import sleep
from copy import deepcopy

ip = "127.0.0.1"
port = 40057

received_data = []
lock = threading.Lock()

def listen_thread():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_address = (ip, port)
    s.bind(server_address)
    fcntl.fcntl(s, fcntl.F_SETFL, os.O_NONBLOCK)
    #print("####### Server is listening #######")

    while True:
        lock.acquire()
        try:
            data = s.recv(128)
        except socket.error:
            pass
        else:
            #received_data.append(int.from_bytes(data, "little"))
            print(",,,,,MARK")
        lock.release()
        sleep(.1)

def socket_listen():
    t = threading.Thread(target=listen_thread)
    t.daemon = True
    t.start()

def socket_empty_data():
    lock.acquire()
    data = deepcopy(received_data)
    received_data.clear()
    lock.release()
    return data


import numpy as np
from sklearn.cluster import KMeans
from sklearn.decomposition import TruncatedSVD

from preprocessing import MTL_preprocessing

class MTL:
    def __init__(self, data, link = 'linear', intercept = True, n_class = 2, penalty = 'new', standardization = True):
        # task j has n_j samples and an empirical risk f_j (sample average)
        # objective fcn: \sum_j n_j [ f_j(w - v_j) + lambda_j \| v_j \|_2 ]
        # data = [X, y]
        # X: a list of m feature matrices, each of which is (n_j, d)
        # y: a list of m response vectors, each of which is (n_j, 1)
        # link = 'linear' or 'logistic'
        # n_class: number of classes in logistic regression, ignored for linear regression
        # class indices in y range from 0, 1, ..., n_class - 1
        # penalty: 'new' or 'ridge'
            # 'new': lbd_j * ||v|| on the j-th node
            # 'ridge': lbd_j * ||v||^2

        [self.X, self.y, self.X_means, self.X_stds, self.y_mean, self.y_std, self.n_list, self.d_out] = MTL_preprocessing(data, link, intercept, n_class, standardization)
        self.y_raw = data[1] # used in clustered and low-rank ARMUL

        self.intercept, self.n_class, self.standardization = intercept, n_class, standardization

        self.m = len(data[0])
        self.d = data[0][0].shape[1]
        if intercept: # No. of feature + 1 (when intercept = True)
            self.d += 1
        self.N = np.sum(self.n_list)
        self.link = link
        self.penalty = penalty
        self.models = dict()

    def vanilla_train(self, lbd = None, eta_global = 0.01, eta_local = 0.01, T_global = 100, T_local = 1):
        # vanilla ARMUL
        # lbd: a list of m penalty parameters
        if lbd is None:
            lbd = 0.1 * np.sqrt(self.d / self.n_list)
        gamma = np.zeros((self.d, self.d_out)) # global model
        V = np.zeros((self.m, self.d, self.d_out)) # local corrections

        for t in range(T_global):
            g = np.zeros((self.d, self.d_out))
            for idx in range(self.m):
                # update v_idx
                v = pgd_v(self.X[idx], self.y[idx], self.link, lbd[idx], gamma, V[idx], self.penalty, eta_local, T_local)
                V[idx] = v

                # compute the local gradient of gamma
                g += gradient_loss(self.X[idx], self.y[idx], gamma - v, self.link) / self.N

            # gd on gamma
            gamma -= eta_global * g

        # global model
        self.models['vanilla_gamma'] = gamma

        # get local models
        self.models['vanilla'] = gamma - V

    def clustered_train(self, lbd = None, K = 2, eta_B = 0.01, eta_local = 0.01, T_global = 100, T_B = 1, T_local = 1):
        if lbd is None:
            lbd = 0.1 * np.sqrt(self.d / self.n_list)

        ####################
        # warm start by STL
        if self.link == 'linear' or self.n_class == 1:
            base = baselines([self.X, self.y], self.link, False, self.n_class, False)
        else:
            base = baselines([self.X, self.y_raw], self.link, False, self.n_class, False)

        base.STL_train(eta = eta_local, T = T_global)
        stl = base.models['STL'] # STL

        kmeans = KMeans(n_clusters = K, random_state = 0).fit(stl.reshape(self.m, self.d * self.d_out))
        B = np.zeros((K, self.d, self.d_out))
        Z = np.zeros((self.m, K))
        for k in range(K):
            B[k] = kmeans.cluster_centers_[k].reshape(self.d, self.d_out)
        Z[:, kmeans.labels_] = 1
        V = np.zeros((self.m, self.d, self.d_out))
        for j in range(self.m):
            V[j] = B[kmeans.labels_[j]] - stl[j]

        ####################

        for t in range(T_global):
            # update v
            for idx in range(self.m):
                # compute the global model
                gamma = np.zeros((self.d, self.d_out))
                for r in range(self.d_out):
                    tmp1 = B[:, :, r].T
                    tmp2 = Z[idx].reshape(-1, 1)
                    gamma[:, r] = (tmp1 @ tmp2).reshape(-1,)
                # update v_idx
                v = pgd_v(self.X[idx], self.y[idx], self.link, lbd[idx], gamma, V[idx], self.penalty, eta_local, T_local)
                V[idx] = v

            # update B and Z
            B = gd_B(self.X, self.y, self.link, B, Z, V, eta_B, T_B)
            Z = hard_Z(self.X, self.y, self.link, B, V)

        # get local models
        Theta_clustered = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            Br = B[:, :, r]
            Theta_clustered[:, :, r] = Z @ Br - V[:, :, r] # m-by-d

        self.models['clustered'] = Theta_clustered
        self.models['clustered_B'] = B
        self.models['clustered_Z'] = Z

    def lowrank_train(self, lbd = None, K = 2, eta_B = 0.01, eta_Z = 0.01, eta_local = 0.01, T_global = 100, T_B = 1, T_Z = 1, T_local = 1):
        if lbd is None:
            lbd = 0.1 * np.sqrt(self.d / self.n_list)

        ####################
        # warm start by STL
        if self.link == 'linear' or self.n_class == 1:
            base = baselines([self.X, self.y], self.link, False, self.n_class, False)
        else:
            base = baselines([self.X, self.y_raw], self.link, False, self.n_class, False)

        base.STL_train(eta = eta_local, T = T_global)
        stl = base.models['STL'] # STL

        ########################################

        svd = TruncatedSVD(n_components = K, n_iter = 10, random_state = 0)
        svd.fit( stl.reshape(self.m, self.d * self.d_out).T )
        Z = svd.components_.T # m by K
        B = np.zeros((K, self.d, self.d_out))
        tmp = np.zeros((self.m, self.d, self.d_out)) # low-rank model
        for r in range(self.d_out):
            B[:, :, r] = Z.T @ stl[:, :, r]
            tmp[:, :, r] = Z @ B[:, :, r]
        V = tmp - stl

        #####################
        # balancing
        tmp = np.linalg.norm(B.reshape(-1,))
        B, Z = B / np.sqrt(tmp), Z * np.sqrt(tmp)

        for t in range(T_global):
            # Step 1: update v
            for idx in range(self.m):
                # compute the global model
                gamma = np.zeros((self.d, self.d_out))
                for r in range(self.d_out):
                    tmp1 = B[:, :, r].T
                    tmp2 = Z[idx].reshape(-1, 1)
                    gamma[:, r] = (tmp1 @ tmp2).reshape(-1,)
                # update v_idx
                v = pgd_v(self.X[idx], self.y[idx], self.link, lbd[idx], gamma, V[idx], self.penalty, eta_local, T_local)
                V[idx] = v

            # Step 2: update B and Z
            # compute the current objective value and save the current B, Z
            obj = self.get_loss_lowrank(B, Z, V)
            B_cache, Z_cache = B.copy(), Z.copy()

            # update B and Z
            B = gd_B(self.X, self.y, self.link, B, Z, V, eta_B, T_B)
            Z = gd_Z(self.X, self.y, self.link, B, Z, V, eta_Z, T_Z)

            # if the objective increases, use the old B, Z
            tmp = self.get_loss_lowrank(B, Z, V)
            if tmp > obj:
                B, Z = B_cache.copy(), Z_cache.copy()
            obj = tmp

        self.models['lowrank'] = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            self.models['lowrank'][:, :, r] = Z @ B[:, :, r] - V[:, :, r] # m-by-d
        self.models['lowrank_B'] = B
        self.models['lowrank_Z'] = Z

    def predict(self, X_test, model):
        return prediction(X_test, self.models[model], self.link, self.X_means, self.X_stds, self.y_mean, self.y_std)

    def evaluate(self, data_test, model):
        y_pred = self.predict(data_test[0], model)
        return evaluation(y_pred, data_test[1], self.link)

    def get_loss_lowrank(self, B, Z, V):
        model = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            model[:, :, r] = Z @ B[:, :, r] - V[:, :, r] # m-by-d
        ans = 0
        for j in range(self.m):
            ans += self.n_list[j] * loss(self.X[j], self.y[j], model[j], link = self.link)
        return ans

# baselines
# single-task learning, hard parameter sharing, clustered MTL and low-rank MTL
class baselines:
    def __init__(self, data, link = 'linear', intercept = True, n_class = 2, standardization = True):
        [self.X, self.y, self.X_means, self.X_stds, self.y_mean, self.y_std, self.n_list, self.d_out] = MTL_preprocessing(data, link, intercept, n_class, standardization)
        self.m = len(self.X)
        self.d = self.X[0].shape[1]
        self.N = np.sum(self.n_list)
        self.link = link
        self.models = dict()

    def STL_train(self, eta = 0.01, T = 1000): # STL
        Theta_STL = np.zeros((self.m, self.d, self.d_out))
        for t in range(T):
            for j in range(self.m):
                theta = Theta_STL[j]
                g = gradient_loss(self.X[j], self.y[j], theta, self.link) / self.n_list[j]
                Theta_STL[j] = theta - eta * g
        self.models['STL'] = Theta_STL

    def DP_train(self, eta = 0.01, T = 1000): # DP
        gamma = np.zeros((self.d, self.d_out))
        for t in range(T):
            g = np.zeros((self.d, self.d_out))
            for j in range(self.m):
                g += gradient_loss(self.X[j], self.y[j], gamma, self.link) / self.N
            gamma -= eta * g
        Theta_DP = np.zeros((self.m, self.d, self.d_out))
        for j in range(self.m):
            Theta_DP[j] = gamma
        self.models['DP'] = Theta_DP

    def clustered_train(self, K = 2, eta_B = 0.01, T = 100, T_B = 1):

        ####################
        # warm start by STL
        self.STL_train(eta = eta_B, T = T)
        stl = self.models['STL'] # STL

        kmeans = KMeans(n_clusters = K, random_state = 0).fit(stl.reshape(self.m, self.d * self.d_out))
        B = np.zeros((K, self.d, self.d_out))
        Z = np.zeros((self.m, K))
        for k in range(K):
            B[k] = kmeans.cluster_centers_[k].reshape(self.d, self.d_out)
        Z[:, kmeans.labels_] = 1

        #########################

        for t in range(T):
            # update B and Z
            B = gd_B(X = self.X, y = self.y, link = self.link, B = B, Z = Z, eta = eta_B, T = T_B)
            Z = hard_Z(X = self.X, y = self.y, link = self.link, B = B)

        # get local models
        Theta_clustered = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            Br = B[:, :, r]
            Theta_clustered[:, :, r] = Z @ Br # m-by-d

        self.models['clustered'] = Theta_clustered
        self.models['clustered_B'] = B
        self.models['clustered_Z'] = Z

    def lowrank_train(self, K = 2, eta_B = 0.01, eta_Z = 0.01, T = 100, T_B = 1, T_Z = 1):

        ####################
        # warm start by STL
        self.STL_train(eta = eta_B, T = T)
        stl = self.models['STL'] # STL
        svd = TruncatedSVD(n_components = K, n_iter = 10, random_state = 0)
        svd.fit( stl.reshape(self.m, self.d * self.d_out).T )
        Z = svd.components_.T # m by K
        B = np.zeros((K, self.d, self.d_out))
        for r in range(self.d_out):
            B[:, :, r] = Z.T @ stl[:, :, r]
        #####################
        # balancing
        tmp = np.linalg.norm(B.reshape(-1,))
        B, Z = B / np.sqrt(tmp), Z * np.sqrt(tmp)
        self.models['lowrank'] = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            self.models['lowrank'][:, :, r] = Z @ B[:, :, r] # m-by-d
        obj = self.get_loss(model = 'lowrank')

        for t in range(T):
            # save the current model
            model_cache = self.models['lowrank'].copy

            # update B and Z
            B = gd_B(X = self.X, y = self.y, link = self.link, B = B, Z = Z, eta = eta_B, T = T_B)
            Z = gd_Z(X = self.X, y = self.y, link = self.link, B = B, Z = Z, eta = eta_Z, T = T_Z)
            #####################
            self.models['lowrank'] = np.zeros((self.m, self.d, self.d_out))
            for r in range(self.d_out):
                self.models['lowrank'][:, :, r] = Z @ B[:, :, r] # m-by-d
            tmp = self.get_loss(model = 'lowrank')

            if tmp > obj:
                self.models['lowrank'] = model_cache
                break
            obj = tmp
            #####################

        # get local models
        self.models['lowrank'] = np.zeros((self.m, self.d, self.d_out))
        for r in range(self.d_out):
            self.models['lowrank'][:, :, r] = Z @ B[:, :, r] # m-by-d
        self.models['lowrank_B'] = B
        self.models['lowrank_Z'] = Z

    def predict(self, X_test, model):
        return prediction(X_test, self.models[model], self.link, self.X_means, self.X_stds, self.y_mean, self.y_std)

    def evaluate(self, data_test, model):
        y_pred = self.predict(data_test[0], model)
        return evaluation(y_pred, data_test[1], self.link)

    def get_loss(self, model):
        ans = 0
        for j in range(self.m):
            ans += self.n_list[j] * loss(self.X[j], self.y[j], self.models[model][j], link = self.link)
        return ans

####################################################
## Auxiliaries

def gradient_loss(X, y, b, link = 'linear'):
    z = X @ b

    if link == 'logistic':
        d_out = b.shape[1]
        if d_out == 1: # y_i = 0 or 1
            z = 1 / (1 + np.exp(-z))
        else: # one-hoc encoding
            tmp = np.exp(z)
            tmp2 = tmp @ np.ones((d_out, 1))
            z = tmp / tmp2

    return X.T @ (z - y)

def pgd_v(X, y, link, lbd, w, v, penalty = 'new', eta = 0.01, T = 100):
    # find an (approximately) optimal v_j for a given w
    # i.e. approximate minimizer of f_j(w - v_j) + lambda_j \| v_j \|_2
    # optimization algorithm: proximal gd (new) or gd (ridge)

    # X: (n, d), y: (n, 1)
    # lbd: penalty parameter
    # w: global model
    # v: local correction
    # eta: stepsize
    # T: number of iterations

    v_t = v
    for t in range(T):
        g = - gradient_loss(X, y, w - v_t, link) / len(y)
        tmp = v_t - eta * g # gradient descent

        if penalty == 'new':
            r = np.linalg.norm(tmp, ord = 'fro')
            if r > 0:
                v_t = max(0, 1 - lbd * eta / r) * tmp
            else:
                v_t = 0
        else: # 'ridge'
            v_t -= eta * (g + lbd * v_t)

    return v_t

def gd_B(X, y, link, B, Z, V = None, eta = 0.01, T = 100):
    # B: K-d-d_out
    # Z: m-K
    # V: m-d-d_out
    K, d, d_out = B.shape
    m = Z.shape[0]
    N = sum([len(l) for l in y])
    if V is None:
        V = np.zeros((m, d, d_out))
    B_t = B
    for t in range(T):
        g = np.zeros((d, d_out))
        for idx in range(m):
            # compute the global model
            gamma = np.zeros((d, d_out))
            for r in range(d_out):
                gamma[:, r] = ( B_t[:, :, r].T @ Z[idx].reshape(-1, 1) ).reshape(-1,)
            g += gradient_loss(X[idx], y[idx], gamma - V[idx], link) / N

        # GD on B
        for k in range(K):
            B_t[k] = B_t[k] - eta * np.sum(Z[:, k]) * g
    return B_t

def gd_Z(X, y, link, B, Z, V = None, eta = 0.01, T = 100):
    # B: K-d-d_out
    # Z: m-K
    # V: m-d-d_out
    K, d, d_out = B.shape
    m = Z.shape[0]
    n_list = [len(l) for l in y]
    if V is None:
        V = np.zeros((m, d, d_out))
    Z_t = Z
    for t in range(T):
        for idx in range(m):
            # compute the global model
            gamma = np.zeros((d, d_out))
            for r in range(d_out):
                gamma[:, r] = ( B[:, :, r].T @ Z_t[idx].reshape(-1, 1) ).reshape(-1,)
            g_idx = gradient_loss(X[idx], y[idx], gamma - V[idx], link) / n_list[idx]

            # GD on Z
            for k in range(K):
                Z_t[idx, k] = Z_t[idx, k] - eta * np.sum(g_idx * B[k])
    return Z_t

def hard_Z(X, y, link, B, V = None):
    # B: K-d-d_out
    # Z: m-K
    # V: m-d-d_out
    K, d, d_out = B.shape
    m = len(X)
    Z = np.zeros((m, K))
    if V is None:
        V = np.zeros((m, d, d_out))

    for idx in range(m):
        tmp = np.zeros(K)
        for k in range(K):
            tmp[k] = loss(X[idx], y[idx], B[k] - V[idx], link)
        Z[idx, np.argmin(tmp)] = 1
    return Z

def loss(X, y, b, link = 'linear'):
    # X: (n, d), y: (n, d_out)
    n = X.shape[0]
    z = X @ b   # (n, d_out)
    if link == 'linear':
        return np.mean( (z - y) ** 2 )
    if link == 'logistic':
        d_out = b.shape[1]
        if d_out == 1: # y_i = 0 or 1
            return np.mean( np.log(1 + np.exp(z)) - y * z )
        else: # one-hoc encoding
            tmp = np.exp(z)
            tmp2 = tmp @ np.ones((d_out, 1))
            return np.mean( np.log(tmp2) ) - np.sum(y * z) / n

def prediction(X_test, Theta, link = 'linear', X_means = np.zeros((1, 1)), X_stds = np.ones((1, 1)), y_mean = 0, y_std = 1):
    # X_test: m tasks
    # output: m arrays of shape = (n,)

    intercept = True
    if X_test[0].shape[1] == Theta.shape[1]: # no all-one column added
        intercept = False

    y_pred = []
    for (j, X) in enumerate(X_test):
        if intercept: # add an all-one column
            X = np.concatenate((np.ones((X.shape[0], 1)), X), axis = 1)

        # standardization using the means and stds of the training data
        X0 = (X - X_means.T) / X_stds.T
        z = X0 @ Theta[j]

        if link == 'linear':
            y_pred_j = (z * y_std + y_mean)
        if link == 'logistic':
            if Theta.shape[2] == 1:
                y_pred_j = ((z >= 0) + 0)
            else:
                y_pred_j = np.argmax(z, axis = 1)

        y_pred.append(y_pred_j.reshape(-1,))
    return y_pred

def evaluation(y_pred, y_true, link = 'linear'):
    # both y_pred and y_true have m tasks
    m = len(y_pred)
    n_list = np.zeros(m) # local sample sizes
    err = np.zeros(m) # MSE or misclassification error
    tmp = np.zeros(m) # for computing R2 in linear regression

    for (j, y_pred_j) in enumerate(y_pred):
        y_pred_j = y_pred_j.reshape(-1,)
        n_list[j] = len(y_pred_j)
        y_true_j = y_true[j].reshape(-1,)
        if link == 'linear':
            err[j] = np.mean( (y_pred_j - y_true_j) ** 2 )
            tmp[j] = np.mean( ( y_true[j] - np.mean(y_true_j) ) ** 2 )
        if link == 'logistic':
            err[j] = np.mean(y_pred_j != y_true_j)
    N = np.sum(n_list)

    ans = dict()
    ans['errors'] = err
    ans['average error'] = np.sum(err * n_list) / N

    if link == 'linear':
        total_variance = np.sum(tmp * n_list) / N
        ans['R2'] = 1 - ans['average error'] / total_variance

    return ans


# https://www.acmicpc.net/problem/20055
# Implementation

from collections import deque
from sys import stdin

n, k = map(int, stdin.readline().split())
seq = deque(map(int, stdin.readline().split()))
robots = deque(False for _ in range(n))
zero = seq.count(0)
for t in range(1, 10**10):
    seq.appendleft(seq.pop())
    robots.pop()
    robots.appendleft(False)
    robots[-1] = False
    for i in reversed(range(n - 1)):
        if robots[i] and not robots[i + 1] and seq[i + 1] > 0:
            robots[i] = False
            robots[i + 1] = True
            seq[i + 1] -= 1
            if seq[i+1] == 0:
                zero += 1
    robots[-1] = False
    if seq[0] > 0:
        robots[0] = True
        seq[0] -= 1
        if seq[0] == 0:
            zero += 1
    if zero >= k:
        print(t)
        break


import datetime
import hashlib
import json
from urllib.parse import urlparse

import requests

class Blockchain:
    def __init__(self):
        self.chain = []
        self.transactions = []
        self.nodes = set()
        self.create_block(proof=1, previous_hash="0")

    @property
    def length(self):
        return len(self.chain)

    def create_block(self, proof, previous_hash):
        block = {
            "index": len(self.chain) + 1,
            "timestamp": str(datetime.datetime.now()),
            "proof": proof,  # Nonce
            "previous_hash": previous_hash,
            "transactions": self.transactions,
        }
        self.transactions = []
        self.chain.append(block)
        return block

    def get_previous_block(self):
        return self.chain[-1]

    def proof_of_work(self, previous_proof):
        new_proof = 1
        check_proof = False
        while check_proof is False:
            hash_operation = self.calculate_hash_operation(new_proof, previous_proof)
            if hash_operation[:4] == "0000":
                check_proof = True
            else:
                new_proof += 1
        return new_proof

    def hash(self, block):
        encoded_block = json.dumps(block, sort_keys=True).encode()
        return hashlib.sha256(encoded_block).hexdigest()

    def calculate_hash_operation(self, new_proof, previous_proof):
        calc = new_proof ** 2 - previous_proof ** 2
        return hashlib.sha256(str(calc).encode()).hexdigest()

    def is_chain_valid(self, chain):
        previous_block = chain[0]
        block_index = 1

        while len(chain) > block_index:
            block = chain[block_index]

            if not self._validate_block_hash(previous_block, block):
                return False

            if not self._validate_block_proof(previous_block, block):
                return False

            block_index += 1
            previous_block = block

        return True

    def add_transaction(self, sender, receiver, amount):
        self.transactions.append(
            {"sender": sender, "receiver": receiver, "amount": amount}
        )

        previous_block = self.get_previous_block()
        return previous_block["index"] + 1

    def add_node(self, address):
        url = urlparse(address)
        self.nodes.add(url.netloc)

    def replace_chain(self):
        network = self.nodes
        longest_chain = None
        max_length = self.length

        for node in network:
            response = requests.get(f"http://{node}/get_chain")

            if response.ok:
                data = response.json()
                if data["length"] > max_length and self.is_chain_valid(data["chain"]):
                    max_length = data["length"]
                    longest_chain = data["chain"]

        if longest_chain:
            self.chain = longest_chain
            return True
        return False

    def _validate_block_hash(self, prev, cur):
        return cur["previous_hash"] == self.hash(prev)

    def _validate_block_proof(self, prev, cur):
        previous_proof = prev["proof"]
        proof = cur["proof"]
        hash_operation = self.calculate_hash_operation(proof, previous_proof)
        return hash_operation[:4] == "0000"


# ! /usr/bin/env python

import json
import smbus

from time import sleep

class sonarSet:
    def __init__(self, config, status, robot, queue = None, bus = None, addr = None):
        self.config = config
        self.status = status
        self.robot = robot
        self.queue = queue

        self.bus = bus
        if bus is None:
            self.bus = int(config["ARDUINO_SONAR_SET_BUS"])

        self.addr = addr
        if addr is None:
            self.addr = int(config["ARDUINO_SONAR_SET_ADDR"], 16)

        self.i2c_interface = smbus.SMBus(self.bus)

    def _get_data(self, last_distance):
        try:
            sonar_data_from_arduino = self.i2c_interface.read_i2c_block_data(self.addr, 0, 32)
            sonar_data = ''
            for i in range(len(sonar_data_from_arduino)):
                sonar_data += chr(sonar_data_from_arduino[i])

            return str(sonar_data)
        except:
            if last_distance is not None:
                return '{},00.00,00.00,00.00, '.format(last_distance)

    def worker(self):
        status = self.status
        log_colission = self.config["LOG_COLISSION"].lower() in ('true', '1', 't')
        limit = int(self.config["LIMIT_COLISSION_IN_CM"])

        last_sonar_data = None
        sonar_keys = ("center", "centerRight", "back", "centerLeft")

        while True:
            try:
                sonar_data = self._get_data(last_sonar_data)
                sonar_data =  sonar_data.split(",")
                del(sonar_data[len(sonar_data)-1])

                sonar_data = dict(zip(sonar_keys, sonar_data))

                if float(sonar_data["center"]) <= limit and not status.colission_detected and status.direction == "Front":
                    self.robot.stop()
                    status.colission_detected = True
                    if log_colission:
                        status.colission_string = 'STOP CRASH {}'.format(sonar_data["center"])

                elif status.colission_detected and status.direction != "Front":
                    status.colission_detected = False
                    if log_colission:
                        status.colission_string ='RETURN {}'.format(sonar_data["center"])

                if self.queue is not None:
                    key = "distance_log"
                    msg = {
                        "type": "distanceLog",
                        "contents": sonar_data
                    }
                    self.queue.set(key, json.dumps(msg))

                if self.status is not None:
                    self.status.sonar_data = sonar_data

            except:
                #print("sonar failure")
                sonar_data = last_sonar_data

            sleep(0.10)


from CodicePython.View.VistaDonazioni import VistaDonazioni
from CodicePython.View.VistaPagamenti import VistaPagamenti
from CodicePython.View.VistaRapportini import VistaRapportini
from CodicePython.View.VistaVisualizzaTurni import VistaVisualizzaTurni
from PyQt5.QtWidgets import QWidget, QGridLayout, QSizePolicy, QPushButton

class VistaAmministratore(QWidget):
    def __init__(self, parent=None):
        super(VistaAmministratore, self).__init__(parent)
        grid_layout = QGridLayout()
        grid_layout.addWidget(self.get_generic_button("Gestione Donazioni", self.go_handle_donazioni), 1, 0)
        grid_layout.addWidget(self.get_generic_button("Gestione Rapportini", self.go_handle_rapportini), 1, 1)
        grid_layout.addWidget(self.get_generic_button("Gestione Pagamenti", self.go_handle_payments), 0, 1)
        grid_layout.addWidget(self.get_generic_button("Gestione Turni", self.go_turni), 0, 0)
        self.setLayout(grid_layout)
        self.resize(400, 300)
        self.setWindowTitle("Amministratore")

    def get_generic_button(self, titolo, on_click):
        button = QPushButton(titolo)
        button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        button.clicked.connect(on_click)
        return button

    def go_handle_donazioni(self):
        self.vista_donazioni = VistaDonazioni()
        self.vista_donazioni.show()

    def go_handle_rapportini(self):
        self.vista_rapportini = VistaRapportini()
        self.vista_rapportini.show()

    def go_handle_payments(self):
        self.vista_pagamenti = VistaPagamenti()
        self.vista_pagamenti.show()

    def go_turni(self):
        self.vista_turni = VistaVisualizzaTurni()
        self.vista_turni.show()


import csv
import requests
from bs4 import BeautifulSoup as BS
from time import sleep
from random import randrange
import json
from selenium import webdriver
import selenium.common.exceptions as exc
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium_stealth import stealth

options = webdriver.ChromeOptions()
options.add_argument("start-maximized")

# options.add_argument("--headless")

options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options)

stealth(driver,
        languages=["en-US", "en"],
        vendor="Google Inc.",
        platform="Win32",
        webgl_vendor="Intel Inc.",
        renderer="Intel Iris OpenGL Engine",
        fix_hairline=True,
        )

headers = {
    'Accept': '*/*',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0'
}

def get_artictles_urls(url):
    with requests.Session() as session:
        response = session.get(url=url, headers=headers)

        with open('index.html', 'w', encoding='utf-8') as file:
            file.write(response.text)

        articles_urls_list = []

    with requests.Session() as session:
        page = 0
        while True:
            page += 1
            response = session.get(f'https://announcements.bybit.com/en-US/?category=&page={page}', headers=headers)
            soup = BS(response.text, 'lxml')
            articles_urls = soup.find_all('a', class_='no-style')
            if (len(articles_urls)):
                for au in articles_urls:
                    art_url = au.get('href')
                    articles_urls_list.append(str(art_url))
            else:
                break

            # time.sleep(randrange(2, 5))

            print(f'Обработал {page}')

    with open('articles_urls.txt', 'w', encoding='utf-8') as file:
        for url in articles_urls_list:
            file.write(f'https://announcements.bybit.com{url}\n')

    print('Обработка законченна')
    return True

def get_data(file_path):
    with open(file_path) as file:
        urls_list = [line.strip() for line in file.readlines()]

    for url in urls_list:
        to_write = []
        driver.get(url)
        article_title = driver.find_element(By.CLASS_NAME, 'article-detail-title').text
        article_time = driver.find_element(By.CLASS_NAME, 'article-detail-date').text
        to_write.append(url)
        to_write.append(article_title)
        to_write.append(article_time)
        with open('data.csv', 'a', encoding='utf-8') as file:
            writer = csv.writer(file)
            writer.writerow(to_write)

def main():
    '''Получение всех ссылок(1 раз)'''

    #get_artictles_urls(url='https://announcements.bybit.com/en-US/?category=&page=1')

    '''Получение информации со всех ссылок'''

    if get_artictles_urls(url='https://announcements.bybit.com/en-US/?category=&page=1') is True:
        get_data('articles_urls.txt')

    '''Постоянный парсинг'''

    try:
        while True:
            if uniq_check(check()) is False:
                append_to_list(check())
                print('Добавлена новость')

            sleep(1)

    except Exception as ex:
        print(f'Что-то пошло не так {ex}')

def check():
    with requests.Session() as session:
        response = session.get(f'https://announcements.bybit.com/en-US/?category=&page=1', headers=headers)
        soup = BS(response.text, 'lxml')
        article_urls = soup.find('a', class_='no-style')
        art_url = article_urls.get('href')
        return 'https://announcements.bybit.com' + art_url

def uniq_check(url):
    with open('articles_urls.txt', 'r', encoding='utf-8') as file:
        for line in file:
            if url in line:
                return True
        else:
            return False

def append_to_list(url):
    with open('articles_urls.txt', 'a', encoding='utf-8') as file:
        file.write(f'https://announcements.bybit.com{url}\n')
    to_write = []
    driver.get(url)
    article_title = driver.find_element(By.CLASS_NAME, 'article-detail-title').text
    article_time = driver.find_element(By.CLASS_NAME, 'article-detail-date').text
    to_write.append(url)
    to_write.append(article_title)
    to_write.append(article_time)
    with open('data.csv', 'a', encoding='utf-8') as file:
        writer = csv.writer(file)
        writer.writerow(to_write)

if __name__ == '__main__':
    main()
    driver.quit()


aa = input()
aaa = input().split(" ")
a = []
for i in range(len(aaa)):
    a.append(int(aaa[i]))
list.sort(a)
num = 0
four = 0
three = 0
two = 0
one = 0
for i in range(len(a)):
    if a[i] == 4:
        four = four + 1
    elif a[i] == 3:
        three = three + 1
    elif a[i] == 2:
        two = two + 1
    elif a[i] == 1:
        one = one + 1
num = num + four
if three > one:
    num = num + three
    one = 0
else:
    num = num + three
    one = one - three
if one != 0:
    num = num + one // 4
    one = one - 4 * (one // 4)
if two % 2 == 0:
    num = num + two / 2
    if one != 0:
        num = num + 1
else:
    num = num + two // 2
    if one > 2:
        num = num + 2
    else:
        num = num + 1
print(int(num))


import datetime
import typing as t
from dateutil.parser import isoparse as dateutil_isoparse, parse as dateutil_parse
from nr.parsing.date import format_set, ISO_8601
from test.samples import SAMPLES

STDLIB_ISO_8601_DATETIME_FORMATS = [
  '%Y-%m-%dT%H:%M:%S.%f%z',
  '%Y-%m-%dT%H:%M:%S.%f',
  '%Y-%m-%dT%H:%M:%S%z',
  '%Y-%m-%dT%H:%M:%S',
  '%Y%m%dT%H%M%S%z',
  '%Y%m%dT%H%M%S',
  '%Y%m%d',
]

STDLIB_ISO_8601_DATETIME_FORMATS_REVERSED = list(reversed(STDLIB_ISO_8601_DATETIME_FORMATS))

ISO8601_DATETIME_SAMPLES = SAMPLES[SAMPLES.tags.apply(lambda x: 'iso8601' in x) &
  SAMPLES.parsed.apply(lambda x: isinstance(x, datetime.datetime))]

def vectorize(func, *args):
  for _idx, row in ISO8601_DATETIME_SAMPLES.iterrows():
    func(row.formatted, row.parsed, *args)

def parse_datetime_stdlib(s: str, expected: datetime.datetime, formats: t.List[str]) -> None:
  for fmt in formats:
    try:
      parsed = datetime.datetime.strptime(s, fmt)
      break
    except ValueError:
      pass
  else:
    raise ValueError(f'Unable to parse {s!r} using {formats!r}')
  assert parsed == expected

def parse_datetime_nr(s: str, expected: datetime.datetime, format: format_set) -> None:
  parsed = format.parse_datetime(s, True)
  assert parsed == expected

def parse_dateutil_parse(s: str, expected: datetime.datetime) -> None:
  parsed = dateutil_parse(s)
  assert parsed == expected

def parse_dateutil_isoparse(s: str, expected: datetime.datetime) -> None:
  parsed = dateutil_isoparse(s)
  assert parsed == expected

class DatetimeParsingSuite:
  """
  An example benchmark that times the performance of various kinds
  of iterating over dictionaries in Python.
  """

  def time_datetime_datetime_strptime(self):
    vectorize(parse_datetime_stdlib, STDLIB_ISO_8601_DATETIME_FORMATS)

  def time_datetime_datetime_strptime_reversed(self):
    vectorize(parse_datetime_stdlib, STDLIB_ISO_8601_DATETIME_FORMATS)

  def time_nr_parsing_date_ISO_8601_parse_datetime(self):
    vectorize(parse_datetime_nr, ISO_8601)

  def time_dateutil_parser_parse(self):
    vectorize(parse_dateutil_parse)

  def time_dateutil_parser_isoparse(self):
    vectorize(parse_dateutil_isoparse)