import matplotlib as mpl
from matplotlib.colors import LightSource
from matplotlib.collections import PolyCollection
import matplotlib.gridspec as gridspec
import matplotlib.patches as patches
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
import numpy as np
import pandas as pd

from .tnautoregression import TNToolsAutoregression
from .tninference import TNToolsSimilarities

new_rc_params = {'text.usetex': False,
                 'svg.fonttype': 'none',
                 'lines.linewidth': 1}

mpl.rcParams.update(new_rc_params)

class TNToolsPlot:
    """
    This class is part of the toolset for usage of Twin Network (TN)
    on evaluation purposes.
    This part of the toolset is used to plot embedding similarities.
    """

    def __init__(self):
        self.plot_cmap = mpl.cm.get_cmap('viridis')
        self.plot_line_alpha_fg = 1
        self.plot_line_colors = ["#fde725",
                                 "#35b779",
                                 "#31688e",
                                 "#1f4696",
                                 "#ed1c24",
                                 '#6abd45'
                                 ]
        self.plot_line_colors_difference = ["#25858e",
                                            "#482173"]
        self.plot_line_width = 1

        self.plot_fig_dpi = 300
        self.plot_fig_size = (5, 3)
        self.plot_fig_size_square = (4, 4)

        self.plot_fontsize_large = 8
        self.plot_fontsize_small = 6

        self.plot_marker_size = self.plot_line_width * 5
        self.plot_marker_size_mean = self.plot_line_width * 8

        self.plot_text_weight = 'ultralight'

        self.tools_autoregression = TNToolsAutoregression()
        self.tools_similarities = TNToolsSimilarities()

    def fn_similarity_self_cmap_custom(self, **kwargs):
        """
        Creates a colormap based on predefined
        'matplotlib' colormaps. Colors are adjusted
        such that color changes occur mainly in
        upper range of values.
        """
        samples = self.fn_similarity_self_cmap_custom_samples_exponential(
            **kwargs)
        list_colors = [self.plot_cmap(s) for s in samples]
        lscmap = mpl.colors.LinearSegmentedColormap.from_list("cmap_custom",
                                                              list_colors)
        return lscmap

    @staticmethod
    def fn_similarity_self_cmap_custom_samples_exponential(lamb=0.2,
                                                           **kwargs):
        """
        Generates 100 samples based on a
        decaying exponential distribution.
        """
        n_samples = 100
        xs = np.linspace(0, n_samples, n_samples)
        ys = np.exp(lamb * xs)
        ys = (ys - np.min(ys)) / (np.max(ys) - np.min(ys))

        grads = np.gradient(ys)
        tp_grad = next(grad for grad in grads if grad >= 1 / n_samples)
        x_tp_grad = np.where(np.isclose(grads, tp_grad))

        if kwargs.get("verbose", False):
            print(f"Index of change: {x_tp_grad[0] / n_samples}")

        return ys

    def plot_distribution_similarities(self,
                                       values_similarities,
                                       colors,
                                       labels,
                                       **kwargs):
        """Plot count of similarities as histogram."""
        bins = np.linspace(0, 1, 1000)
        plot_x_mins = list()
        plot_y_maxs = list()

        fig, axs = plt.subplots(dpi=self.plot_fig_dpi,
                                figsize=self.plot_fig_size)

        for i in range(len(values_similarities)):
            b = axs.hist(values_similarities[i],
                         bins=bins,
                         color=colors[i],
                         label=labels[i])
            # Append x position of the lowest bin to list
            for val_y in b[0]:
                if val_y != .0:
                    ind_x_min = list(b[0]).index(val_y)
                    break
            plot_x_mins.append(b[1][ind_x_min])
            # Append max. y-values to list
            plot_y_maxs.append(max(b[0]))

        plt.tick_params(axis='both',
                        which='both',
                        labelsize=self.plot_fontsize_small)

        plt.xlabel('Similarity values',
                   fontsize=self.plot_fontsize_large)
        plt.xlim(left=np.min(plot_x_mins) * 0.9,
                 right=1.01)

        plt.ylim(bottom=0,
                 top=np.max(plot_y_maxs) * 1.1)
        axs.set_xlabel('Similarity values',
                       fontsize=self.plot_fontsize_large)
        axs.set_ylabel('Density',
                       fontsize=self.plot_fontsize_large)
        plt.legend(fontsize=self.plot_fontsize_small)

        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'])
        plt.show()
        plt.close(fig)

    def plot_distribution_similarities_multiple(self,
                                                similarities,
                                                **kwargs):
        """
        Plot the distributions of the similarities at multiple indices
        of test image series.
        """
        # We have to un-nest the similarities for each timepoint first, as
        # the similarities are stored separately
        similarities_mean = [np.mean(_v_sim) for _v_sim in
                             similarities.values()]
        similarities_widths = [max(_v_sim) - min(_v_sim) for _v_sim in
                               similarities.values()]

        num_timepoints_reference = len(similarities_mean)

        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_colors = ["#431c53", '#1f4497']

        plot_x_ticks = list(range(0, num_timepoints_reference, 50))

        fig, axs = plt.subplots(1,
                                dpi=self.plot_fig_dpi,
                                figsize=self.plot_fig_size)
        axs.plot(range(len(similarities_mean)),
                 similarities_mean,
                 color=plot_colors[0],
                 linewidth=self.plot_line_width,
                 label="Similarity mean")
        axs.set_ylabel('Average cosine similarity φ',
                       fontsize=self.plot_fontsize_large)
        axs.set_xlabel('Acquisition time (min)',
                       fontsize=self.plot_fontsize_large)
        axs.tick_params(axis='both',
                        which='both',
                        labelsize=self.plot_fontsize_small)
        axs.set_xlim(left=0,
                     right=num_timepoints_reference)
        axs.set_xticks(plot_x_ticks,
                       [str(x * plot_atp_min_correction)
                        for x in plot_x_ticks],
                       fontsize=self.plot_fontsize_small)

        axs2 = plt.twinx()
        axs2.plot(range(len(similarities_mean)),
                  similarities_widths,
                  color=plot_colors[1],
                  linewidth=self.plot_line_width,
                  label='Similarity range')
        axs2.grid(False)
        axs2.tick_params(axis='both',
                         which='both',
                         labelsize=self.plot_fontsize_small)
        axs2.set_ylabel('Similarity distribution range',
                        fontsize=self.plot_fontsize_large)
        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'])
        plt.show()
        plt.close()

    def plot_distribution_stages_dev_fractions(self,
                                               values_histograms,
                                               num_timepoints_reference,
                                               colors,
                                               labels,
                                               **kwargs):
        """
        Plot a histogram to show where the
        predictions for an acquisition timepoint lie.
        """
        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_x_ticks = list(range(0, num_timepoints_reference, 50))
        bins = list(range(0, num_timepoints_reference))

        fig, ax_hist = plt.subplots(dpi=self.plot_fig_dpi,
                                    figsize=self.plot_fig_size)
        for i in range(len(values_histograms)):
            alpha = np.sqrt(self.plot_line_alpha_fg / len(values_histograms))
            counts, bins = np.histogram(values_histograms[i], bins=bins)
            plt.hist(bins[:-1], bins,
                     alpha=self.plot_line_alpha_fg,
                     color=colors[i] + (alpha,),
                     label=labels[i],
                     weights=[c / sum(counts) * 100 for c in counts])

        ax_hist.set_ylabel('Percentage of predictions',
                           fontsize=self.plot_fontsize_large)
        ax_hist.set_xlabel('Predicted reference developmental time (min)',
                           fontsize=self.plot_fontsize_large)
        ax_hist.tick_params(axis='both',
                            which='both',
                            labelsize=self.plot_fontsize_small)
        ax_hist.set_xlim(left=0, right=num_timepoints_reference)
        ax_hist.set_xticks(plot_x_ticks,
                           [str(x * plot_atp_min_correction)
                            for x in plot_x_ticks],
                           fontsize=self.plot_fontsize_small)
        plt.legend()
        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'])
        plt.show()

        plt.close()

    def plot_similarity_self_2d(self, similarities, **kwargs):
        """
        Plot similarities of images from a time-series image sequence
        of an embryo to earlier images from the same series as
        two-dimensional matrix.

        Parameters
        ----------
        similarities: list
            List of lists with similarity values. Each sublist contains
            similarity values between one test image and all earlier
            images in the test sequence. Thus, the number of similarities
            must be number of time points in the acquisition - 1.
        **kwargs: Variable keyword arguments
            See below.

        Keyword Arguments
        -----------------
        intervals: int/float
            Time intervals (min), at which test embryo images
            were acquired. Default is 4 minutes. Use this
            to adjust axis labels to be labelled with minutes.
        """
        num_imgs_test = len(similarities) + 1
        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_xy_ticks = list(range(0, num_imgs_test, 50))

        if kwargs.get("color_adjust", False):
            plot_colors_hlf = self.fn_similarity_self_cmap_custom(**kwargs)
        else:
            plot_colors_hlf = self.plot_cmap

        xs_grid, ys_grid, zs_grid_plot = self.tools_autoregression.\
            fn_2d_sims_to_arrays(
                similarities,
                num_imgs_test,
                square=True
            )

        # Figure
        f, ax = plt.subplots(dpi=self.plot_fig_dpi,
                             figsize=self.plot_fig_size_square)
        f.tight_layout(rect=[0.0, 0.00, 0.8, 1.0])

        # Plot
        ax.imshow(zs_grid_plot,
                  cmap=plot_colors_hlf)

        # Labels and titles
        ax.tick_params(axis='both',
                       which='both',
                       labelsize=self.plot_fontsize_small)
        ax.set_xlabel('Time (min)',
                      fontsize=self.plot_fontsize_large)
        ax.set_ylabel('Time (min)',
                      fontsize=self.plot_fontsize_large)
        ax.set_xlim(left=0,
                    right=len(similarities))
        ax.set_ylim(bottom=0,
                    top=len(similarities))
        ax.set_xticks(plot_xy_ticks,
                      [str(a * plot_atp_min_correction)
                       for a in plot_xy_ticks],
                      fontsize=self.plot_fontsize_small)
        ax.set_yticks(plot_xy_ticks,
                      [str(a * plot_atp_min_correction)
                       for a in plot_xy_ticks],
                      fontsize=self.plot_fontsize_small)

        divider = make_axes_locatable(ax)
        sm = plt.cm.ScalarMappable(cmap=plot_colors_hlf)
        cax = divider.append_axes("right",
                                  size="5%",
                                  pad=0.05)
        cax.tick_params(axis='both',
                        which='both',
                        labelsize=self.plot_fontsize_small)
        cbar = f.colorbar(sm,
                          cmap=plot_colors_hlf,
                          cax=cax)
        cbar.ax.set_ylabel('Cosine similarity ϕ',
                           fontsize=self.plot_fontsize_large)

        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'])
        else:
            plt.show()
        plt.close()

    def plot_similarity_self_3d(self,
                                similarities,
                                **kwargs):
        """
        Plot similarities of images from a time-series image sequence
        of an embryo to earlier images from the same series as
        three-dimensional surface plot.

        Parameters
        ----------
        similarities: list
            List of lists with similarity values. Each sublist contains
            similarity values between one test image and all earlier
            images in the test sequence. Thus, the number of similarities
            must be number of time points in the acquisition - 1.
        **kwargs: Variable keyword arguments
            See below.

        Keyword Arguments
        -----------------
        intervals: int/float
            Time intervals (min), at which test embryos
            were acquired. Default is 4 minutes. Use this
            to adjust axis labels to minute.
        plot_index: int
            Index of the last image that should be included in the plot.
            We assume 1-based indexing for indexing of the images. Thus,
            the lowest image index that can be given is 2, as the first
            comparison between two images is between images at indices
            2 and 1.
        rotation_angle: int
            Rotation of plot along z-axis.
        """
        # Number of images in the dataset is by 1 lager
        # than number of similarities
        num_imgs_test = len(similarities) + 1
        # This is the index of the image until which similarities
        # should be plotted.
        plot_index = kwargs.get("plot_index", len(similarities))

        print(f"[INFO] Number of similarity lists: {len(similarities)}")

        assert num_imgs_test >= plot_index >= 2, \
            f"Plot index is wrong." \
            f"\nLowest plot index is 2, " \
            f"as first comparison is image nr. 2 vs. image nr. 1. " \
            f"\nHighest plot index is {num_imgs_test}, " \
            f"as this is the number of images in the dataset."

        # Plot parameters
        plot_alpha_polygon = 0.5
        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_bottom = 0.0
        plot_color_line_marker = self.plot_line_colors[5]
        plot_color_polygon = plot_color_line_marker
        plot_grid_linewidth = 0.5
        plot_tick_pad = -2
        plot_xy_ticks = list(range(0, num_imgs_test, 50))

        mpl.rcParams.update({'axes.linewidth': plot_grid_linewidth,
                             'grid.linewidth': plot_grid_linewidth,
                             'xtick.major.width': plot_grid_linewidth,
                             'xtick.major.pad': plot_tick_pad,
                             'ytick.major.width': plot_grid_linewidth,
                             'ytick.major.pad': plot_tick_pad})

        # We used 1-indexing for the image indices and the number of
        # similarities is supposed to be 1 smaller than the number of
        # images. Thus, we can slice the array of similarities until
        # plot_index - 1 (will be excluded from slice). As a result,
        # the last list of similarities shown here corresponds to
        # similarities[plot_index - 2], which can be used to plot a
        # line at the edge.
        xs_grid, ys_grid, zs_grid_plot, zs_grid_colors = \
            self.tools_autoregression.fn_3d_sims_to_arrays(
                similarities[:plot_index - 1],
                num_imgs_test
            )

        # Figure
        fig = plt.figure(dpi=self.plot_fig_dpi,
                         figsize=self.plot_fig_size)
        spec = gridspec.GridSpec(figure=fig,
                                 ncols=2,
                                 nrows=1,
                                 width_ratios=[1, 0.05])
        ax = fig.add_subplot(spec[0, 0],
                             projection='3d')
        cax = fig.add_subplot(spec[0, 1])

        scatter = ax.plot_surface(xs_grid, ys_grid, zs_grid_plot,
                                  cmap=self.plot_cmap,
                                  edgecolors=(1., 1., 1., 1.),
                                  alpha=1.0,
                                  linewidth=0,
                                  antialiased=False,
                                  shade=False)

        # Set axis range
        xlim_max = ax.get_xlim()[1]
        ylim_min = ax.get_ylim()[0]
        zlim_min = -0.017

        if kwargs.get("plot_line", False):
            line_x, line_y, line_z = \
                self.tools_autoregression.fn_3d_sims_line_plot(
                    similarities,
                    plot_index
                )

            # Line plots
            ax.plot(line_x, line_y, line_z,
                    color=plot_color_line_marker,
                    linewidth=self.plot_line_width,
                    zorder=10)

            vertices = [self.tools_autoregression.fn_polygon_under_graph(
                line_y,
                line_z,
                zlim_min
            )]

            poly = PolyCollection(
                vertices,
                facecolors=[plot_color_polygon]*len(vertices),
                alpha=plot_alpha_polygon
            )

            ax.add_collection3d(poly,
                                zs=[plot_index],
                                zdir='x')

            # Line orthogonal to x-axis
            ax.plot([plot_index, plot_index],
                    [ylim_min, plot_index],
                    [zlim_min, zlim_min],
                    color=plot_color_line_marker,
                    linewidth=self.plot_line_width)
            # Line orthogonal to y-axis
            ax.plot([plot_index, xlim_max],
                    [plot_index, plot_index],
                    [zlim_min, zlim_min],
                    color=plot_color_line_marker,
                    linewidth=self.plot_line_width)
            # Line parallel to z-axis
            ax.plot([plot_index, plot_index],
                    [plot_index, plot_index],
                    [zlim_min, similarities[plot_index - 2][-1]],
                    color=plot_color_line_marker,
                    linewidth=self.plot_line_width)

        # Labels and titles
        ax.tick_params(axis='both',
                       which='both',
                       labelsize=self.plot_fontsize_small)
        ax.set_xlabel('Time (min)',
                      fontsize=self.plot_fontsize_large)
        ax.set_ylabel('Time (min)',
                      fontsize=self.plot_fontsize_large)
        ax.set_zlabel('Cosine similarity ϕ',
                      fontsize=self.plot_fontsize_large)
        ax.set_xlim3d(left=0,
                      right=len(similarities))
        ax.set_xticks(plot_xy_ticks,
                      [str(a * plot_atp_min_correction)
                       for a in plot_xy_ticks],
                      fontsize=self.plot_fontsize_small)
        ax.set_yticks(plot_xy_ticks,
                      [str(a * plot_atp_min_correction)
                       for a in plot_xy_ticks],
                      fontsize=self.plot_fontsize_small)
        ax.set_ylim3d(bottom=0,
                      top=len(similarities))
        ax.set_zlim3d(bottom=plot_bottom,
                      top=1.0)

        # Colorbar
        scatter.set_clim(0.0, 1.0)
        cax.set_ylim(bottom=0.0, top=1.0)
        cax.set_yticks(list(np.arange(0, 1, 0.1)),
                       list(np.arange(0, 1, 0.1)),
                       fontsize=self.plot_fontsize_small)
        cax.tick_params(pad=-plot_tick_pad)
        cbar = plt.colorbar(scatter,
                            cax=cax,
                            format='%.1f',
                            fraction=0.05,
                            )
        cbar.set_label(label="Cosine similarity ϕ",
                       size=self.plot_fontsize_large,)
        cax.set_aspect(10)

        if isinstance(kwargs.get('rotation_angle', None), int):
            ax.view_init(
                25,
                int(kwargs.get('rotation_angle', -60))
            )

        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'])
        else:
            plt.show()
        plt.close()

    def plot_similarity_curve(self, path_df, path_save=None, **kwargs):
        """
        Plot mean of calculated similarities of images
        from dataframe.
        """
        # Dataframe
        if isinstance(path_df, str):
            path_df = pd.read_csv(path_df)
        df = path_df
        df_mean = df.mean(axis=1)
        df_similarity_high = df.loc[df['Anch_sim_1'] > 0.8]
        num_timepoints_reference = len(df)

        # Plot parameters
        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_label_x = f'Time (min)'
        plot_label_y = "Cosine similarity φ"
        plot_xs = range(1, num_timepoints_reference + 1)
        plot_x_ticks = list(range(0, num_timepoints_reference, 50))

        # Figure
        fig, (ax_hist, ax_box) = plt.subplots(
            2,
            dpi=self.plot_fig_dpi,
            figsize=self.plot_fig_size,
            gridspec_kw={"height_ratios": (.85, .15)},
            sharex=True
        )

        # Plot similarities
        ax_hist.scatter(plot_xs,
                        df_mean,
                        alpha=self.plot_line_alpha_fg,
                        c=self.plot_line_colors[2],
                        label='Mean',
                        marker='o',
                        linewidth=0,
                        s=self.plot_marker_size)

        ax_hist.set_xlim(left=0,
                         right=num_timepoints_reference)
        ax_hist.set_ylabel(plot_label_y,
                           fontsize=self.plot_fontsize_large,
                           weight=self.plot_text_weight)
        ax_hist.set_ylim(0, 1)
        ax_hist.tick_params(axis='both',
                            labelsize=self.plot_fontsize_small)
        ax_hist.set_xticks(plot_x_ticks,
                           [str(x * plot_atp_min_correction)
                            for x in plot_x_ticks],
                           fontsize=self.plot_fontsize_small)

        ax_hist.tick_params(axis='x',
                            which='both',
                            bottom=False,
                            top=False,
                            labelbottom=False)
        plt.setp(ax_hist.get_xticklabels(), visible=False)

        # Plot boxplot
        ax_box.scatter(df_similarity_high['Anch_sim_1'].idxmax(),
                       [0],  # y-value for plot in boxplot axis
                       c='r',
                       s=self.plot_marker_size_mean)
        ax_box.set_xlabel(plot_label_x,
                          fontsize=self.plot_fontsize_large,
                          weight=self.plot_text_weight)
        boxplot = ax_box.boxplot(
            x=df_similarity_high.index.values,
            boxprops={'facecolor': self.plot_line_colors[2],
                      'linewidth': self.plot_line_width},
            capprops={'linewidth': self.plot_line_width},
            flierprops={'marker': '.',
                        'markersize': self.plot_marker_size},
            medianprops={'color': (0, 0, 0),
                         'linewidth': self.plot_line_width},
            patch_artist=True,
            positions=[0],
            vert=False,
            widths=0.5,
            whiskerprops={'linewidth': self.plot_line_width},
            zorder=0)
        ax_box.tick_params(axis='both', labelsize=self.plot_fontsize_small)
        ax_box.set_yticks([])
        ax_box.linewidth = 0.1

        # Quartile highlight
        q25_mean = boxplot["whiskers"][0].get_path().get_extents().x1
        median_mean = boxplot["medians"][0].get_path().get_extents().x1
        q75_mean = boxplot["whiskers"][1].get_path().get_extents().x0

        ax_hist.add_patch(
            patches.Rectangle((q25_mean, 0),
                              q75_mean - q25_mean,
                              1,
                              facecolor=self.plot_line_colors[2],
                              alpha=0.1))
        ax_hist.plot([median_mean, median_mean],
                     [0, 1],
                     color=self.plot_line_colors[2],
                     linewidth=self.plot_line_width,
                     alpha=0.4)

        ax_box.add_patch(
            patches.Rectangle((q25_mean, -0.5),
                              q75_mean - q25_mean,
                              1,
                              facecolor=self.plot_line_colors[2],
                              alpha=0.1))
        ax_box.plot([median_mean, median_mean],
                    [-0.5, 0.5],
                    color=self.plot_line_colors[2],
                    linewidth=self.plot_line_width,
                    alpha=0.4)

        # Formatting
        plt.subplots_adjust(hspace=0)

        if path_save is not None:
            plt.savefig(path_save, dpi=fig.dpi)
        plt.show()
        plt.close(fig)

    def plot_similarity_maxima(self, df_sims, **kwargs):
        """
        Plot maxima of similarity sequence.

        Parameters
        ----------
        df_sims: pandas.core.frame.DataFrame
        **kwargs: See below

        Keyword Arguments
        -----------------
        intervals_x: int
            Time intervals (min), at which reference embryos
            were acquired. Default is 4 minutes.
        intervals_y: int
            Time intervals (min), at which reference embryos
            were acquired. Default, corresponding to default
            set of reference embryos, is 4 minutes.
        """

        num_timepoints_query = df_sims.shape[0]
        num_timepoints_reference = df_sims.shape[1]

        plot_atp_min_correction_x = int(kwargs.get('intervals_x', 4))
        plot_atp_min_correction_y = int(kwargs.get('intervals_y', 4))
        plot_axis = 1
        plot_label_x = "Query embryo time (min)"
        plot_label_y = "Reference embryo time (min)"

        # Make axes equal length
        # Calculate durations of data sets
        duration_query = num_timepoints_query * \
                         plot_atp_min_correction_x
        duration_reference = num_timepoints_reference * \
                             plot_atp_min_correction_y
        # Get longer duration
        plot_range = max(duration_query, duration_reference)

        # Make tick labels based on timepoints
        plot_tick_labels = [int(i) for i in list(range(0, plot_range, 200))]

        # Divide timepoints by acquisition time intervals
        # to get indices for plotting
        plot_xs = range(1, num_timepoints_query + 1)
        plot_x_ticks = [plot_tick_label / plot_atp_min_correction_x
                        for plot_tick_label in plot_tick_labels]
        plot_y_ticks = [plot_tick_label / plot_atp_min_correction_y
                        for plot_tick_label in plot_tick_labels]

        plot_maxima = df_sims.idxmax(axis=plot_axis)
        plot_true_values = kwargs.get("true_values", [])

        # Plot
        fig, axs = plt.subplots(dpi=self.plot_fig_dpi,
                                figsize=self.plot_fig_size_square)

        axs.scatter(plot_xs,
                    plot_maxima,
                    alpha=self.plot_line_alpha_fg,
                    c=self.plot_line_colors[3],
                    label='Mean',
                    marker='o',
                    linewidth=0,
                    s=self.plot_marker_size)
        axs.plot(plot_true_values,
                 c=self.plot_line_colors[4],
                 label='Theoretical true value',
                 lw=self.plot_line_width)

        axs.set_xlabel(plot_label_x,
                       fontsize=self.plot_fontsize_large)
        axs.set_xlim(0, int(plot_range / plot_atp_min_correction_x))
        axs.set_xticks(plot_x_ticks,
                       plot_tick_labels,
                       fontsize=self.plot_fontsize_small)

        axs.set_ylabel(plot_label_y,
                       fontsize=self.plot_fontsize_large)
        axs.set_ylim(0, int(plot_range / plot_atp_min_correction_y))
        axs.set_yticks(plot_y_ticks,
                       plot_tick_labels,
                       fontsize=self.plot_fontsize_small)

        if 'path_save' in kwargs:
            plt.savefig(kwargs['path_save'], dpi=self.plot_fig_dpi)
        plt.show()
        plt.close(fig)

    def plot_similarity_differences(self, df_earlier, df_later, **kwargs):
        """
        Plot differences in calculated similarities
        of two subsequently acquired images.
        """
        # Calculate similarity differences
        df_earlier_mean, df_later_mean, df_diff = \
            self.tools_similarities.fn_calculate_similarity_differences(
                df_earlier, df_later
            )

        num_timepoints_reference = len(df_earlier_mean)
        plot_atp_min_correction = int(kwargs.get('intervals', 4))
        plot_xs = list(range(1, num_timepoints_reference + 1))
        plot_x_ticks = list(range(0, num_timepoints_reference, 50))

        fig, ax_hist = plt.subplots(1,
                                    dpi=self.plot_fig_dpi,
                                    figsize=self.plot_fig_size)

        ax_hist.scatter(plot_xs,
                        df_earlier_mean,
                        alpha=self.plot_line_alpha_fg,
                        c=self.plot_line_colors_difference[0],
                        label='Earlier',
                        marker='o',
                        linewidth=0,
                        s=self.plot_marker_size)
        ax_hist.scatter(plot_xs,
                        df_later_mean,
                        alpha=self.plot_line_alpha_fg,
                        c=self.plot_line_colors_difference[1],
                        label='Later',
                        marker='o',
                        linewidth=0,
                        s=self.plot_marker_size)
        ax_hist.scatter(plot_xs,
                        df_diff,
                        alpha=self.plot_line_alpha_fg,
                        c=self.plot_line_colors[3],
                        label='Difference',
                        marker='o',
                        linewidth=0,
                        s=self.plot_marker_size)
        ax_hist.set_ylabel('Cosine similarity φ',
                           fontsize=self.plot_fontsize_large)
        ax_hist.set_xlabel('Reference time (min)',
                           fontsize=self.plot_fontsize_large)
        ax_hist.tick_params(axis='both', which='both',
                            labelsize=self.plot_fontsize_small)
        ax_hist.set_xlim(left=0, right=num_timepoints_reference)
        ax_hist.set_xticks(plot_x_ticks,
                           [str(x * plot_atp_min_correction)
                            for x in plot_x_ticks],
                           fontsize=self.plot_fontsize_small)
        ax_hist.set_ylim(-0.2, 1)

        ax_hist.spines['left'].set_position('zero')
        ax_hist.spines['bottom'].set_position('zero')
        ax_hist.xaxis.set_ticks_position('bottom')
        ax_hist.yaxis.set_ticks_position('left')

        lgnd = ax_hist.legend()
        for lgndHandle in lgnd.legendHandles:
            lgndHandle._sizes = [30]

        if kwargs.get("path_save", None) is not None:
            plt.savefig(kwargs["path_save"], dpi=self.plot_fig_dpi)
        plt.show()
        plt.close(fig)

    def plot_similarity_zscores_inbatch(self, similarities, **kwargs):
        """
        Plot calculated similarities of multiple embryos in one batch
        for multiple image indices of these embryos.

        Parameters
        ----------
        similarities: pandas.core.frame.DataFrame
            A pandas DataFrame containing similarity values
            or zscores. Values should be sorted by embryo
            names (column names) and image indices (row indices).
        **kwargs: Variable keyword arguments
            See below.

        Keyword Arguments
        -----------------
        intervals_x: int/float
            Time intervals (min), at which test embryo
            images were acquired. Default is 4 minutes.
        """
        columns_embryos = list(similarities.columns)
        num_embryos = len(columns_embryos)
        num_indices = len(similarities.index)

        plot_atp_min_correction = kwargs.get("intervals_x", 2)
        plot_xs = list(range(1, num_indices + 1))
        plot_x_ticks = list(range(0, num_indices, 50))

        fig, ax = plt.subplots(1,
                               dpi=self.plot_fig_dpi,
                               figsize=self.plot_fig_size)

        for column_embryo in columns_embryos:
            color = self.plot_cmap(
                columns_embryos.index(column_embryo) / num_embryos)

            ax.scatter(plot_xs,
                       similarities[column_embryo],
                       alpha=self.plot_line_alpha_fg,
                       c=[color] * len(plot_xs),
                       label=column_embryo,
                       linewidth=self.plot_line_width,
                       marker='o',
                       s=self.plot_line_width
                       )

        ax.set_xlabel('Time (min)',
                      fontsize=self.plot_fontsize_large)
        ax.set_xticks(plot_x_ticks,
                      [str(x * plot_atp_min_correction)
                       for x in plot_x_ticks],
                      fontsize=self.plot_fontsize_small)
        ax.set_xlim(left=0,
                    right=num_indices)
        ax.set_ylabel('Cumulative z-score',
                      fontsize=self.plot_fontsize_large)
        ax.tick_params(axis='both',
                       which='both',
                       labelsize=self.plot_fontsize_small)
        ax.legend(fontsize=self.plot_fontsize_small,
                  ncol=2)
        if kwargs.get('path_save', None) is not None:
            _ = plt.savefig(kwargs['path_save'],
                            dpi=300)
        plt.show()
        plt.close(fig)


import os
import re
import sys
from setuptools import setup, find_packages

with open("README.md", "r") as f:
    long_description = f.read()

package = 'contentstack_management'

def get_version(package):
    """
    Return package version as listed in `__version__` in `init.py`.
    """
    init_py = open(os.path.join(package, '__init__.py')).read()
    return re.search("^__version__ = ['\"]([^'\"]+)['\"]",
                     init_py, re.MULTILINE).group(1)

def get_author_name(package):
    """
    Return package author as listed in `__author__` in `init.py`.
    """
    init_py = open(os.path.join(package, '__init__.py')).read()
    return re.search("^__author__ = ['\"]([^'\"]+)['\"]",
                     init_py, re.MULTILINE).group(1)

def get_author_email(package):
    """
    Return package email as listed in `__email__` in `init.py`.
    """
    init_py = open(os.path.join(package, '__init__.py')).read()
    return re.search("^__email__ = ['\"]([^'\"]+)['\"]",
                     init_py, re.MULTILINE).group(1)

setup(
    name="contentstack-management",
    version=get_version(package),
    packages=find_packages(exclude=['tests']),
    py_modules=['_api_client', 'contentstack','common','_errors','_constant'],
    description="Contentstack API Client Library for Python",
    long_description=long_description,
    long_description_content_type="text/markdown",
    url="https://github.com/contentstack/contentstack-management-python",
    author=get_author_name(package),
    author_email=get_author_email(package),
    license="MIT",
    classifiers=[
        "License :: OSI Approved :: MIT License",
        "Programming Language :: Python :: 3.10",
        "Operating System :: OS Independent",
    ],
    install_requires=["bson >= 0.5.9", "requests >= 2.5.4", "requests-toolbelt >= 0.3.1"],
    extras_require={
        "dev": ["pytest>=7.0", "twine>=4.0.2", "dotenv>=0.0.5"],
    },
    python_requires=">=3.9",
)


import calendar
import sys

def main():
    days = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
    poem = ["Monday's child is fair of face.", "Tuesday's child is full of grace.", "Wednesday's child is full of woe.",
            "Thursday's child has far to go.", "Friday's child is loving and giving.",
            "Saturday's child works hard for a living.", "Sunday's child is fair and wise and good in every way."]
    day = calendar.weekday(int(sys.argv[3]), int(sys.argv[2]), int(sys.argv[1]))
    print("You were born on a", days[day] + " and", poem[day])

if __name__ == '__main__':
    main()


from atom import Potential, RotatePoints, EnergyLevel, ElectronStates

class Atom:
	def __init__(self):
		self.xU   = Potential()
		self.e    = EnergyLevel()
		self.N    = ElectronStates()
		self.__RP = RotatePoints()

	def setV(self, V):
		self.xU.setV(V)
		self.e.setV(V)
		self.N.setV(V)
		self.__RP.setV(V)

	def setT(self, T):
		self.xU.setT(T)
		self.e.setT(T)
		self.N.setT(T)
		self.__RP.setT(T)

	def setZ(self, Z):
		self.xU.setZ(Z)
		self.e.setZ(Z)
		self.N.setZ(Z)
		self.__RP.setZ(Z)

	def setVTZ(self, V, T, Z):
		self.xU.setVTZ(V, T, Z)
		self.e.setVTZ(V, T, Z)
		self.N.setVTZ(V, T, Z)
		self.__RP.setVTZ(V, T, Z)

	def setTolerance(self, eps):
		self.xU.setTolerance(eps)
		self.e.setTolerance(eps)
		self.N.setTolerance(eps)
		self.__RP.setTolerance(eps)


"""Interactive plots for statistics over trajectories.

"""
import warnings

import panel as pn

from ad_sensitivity_analysis.interactive import create_widgets
from ad_sensitivity_analysis.plot import stats_per_traj_static

warnings.simplefilter(action="ignore", category=RuntimeWarning)

def plot_kde_histogram_interactive(ds):
    """
    Use plot_kde_histogram() interactively in a jupyter notebook.

    Parameters
    ----------
    ds : xarray.Dataset
        Dataset created via create_rank_traj_dataset().

    Returns
    -------
    panel.layout that can be used in a jupyter notebook.
    """
    out_param = create_widgets.create_out_params_select(ds)
    width_height_font_row = create_widgets.image_font_size_row(pixel=False)

    title_widget = create_widgets.create_title_widget()
    save_field_button_latex_row = create_widgets.create_save_latex_row()

    in_params = create_widgets.create_in_params_selector(ds)
    line_slider = create_widgets.create_line_slider()
    bw_slider = pn.widgets.FloatSlider(
        name="Change the bandwidth for the kde calculation",
        start=0.05,
        end=1.5,
        step=0.05,
        value=1.0,
    )
    flow_phase_ignore_row = create_widgets.create_flow_phase_ignore_row()
    common_toggle = pn.widgets.Toggle(
        name="Common norm",
        button_type="success",
    )

    plot_pane = pn.panel(
        pn.bind(
            stats_per_traj_static.plot_kde_histogram,
            ds=ds,
            in_params=in_params,
            out_param=out_param,
            linewidth=line_slider,
            bw_adjust=bw_slider,
            flow=flow_phase_ignore_row[0],
            phase=flow_phase_ignore_row[1],
            ignore_zero_gradients=flow_phase_ignore_row[2],
            title=title_widget,
            filename=save_field_button_latex_row[0],
            width=width_height_font_row[0],
            height=width_height_font_row[1],
            common_norm=common_toggle,
            font_scale=width_height_font_row[2],
            save=save_field_button_latex_row[1],
            latex=save_field_button_latex_row[2],
        ),
    ).servable()

    return pn.Column(
        width_height_font_row,
        save_field_button_latex_row,
        flow_phase_ignore_row,
        pn.Row(
            in_params,
            pn.Column(
                out_param,
                common_toggle,
            ),
        ),
        pn.Row(
            line_slider,
            bw_slider,
            title_widget,
        ),
        plot_pane,
    )


import pytest
import unittest.mock
from podcast_agent.services.supabase import SupabaseService

class TestSupabaseService:
    @pytest.fixture
    def mock_upload_file(self):
        with unittest.mock.patch(
            'podcast_agent.services.supabase.SupabaseService.upload_file',
            new_callable=unittest.mock.MagicMock
        ) as _mock:
            yield _mock

    def test_upload_file(self, mock_upload_file):
        mock_upload_file.return_value = True

        service = SupabaseService()

        result = service.upload_file(
            bucket_name='episode-audio-files',
            file_name='test.mp3'
        )

        mock_upload_file.assert_called_once_with(
            bucket_name='episode-audio-files',
            file_name='test.mp3'
        )
        assert result is not None


import os
import pysam
import argparse

############################################
############################################
## PARSE ARGUMENTS
############################################
############################################

Description = 'Remove singleton reads from paired-end BAM file i.e if read1 is present in BAM file without read 2 and vice versa.'
Epilog = """Example usage: bampe_rm_orphan.py <BAM_INPUT_FILE> <BAM_OUTPUT_FILE>"""

argParser = argparse.ArgumentParser(description=Description, epilog=Epilog)

## REQUIRED PARAMETERS
argParser.add_argument('BAM_INPUT_FILE', help="Input BAM file sorted by name.")
argParser.add_argument('BAM_OUTPUT_FILE', help="Output BAM file sorted by name.")

## OPTIONAL PARAMETERS
argParser.add_argument('-fr', '--only_fr_pairs', dest="ONLY_FR_PAIRS", help="Only keeps pairs that are in FR orientation on same chromosome.",action='store_true')
args = argParser.parse_args()

############################################
############################################
## HELPER FUNCTIONS
############################################
############################################

def makedir(path):

    if not len(path) == 0:
        try:
            #!# AVI: changed because of race conditions if directory exists, original code:  os.makedirs(path)
            os.makedirs(path, exist_ok=True)
        except OSError as exception:
            if exception.errno != errno.EEXIST:
                raise

############################################
############################################
## MAIN FUNCTION
############################################
############################################

def bampe_rm_orphan(BAMIn,BAMOut,onlyFRPairs=False):

    ## SETUP DIRECTORY/FILE STRUCTURE
    OutDir = os.path.dirname(BAMOut)
    makedir(OutDir)

    ## COUNT VARIABLES
    totalReads = 0; totalOutputPairs = 0; totalSingletons = 0; totalImproperPairs = 0

    ## ITERATE THROUGH BAM FILE
    EOF = 0
    SAMFin = pysam.AlignmentFile(BAMIn,"rb")  #!# AVI: changed to new API from pysam.Samfile
    SAMFout = pysam.AlignmentFile(BAMOut, "wb",header=SAMFin.header)   #!# AVI: changed to new API from pysam.Samfile
    currRead = next(SAMFin)     #!# AVI: adapted for the use of the iterator, original code: currRead = SAMFin.next()

    for read in SAMFin.fetch(until_eof=True): #!# AVI: added .fetch() to explicitly use new API
        totalReads += 1
        if currRead.qname == read.qname:
            pair1 = currRead; pair2 = read

            ## FILTER FOR READS ON SAME CHROMOSOME IN FR ORIENTATION
            if onlyFRPairs:
                if pair1.tid == pair2.tid:

                    ## READ1 FORWARD AND READ2 REVERSE STRAND
                    if not pair1.is_reverse and pair2.is_reverse:
                        if pair1.reference_start <= pair2.reference_start:
                            totalOutputPairs += 1
                            SAMFout.write(pair1)
                            SAMFout.write(pair2)
                        else:
                            totalImproperPairs += 1

                    ## READ1 REVERSE AND READ2 FORWARD STRAND
                    elif pair1.is_reverse and not pair2.is_reverse:
                        if pair2.reference_start <= pair1.reference_start:
                            totalOutputPairs += 1
                            SAMFout.write(pair1)
                            SAMFout.write(pair2)
                        else:
                            totalImproperPairs += 1

                    else:
                        totalImproperPairs += 1
                else:
                    totalImproperPairs += 1
            else:
                totalOutputPairs += 1
                SAMFout.write(pair1)
                SAMFout.write(pair2)

            ## RESET COUNTER
            try:
                totalReads += 1
                currRead = next(SAMFin)   #!# AVI: adapted for the use of the iterator, original code: currRead = SAMFin.next()
            except:
                StopIteration
                EOF = 1

        ## READS WHERE ONLY ONE OF A PAIR IS IN FILE
        else:
            totalSingletons += 1
            pair1 = currRead
            currRead = read

    if not EOF:
        totalReads += 1
        totalSingletons += 1
        pair1 = currRead

    ## CLOSE ALL FILE HANDLES
    SAMFin.close()
    SAMFout.close()

    LogFile = os.path.join(OutDir,'%s_bampe_rm_orphan.log' % (os.path.basename(BAMOut[:-4])))
    SamLogFile = open(LogFile,'w')
    SamLogFile.write('\n##############################\n')
    SamLogFile.write('FILES/DIRECTORIES')
    SamLogFile.write('\n##############################\n\n')
    SamLogFile.write('Input File: ' + BAMIn + '\n')
    SamLogFile.write('Output File: ' + BAMOut + '\n')
    SamLogFile.write('\n##############################\n')
    SamLogFile.write('OVERALL COUNTS')
    SamLogFile.write('\n##############################\n\n')
    SamLogFile.write('Total Input Reads = ' + str(totalReads) + '\n')
    SamLogFile.write('Total Output Pairs = ' + str(totalOutputPairs) + '\n')
    SamLogFile.write('Total Singletons Excluded = ' + str(totalSingletons) + '\n')
    SamLogFile.write('Total Improper Pairs Excluded = ' + str(totalImproperPairs) + '\n')
    SamLogFile.write('\n##############################\n')
    SamLogFile.close()

############################################
############################################
## RUN FUNCTION
############################################
############################################

bampe_rm_orphan(BAMIn=args.BAM_INPUT_FILE,BAMOut=args.BAM_OUTPUT_FILE,onlyFRPairs=args.ONLY_FR_PAIRS)

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


import shutil
import subprocess
import os
import sys
import re

def ffmpeg_isInstalled() -> bool:
    output: str = os.popen(
        cmd="ffmpeg -version"
    ).read()
    return "gcc" in output

def get_audio_duration(file) -> float:
    result = subprocess.run(['ffprobe', '-v', 'error', '-show_entries', 'format=duration', '-of', 'default=noprint_wrappers=1:nokey=1', file],
                            stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, encoding=os.device_encoding(0))
    return float(result.stdout)

def durationToSeconds(hms) -> float:
    a = hms.split(":")
    seconds = (int(a[0])) * 60 * 60 + (int(a[1])) * 60 + (float(a[2]));
    return seconds

def get_full_filepath(filename):
    current_dir = os.getcwd()
    directory, file = os.path.split(filename)

    if not directory:
        directory = current_dir

    full_file_path = os.path.join(directory, file)
    return os.path.abspath(full_file_path)

def display_progress_bar(
    current: int, filesize: int, ch: str = "█", scale: float = 0.5
) -> None:
    columns = shutil.get_terminal_size().columns
    max_width = int(columns * scale)
    filled = int(round(max_width * current / float(filesize)))
    remaining = max_width - filled
    progress_bar = ch * filled + " " * remaining
    percent = round(100.0 * current / float(filesize), 1)
    text = f"[ ↳ ] |{progress_bar}| {percent}%\r"
    sys.stdout.write(text)
    sys.stdout.flush()

def clear_last_line(amount=1):
    os.system('')
    for i in range(amount):
        sys.stdout.write("\033[K") #clear line
        if i < amount-1:
            sys.stdout.write("\033[F") #back to previous line

def make_ffmpeg_command(command, duration, on_progress=None):
    process = subprocess.Popen(command, encoding=os.device_encoding(0), universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    history = []
    with process.stdout as pipe:
        for line in pipe:
            line = line.strip()
            history.append(line)
            if "time=" in line:
                try:
                    result = re.search(r"\.*time=(.*?) ", line)
                    seconds = durationToSeconds(result.group(1))
                    if on_progress:
                        on_progress(seconds, duration)
                    else:
                        display_progress_bar(seconds, duration)
                except: None

    return process.wait(), history


from string import ascii_letters
from dataclasses import dataclass
from typing import Sequence, Union

from tucker_riemopt import backend as back
from tucker_riemopt import Tucker

@dataclass()
class TuckerMatrix(Tucker):
    """Tucker representation for operators.

    :param core: The core tensor of the decomposition.
    :param factors: The list of factors of the decomposition.
    :param n: Dimensionality of source space (see ``from_dense`` method).
    :param m: Dimensionality of target space (see ``from_dense`` method).
    """
    n: Union[Sequence[int], None] = None
    m: Union[Sequence[int], None] = None

    @classmethod
    def from_dense(cls, dense_tensor: back.type(), n: Union[Sequence[int], None] = None, m: Union[Sequence[int], None] = None,
                   eps=1e-14):
        """Convert dense tensor into `TuckerMatrix` representation. The resul is tensor that represents linear operator
        from R^N to R^M.

        :param dense_tensor: Dense tensor that will be factorized.
        :param n: Sequence of ints (n_1, ..., n_a) such that N = n_1 * ... * n_a.
        :param m: Sequence of ints (m_1, ..., m_b) such that M = m_1 * ... * m_b.
        :param eps: Precision of approximation.
        :return: `TuckerMatrix` representation of the provided dense tensor.
        """
        if n is None or m is None:
            raise ValueError("n and m parameter must be specialized for matrices")
        dense_tensor = cls._hosvd(dense_tensor, eps=eps)
        return cls(dense_tensor.core, dense_tensor.factors, n, m)

    def __matmul__(self, other: Union[back.type(), Tucker, "TuckerMatrix"]):
        """Perform matrix multiplication of tensors. If other's ndim > matrix ndim, then perform batch matmul over last
        ndim modes.

        :param other: Can be dense tensor, Tucker or TuckerMatrix. If dense tensor, then treated as vector and matvec
        operation is performed. The result is also a dense tensor. If `Tucker`, then treated as vector and matvec
        operation is performed. The result is also a Tucker. Else matmul operation is performed and another matrix is
        returned.
        :return: Dense tensor, Tucker or TuckerMatrix, depends on type of `other`.
        """
        if type(other) == back.type():
            core_letters = ascii_letters[:self.ndim]
            factors_letters = []
            operand_letters = []
            result_letters = []
            reshaped_factors = []
            for i in range(self.ndim):
                reshaped_factors.append(back.reshape(self.factors[i], (self.n[i], self.m[i], -1), order="F"))
                factors_letters.append(ascii_letters[self.ndim + 2 * i: self.ndim + 2 * (i + 1)] + core_letters[i])
                operand_letters.append(factors_letters[-1][0])
                result_letters.append(factors_letters[-1][1])
            batch_letters = []
            for i in range(len(other.shape) - self.ndim):
                batch_letters.append(ascii_letters[2 * (self.ndim + 1) + i + 1])
            operand_letters = batch_letters + operand_letters
            result_letters =  batch_letters + result_letters
            return back.einsum(f"{core_letters},{','.join(factors_letters)},{''.join(operand_letters)}->{''.join(result_letters)}",
                               self.core, *reshaped_factors, other)


import justpy as jp

def hint_test():
    wp = jp.QuasarPage()
    d = jp.Div(classes="q-pa-md q-gutter-lg", style="max-width: 300px", a=wp)
    input1 = jp.QInput(a=d, label='My Field', hint="This is my hint.")
    s1 = jp.QSelect(label='Select Company', a=d, color='orange', clearable=True, standout=True, bottom_slots=True, counter=True,
                 hint='My hint', options=['Google', 'Facebook', 'Twitter', 'Apple', 'Oracle'])
    return wp

jp.justpy(hint_test)


import cv2 as cv
import matplotlib
import numpy as np
import random

matplotlib.use('Agg')

from itertools import cycle
from matplotlib import colors as mcolors
from matplotlib import pyplot as plt
from pls_classifier import PLSClassifier
from sklearn.metrics import auc
from sklearn.metrics import average_precision_score, precision_recall_curve
from sklearn.metrics import roc_curve, roc_auc_score
from sklearn.svm import SVC
from sklearn.svm import SVR

def load_txt_file(file_name):
    this_file = open(file_name, 'r')
    this_list = []
    for line in this_file:
        line = line.rstrip()
        components = line.split()
        this_list.append(components)
    return this_list

def read_fold_file(file_name):
    with open(file_name) as file_input:
        neg_folds = []
        pos_folds = []

        file_list = file_input.readlines()
        for index in range(len(file_list)):
            file_list[index] = file_list[index].replace(' ', '_').strip().split('\t')

        index = 0;
        while str(file_list[index][0]).startswith('#'):
            index += 1
        num_folds = int(file_list[index][0])
        index += 1

        while index < len(file_list):
            if not str(file_list[index][0]).startswith('#'):
                num_pos = int(file_list[index][0])
                num_neg = int(file_list[index][1])
                index += 1

                pos_list = []
                for inner in range(index, num_pos + index):
                    if len(file_list[inner]) == 3:
                        file_list[inner].insert(2, file_list[inner][0])
                    pos_list.append(tuple(file_list[inner]))
                    index += 1

                neg_list = []
                for inner in range(index, num_neg + index):
                    neg_list.append(tuple(file_list[inner]))
                    index += 1

                pos_folds.append(pos_list)
                neg_folds.append(neg_list)
            else:
                index += 1

        return pos_folds, neg_folds

def split_known_unknown_sets(complete_tuple_list, known_set_size=0.5):
    label_set = set()
    for (path, label) in complete_tuple_list:
        label_set.add(label)

    known_set = set(random.sample(label_set, int(known_set_size * len(label_set))))
    unknown_set = label_set - known_set

    known_tuple = [(path, label) for (path, label) in complete_tuple_list if label in known_set]
    unknown_tuple = [(path, label) for (path, label) in complete_tuple_list if label in unknown_set]

    return known_tuple, unknown_tuple

def split_train_test_samples(complete_tuple_list, train_set_samples=4):
    to_shuffle = [item for item in complete_tuple_list]
    np.random.shuffle(to_shuffle)

    tuple_dict = dict()
    for (path, label) in to_shuffle:
        if tuple_dict.has_key(label):
            tuple_dict[label].append(path)
        else:
            tuple_dict[label] = [path]

    # for tuple in tuple_dict.iteritems():
    #     assert len(tuple[1]) > train_set_samples

    test_set = []
    train_set = []
    for (label, paths) in tuple_dict.iteritems():
        for path in paths[0:train_set_samples]:
            train_set.append((path, label))
        for path in paths[train_set_samples:len(paths)]:
            test_set.append((path, label))

    return train_set, test_set

def split_train_test_sets(complete_tuple_list, train_set_size=0.5):
    label_set = {label for (path, label) in complete_tuple_list}
    train_tuple = list()
    test_tuple = list()
    for label in label_set:
        # Sample images to compose train_set
        path_set = {path for (path, target) in complete_tuple_list if label == target}
        train_set = set(random.sample(path_set, int(train_set_size * len(path_set))))
        test_set = path_set - train_set
        # Put together labels and paths
        train_tuple.extend([(path, label) for path in train_set])
        test_tuple.extend([(path, label) for path in test_set])
    return train_tuple, test_tuple

def load_images(path, image_list, display=False):
    for name in image_list:
        image = cv.imread(path + '/' + name, cv.IMREAD_COLOR)
        if display:
            cv.imshow('img', image)
            cv.waitKey(20)
    if display:
        cv.destroyAllWindows()

def augment_gallery_set(image_sample):
    image_samples = []
    image_samples.append(image_sample)
    image_samples.append(cv.flip(image_sample, 1)) # vertical-axis flip
    rows,cols,dep = image_sample.shape
    for angle in range(-5,6,10):
        rot_matrix = cv.getRotationMatrix2D((rows/2, cols/2), angle, 1.1)
        image_samples.append(cv.warpAffine(image_sample, rot_matrix,(cols, rows)))
    return image_samples

def sliding_window(image, window_size, step_size):
    for y in xrange(0, image.shape[0], step_size):
        for x in xrange(0, image.shape[1], step_size):
            yield (x, y, image[y:y + window_size[1], x:x + window_size[0]])

def generate_pos_neg_dict(labels):
    to_shuffle = [item for item in labels]
    np.random.shuffle(to_shuffle)
    neg_set = map(lambda neg: (neg, -1), to_shuffle[0:(len(labels) / 2)])
    pos_set = map(lambda pos: (pos, +1), to_shuffle[(len(labels) / 2):len(labels)])
    full_set = neg_set + pos_set
    full_dict = dict((key, val) for key, val in full_set)
    return full_dict

def mount_tuple(tuple, dataset='lfw'):
    if dataset == 'lfw':
        sample_a = tuple[0] + '/' + tuple[0] + '_' + format(int(tuple[1]),'04d') + '.jpg'
        sample_b = tuple[2] + '/' + tuple[2] + '_' + format(int(tuple[3]),'04d') + '.jpg'
    elif dataset == 'pubfig':
        sample_a = tuple[0] + '/' + tuple[1] + '.jpg'
        sample_b = tuple[2] + '/' + tuple[3] + '.jpg'
    else:
        exit(0)
    return sample_a, sample_b

def split_into_chunks(full_tuple, num_models=100, num_subjects=100):
    neg_split = []
    pos_split = []

    tuple_dict = dict()
    for (path, label) in full_tuple:
        if tuple_dict.has_key(label):
            tuple_dict[label].append(path)
        else:
            tuple_dict[label] = [path]

    individuals = []
    while len(individuals) < num_subjects:
        individuals.extend(tuple_dict.keys())

    for index in range(num_models):
        neg_list = []
        pos_list = []
        for pos in random.sample(individuals, num_subjects):
            candidates = tuple(random.sample(tuple_dict[pos], 2))
            pos_list.append(candidates)
        for neg_index in range(num_subjects):
            chosen = random.sample(tuple_dict.keys(), 2)
            candidate_a = random.sample(tuple_dict[chosen[0]], 1)
            candidate_b = random.sample(tuple_dict[chosen[1]], 1)
            neg_list.append((candidate_a[0], candidate_b[0]))

        pos_split.append(pos_list)
        neg_split.append(neg_list)

    return pos_split, neg_split

def getModel(input_shape, nclasses=2):
    import keras
    from keras.datasets import mnist
    from keras.layers import Dense, Dropout
    from keras.models import Sequential
    from keras.optimizers import RMSprop
    from keras import callbacks

    model = Sequential()
    model.add(Dense(64, activation='relu', input_shape=input_shape))
    model.add(Dropout(0.2))
    model.add(Dense(nclasses, activation='softmax'))
    #model.summary()
    model.compile(loss='categorical_crossentropy', optimizer='adadelta', metrics=['accuracy'])#RMSprop()

    return model

def learn_fcn_model(X, Y, split):
    import keras
    from keras.utils import np_utils
    from keras import callbacks

    boolean_label = [(split[key]+1)/2 for key in Y]
    y_train = np_utils.to_categorical(boolean_label, 2)

    model = getModel(input_shape=X[0].shape)
    model.fit(X, y_train, batch_size=40, nb_epoch=100, verbose=0)
    return (model, split)

def learn_pls_model(matrix_x, matrix_y):
    classifier = PLSClassifier()
    model = classifier.fit(np.array(matrix_x), np.array(matrix_y))
    return model

def learn_plsh_model(matrix_x, matrix_y, split):
    classifier = PLSClassifier()
    boolean_label = [split[key] for key in matrix_y]
    model = classifier.fit(np.array(matrix_x), np.array(boolean_label))
    return (model, split)

def learn_plsh_v_model(pos_split, neg_split):
    neg_label = np.zeros(len(neg_split))
    pos_label = np.ones(len(pos_split))
    labels = np.concatenate([neg_label, pos_label])
    splits = neg_split + pos_split

    classifier = PLSClassifier()
    model = classifier.fit(np.array(splits), np.array(labels))
    return model

def learn_svmh_v_model(pos_split, neg_split):
    neg_label = np.zeros(len(neg_split))
    pos_label = np.ones(len(pos_split))
    labels = np.concatenate([neg_label, pos_label])
    splits = neg_split + pos_split

    classifier = SVR(C=1.0,kernel='linear')
    model = classifier.fit(np.array(splits), np.array(labels))
    return model

def learn_plsh_cv_model(features, dictionary, pos_split, neg_split):
    matrix_x = []
    matrix_y = []

    for pos in pos_split:
        index_a = dictionary[pos[0]]
        index_b = dictionary[pos[1]]
        diff_feat = np.absolute(np.subtract(features[index_a], features[index_b]))
        matrix_x.append(diff_feat)
        matrix_y.append(1)

    for neg in neg_split:
        index_a = dictionary[neg[0]]
        index_b = dictionary[neg[1]]
        diff_feat = np.absolute(np.subtract(features[index_a], features[index_b]))
        matrix_x.append(diff_feat)
        matrix_y.append(0)

    classifier = PLSClassifier()
    model = classifier.fit(np.array(matrix_x), np.array(matrix_y))
    return (model, (pos_split, neg_split))

def learn_svmh_cv_model(features, dictionary, pos_split, neg_split):
    matrix_x = []
    matrix_y = []

    for pos in pos_split:
        index_a = dictionary[pos[0]]
        index_b = dictionary[pos[1]]
        diff_feat = np.absolute(np.subtract(features[index_a], features[index_b]))
        matrix_x.append(diff_feat)
        matrix_y.append(1)

    for neg in neg_split:
        index_a = dictionary[neg[0]]
        index_b = dictionary[neg[1]]
        diff_feat = np.absolute(np.subtract(features[index_a], features[index_b]))
        matrix_x.append(diff_feat)
        matrix_y.append(0)

    classifier = SVR(C=1.0,kernel='linear')
    model = classifier.fit(np.array(matrix_x), np.array(matrix_y))
    return (model, (pos_split, neg_split))

def generate_probe_histogram(individuals, values, extra_name):
    plt.clf()
    plt.bar(range(len(individuals)), values)
    if sample_name == result[0][0]:
        plt.savefig('plots/' + extra_name + '_' + str(NUM_HASH) + '_' + str(counter) + '_' + sample_name + '_' + result[0][0])
    else:
        plt.savefig('plots/' + extra_name + '_' + str(NUM_HASH) + '_' + str(counter) + '_' + sample_name + '_' + result[0][0] + '_ERROR')

def generate_precision_recall(y_label_list, y_score_list):
    """
    A system with high recall but low precision returns many results, but most of its predicted labels are incorrect when compared to the training labels.
    A system with high precision but low recall is just the opposite, returning very few results, but most of its predicted labels are correct when compared to the training labels.
    An ideal system with high precision and high recall will return many results, with all results labeled correctly.
    """
    # Prepare input data
    label_list = [line[1] for line in y_label_list]
    score_list = [line[1] for line in y_score_list]
    label_array = np.array(label_list)
    score_array = np.array(score_list)

    # Compute micro-average ROC curve and ROC area
    pr = dict()
    pr['precision'], pr['recall'], pr['thresh'] = precision_recall_curve(label_array.ravel(), score_array.ravel())
    pr['avg_precision'] = average_precision_score(label_array, score_array, average="micro")
    return pr

def generate_roc_curve(y_label_list, y_score_list):
    """
    ROC curves typically feature true positive rate on the Y axis, and false positive rate on the X axis.
    This means that the top left corner of the plot is the ideal point - a false positive rate of zero, and a true positive rate of one.
    This is not very realistic, but it does mean that a larger area under the curve (AUC) is usually better.
    """
    # Prepare input data
    label_list = [line[1] for line in y_label_list]
    score_list = [line[1] for line in y_score_list]
    label_array = np.array(label_list)
    score_array = np.array(score_list)

    # Compute micro-average ROC curve and ROC area
    roc = dict()
    roc['fpr'], roc['tpr'], roc['thresh'] = roc_curve(label_array.ravel(), score_array.ravel())
    roc['auc']  = auc(roc['fpr'], roc['tpr'])
    return roc

def plot_cmc_curve(cmc_scores, extra_name):
    """
    The CMC shows how often the biometric subject template appears in the ranks (1, 5, 10, 100, etc.), based on the match rate.
    It is a method of showing measured accuracy performance of a biometric system operating in the closed-set identification task.
    Templates are compared and ranked based on their similarity.
    """
    # Setup plot details
    color_dict = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
    color_names = [name for name, color in color_dict.items()]
    colors = cycle(color_names)
    lw = 2

    # Plot Cumulative Matching Characteristic curve
    plt.clf()
    counter = 1
    for score, color in zip(cmc_scores, colors):
        x_axis = range(len(score))
        y_axis = score
        area = auc(x_axis, y_axis)/len(score)
        rank1 = score[0]
        plt.plot(x_axis, y_axis, lw=lw, color=color, label='CMC curve %d (area = %0.2f, rank-1 = %0.2f)' % (counter, area, rank1))
        counter += 1

    plt.xlim([0, len(score)])
    plt.ylim([0.0, 1.05])
    plt.xlabel('Rank')
    plt.ylabel('Accuracy Rate')
    plt.title('Cumulative Matching Characteristic')
    plt.legend(loc="lower right")
    plt.grid()
    if extra_name == None:
        plt.show()
    else:
        plt.savefig('./plots/CMC_' + extra_name + '.pdf')
    plt.close()

def plot_precision_recall(prs, extra_name=None):
    # Setup plot details
    aucs = [pr['avg_precision'] for pr in prs]
    color_dict = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
    color_names = [name for name, color in color_dict.items()]
    colors = cycle(color_names)
    lw = 2

    # Plot Precision-Recall curve
    plt.clf()
    for index, color in zip(range(len(prs)), colors):
        pr = prs[index]
        plt.plot(pr['recall'], pr['precision'], lw=lw, color=color, label='PR curve %d (area = %0.2f)' % (index+1, pr['avg_precision']))
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.ylim([0.0, 1.05])
    plt.xlim([0.0, 1.0])
    plt.title('Precision-Recall Curve (%0.3f - %0.3f)' % (np.mean(aucs),np.std(aucs)))
    plt.legend(loc="lower left")
    plt.grid()
    if extra_name == None:
        plt.show()
    else:
        plt.savefig('./plots/PR_' + extra_name + '.pdf')
    plt.close()

def plot_roc_curve(rocs, extra_name=None):
    # Setup plot details
    aucs = [roc['auc'] for roc in rocs]
    color_dict = dict(mcolors.BASE_COLORS, **mcolors.CSS4_COLORS)
    color_names = [name for name, color in color_dict.items()]
    colors = cycle(color_names)
    lw = 2

    # Plot Receiver Operating Characteristic curve
    plt.clf()
    for index, color in zip(range(len(rocs)), colors):
        roc = rocs[index]
        plt.plot(roc['fpr'], roc['tpr'], color=color, lw=lw, label='ROC curve %d (area = %0.2f)' % (index+1, roc['auc']))
    plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
    plt.xlim([0.0, 1.0])
    plt.ylim([0.0, 1.05])
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.title('Receiver Operating Characteristic (%0.3f - %0.3f)' % (np.mean(aucs),np.std(aucs)))
    plt.legend(loc="lower right")
    plt.grid()
    if extra_name == None:
        plt.show()
    else:
        plt.savefig('./plots/ROC_' + extra_name + '.pdf')
    plt.close()

def iteration_to_fold(prs, rocs):
    fold_prs = {}
    fold_rocs = {}

    # prs_avg = []
    # prs_pre = []
    # prs_rec = []
    # prs_thr = []
    # avg_prs = {}
    for row in prs:
        for col in range(len(row)):
            if fold_prs.has_key(col):
                fold_prs[col].append(row[col])
            else:
                fold_prs[col] = [row[col]]
    # for fold in fold_prs.values():
    #     for item in fold:
    #         prs_avg.append(item['avg_precision'])
    #         prs_pre.append(item['precision'])
    #         prs_rec.append(item['recall'])
    #         prs_thr.append(item['thresh'])
    #     avg_prs['avg_precision'] = np.mean(prs_avg, axis=0)
    #     avg_prs['precision'] = np.mean(prs_pre, axis=0)
    #     avg_prs['recall'] = np.mean(prs_rec, axis=0)
    #     avg_prs['thresh'] = np.mean(prs_thr, axis=0)

    # rocs_auc = []
    # rocs_fpr = []
    # rocs_thr = []
    # rocs_tpr = []
    # avg_rocs = {}
    for row in rocs:
        for col in range(len(row)):
            if fold_rocs.has_key(col):
                fold_rocs[col].append(row[col])
            else:
                fold_rocs[col] = [row[col]]
    # for fold in fold_rocs.values():
    #     for item in fold:
    #         rocs_auc.append(item['auc'])
    #         rocs_fpr.append(item['fpr'])
    #         rocs_thr.append(item['thresh'])
    #         rocs_tpr.append(item['tpr'])
    #     avg_rocs['auc'] = np.mean(rocs_auc, axis=0)
    #     avg_rocs['fpr'] = np.mean(rocs_fpr, axis=0)
    #     avg_rocs['thresh'] = np.mean(rocs_thr, axis=0)
    #     avg_rocs['tpr'] = np.mean(rocs_tpr, axis=0)

    return fold_prs, fold_rocs


"""
Base class for env files.  This class inherits from EntryID.py
"""
from CIME.XML.standard_module_setup import *
from CIME.XML.entry_id import EntryID
from CIME.XML.headers import Headers
from CIME.utils import convert_to_type
logger = logging.getLogger(__name__)

class EnvBase(EntryID):

    def __init__(self, case_root, infile, schema=None):
        if case_root is None:
            case_root = os.getcwd()

        if os.path.isabs(infile):
            fullpath = infile
        else:
            fullpath = os.path.join(case_root, infile)

        EntryID.__init__(self, fullpath, schema=schema)
        if not os.path.isfile(fullpath):
            headerobj = Headers()
            headernode = headerobj.get_header_node(os.path.basename(fullpath))
            self.root.append(headernode)

    def set_components(self, components):
        if hasattr(self, '_components'):
            # pylint: disable=attribute-defined-outside-init
            self._components = components

    def check_if_comp_var(self, vid, attribute=None):
        nodes = self.get_nodes("entry", {"id" : vid})
        node = None
        comp = None
        if len(nodes):
            node = nodes[0]
        if node:
            valnodes = node.findall(".//value[@compclass]")
            if len(valnodes) == 0:
                logger.debug("vid {} is not a compvar".format(vid))
                return vid, None, False
            else:
                logger.debug("vid {} is a compvar".format(vid))
                if attribute is not None:
                    comp = attribute["compclass"]
                return vid, comp, True
        else:
            if hasattr(self, "_components"):
                new_vid = None
                for comp in self._components:
                    if "_"+comp in vid:
                        new_vid = vid.replace('_'+comp, '', 1)
                    elif comp+"_" in vid:
                        new_vid = vid.replace(comp+'_', '', 1)

                    if new_vid is not None:
                        break
                if new_vid is not None:
                    logger.debug("vid {} is a compvar with comp {}".format(vid, comp))
                    return new_vid, comp, True

        return vid, None, False

    def get_value(self, vid, attribute=None, resolved=True, subgroup=None):
        """
        Get a value for entry with id attribute vid.
        or from the values field if the attribute argument is provided
        and matches
        """
        value = None
        vid, comp, iscompvar = self.check_if_comp_var(vid, attribute)
        logger.debug("vid {} comp {} iscompvar {}".format(vid, comp, iscompvar))
        if iscompvar:
            if comp is None:
                if subgroup is not None:
                    comp = subgroup
                else:
                    logger.debug("Not enough info to get value for {}".format(vid))
                    return value
            if attribute is None:
                attribute = {"compclass" : comp}
            else:
                attribute["compclass"] = comp
            node = self.get_optional_node("entry", {"id":vid})
            if node is not None:
                type_str = self._get_type_info(node)
                val = self.get_element_text("value", attribute, root=node)
                if val is not None:
                    if val.startswith("$"):
                        value = val
                    else:
                        value = convert_to_type(val,type_str, vid)
                return value
        return EntryID.get_value(self, vid, attribute=attribute, resolved=resolved, subgroup=subgroup)

    def set_value(self, vid, value, subgroup=None, ignore_type=False):
        """
        Set the value of an entry-id field to value
        Returns the value or None if not found
        subgroup is ignored in the general routine and applied in specific methods
        """
        vid, comp, iscompvar = self.check_if_comp_var(vid, None)
        val = None
        root = self.root if subgroup is None else self.get_optional_node("group", {"id":subgroup})
        node = self.get_optional_node("entry", {"id":vid}, root=root)
        if node is not None:
            if iscompvar and comp is None:
                # pylint: disable=no-member
                for comp in self._components:
                    val = self._set_value(node, value, vid, subgroup, ignore_type, compclass=comp)
            else:
                val = self._set_value(node, value, vid, subgroup, ignore_type, compclass=comp)
        return val

    # pylint: disable=arguments-differ
    def _set_value(self, node, value, vid=None, subgroup=None, ignore_type=False, compclass=None):
        if vid is None:
            vid = node.get("id")
        vid, _, iscompvar = self.check_if_comp_var(vid, None)

        if iscompvar:
            attribute = {"compclass":compclass}
            str_value = self.get_valid_value_string(node, value, vid, ignore_type)
            val = self.set_element_text("value", str_value, attribute, root=node)
        else:
            val = EntryID._set_value(self, node, value, vid, subgroup, ignore_type)
        return val

    def get_nodes_by_id(self, varid):
        varid, _, _ = self.check_if_comp_var(varid, None)
        return EntryID.get_nodes_by_id(self, varid)

    def cleanupnode(self, node):
        """
        Remove the <group>, <file>, <values> and <value> childnodes from node
        """
        fnode = node.find(".//file")
        node.remove(fnode)
        gnode = node.find(".//group")
        node.remove(gnode)
        dnode = node.find(".//default_value")
        if dnode is not None:
            node.remove(dnode)
        vnode = node.find(".//values")
        if vnode is not None:
            componentatt = vnode.findall(".//value[@component=\"ATM\"]")
            # backward compatibility (compclasses and component were mixed
            # now we seperated into component and compclass)
            if len(componentatt) > 0:
                for ccnode in vnode.findall(".//value[@component]"):
                    val = ccnode.attrib.get("component")
                    ccnode.attrib.pop("component")
                    ccnode.set("compclass",val)
            compclassatt = vnode.findall(".//value[@compclass]")

            if len(compclassatt) == 0:
                node.remove(vnode)

        return node


#!/usr/bin/env python
import pickle
import os

import numpy as np
from keras.backend.tensorflow_backend import set_session
import tensorflow as tf
config = tf.ConfigProto()
config.gpu_options.allow_growth = True
set_session(tf.Session(config=config))
from keras.preprocessing.sequence import pad_sequences
from keras.callbacks import EarlyStopping, ModelCheckpoint
from keras.utils import plot_model

from src import get_model

def pad(sentences, mapping, pad_length):
    return pad_sequences([[mapping.get(word, 0) for word in sentence] for sentence in sentences],
                         pad_length, padding='post', truncating='post')

def main(args):
    with open(args.training_path, 'rb') as fp:
        train_q, train_a, train_y, valid_q, valid_a, valid_y = pickle.load(fp)
    embedding = np.load('models/char_base_100_embedding.npy')
    model = get_model(args.model, embedding)
    os.makedirs(f'models/{args.model}', exist_ok=True)
    early_stopping = EarlyStopping(monitor='val_loss', patience=3)
    checkpoint = ModelCheckpoint(f'models/{args.model}/{args.model}' + '_{epoch:03d}_{val_loss:.4f}')
    plot_model(model, f'models/{args.model}/structure.png')
    model.fit([train_q, train_a], train_y, validation_data=([valid_q, valid_a], valid_y),
              batch_size=256, epochs=100, callbacks=[early_stopping, checkpoint])

def parse_args():
    import argparse
    parser = argparse.ArgumentParser('hw5')
    parser.add_argument('-train', '--training-path', default='./raw_data/train_padded.pkl')
    parser.add_argument('-test', '--testing-path', default='./raw_data/testing_data.txt')
    parser.add_argument('-o', '--output-path', default='prediction.csv')
    parser.add_argument('-m', '--model', default='stacked_gru_2_dot')
    parser.add_argument('--mapping', default='./models/mapping.pkl')
    return parser.parse_args()

if __name__ == '__main__':
    main(parse_args())


# coding: utf-8

# In[3]:

get_ipython().run_line_magic('config', "InlineBackend.figure_format = 'retina'")
get_ipython().run_line_magic('matplotlib', 'inline')

import numpy as np
import matplotlib.pyplot as plt

def iterate_map(f, x_0, n):
    # extended for multi-dim case
    # trajectory = [x_0] * (n+1)
    trajectory = np.stack([x_0] * (n+1), axis=0)
    trajectory[0] = x_0
    for i in range(1, n + 1):
        # should probably just pass in vector instead of unpacking
        trajectory[i] = f(*trajectory[i-1])

    return trajectory

henon = lambda a, b: lambda x, y: np.asarray((a - (x**2) + b * y, x))
henon_inv = lambda a, b: lambda x, y: np.asarray((y, (x + y**2 - a) / b))

# In[4]:

# def problem_1():
#     trapezoid =
def make_polygon(corners, total_pts):
    pts_per_side = total_pts // len(corners)
    points = np.array([]).reshape(0, 2)

    for i in range(len(corners)):
        (x_start, y_start) = corners[i]
        (x_end, y_end) = corners[(i+1) % len(corners)]

        side_points = np.array([np.linspace(x_start, x_end, pts_per_side),
                                np.linspace(y_start, y_end, pts_per_side)]).T

#         np.linspace(x_start, x_end, pts_per_side)
#         np.interp(,  [x_start, x_end], [y_start, y_end])
        points = np.concatenate((points, side_points), axis=0)

    return points

# In[5]:

def recur(fn, x_in, y_in, remaining):
    if remaining == 0:
        return (x_in, y_in)
    else:
        (x_out, y_out) = fn(x_in, y_in)
        return recur(fn, x_out, y_out, remaining - 1)

def problem_1():
    a = 4
    b = 0.2

    fig1, ax1 = plt.subplots(figsize=(8, 8))
    fig2, ax2 = plt.subplots(figsize=(8, 8))

    ax1.set_aspect('equal')
    ax1.set_ylim(-6, 6)
    ax1.set_xlim(-6, 6)

    ax2.set_aspect('equal')
    ax2.set_ylim(-6, 6)
    ax2.set_xlim(-6, 6)

    trapezoid = make_polygon([[-3, -3], [3, -1.5], [3, 1.5], [-3, 3]], 10**6)
    trapezoid_x = trapezoid[:,0].flatten()
    trapezoid_y = trapezoid[:,1].flatten()
    dist = trapezoid_x + trapezoid_y

    # Plot trapezoid, f1 and f1_inv
    ax1.scatter(trapezoid_x, trapezoid_y, c=dist, s=0.1, cmap=plt.get_cmap('cool'))

    trapezoid_f1 = henon(a, b)(trapezoid_x, trapezoid_y)
    ax1.scatter(trapezoid_f1[0], trapezoid_f1[1], c=dist, s=0.1, cmap=plt.get_cmap('cool'))

    trapezoid_f1_inv = henon_inv(a, b)(trapezoid_x, trapezoid_y)
    ax1.scatter(trapezoid_f1_inv[0], trapezoid_f1_inv[1], c=dist, s=0.1, cmap=plt.get_cmap('cool'))

    # Plot trapezoid, f4 and f4_inv
    ax2.scatter(trapezoid_x, trapezoid_y, s=0.1, color="tomato")

    henon4 = lambda a, b: lambda x, y: recur(henon(a, b), x, y, 4)
    henon4_inv = lambda a, b: lambda x, y: recur(henon_inv(a, b), x, y, 4)

    trapezoid_f4 = henon4(a, b)(trapezoid_x, trapezoid_y)
    trapezoid_f4_inv = henon4_inv(a, b)(trapezoid_x, trapezoid_y)

    ax2.scatter(trapezoid_f4[0], trapezoid_f4[1], s=0.1, color="orange")
    ax2.scatter(trapezoid_f4_inv[0], trapezoid_f4_inv[1], s=1, color="dodgerblue")

# In[6]:

problem_1()

# In[124]:

# .plot is faster than .scatter for large # of data points
def problem_1_fast():
    a = 4
    b = 0.2

    fig1, ax1 = plt.subplots(figsize=(8, 8))
    fig2, ax2 = plt.subplots(figsize=(8, 8))

#     https://matplotlib.org/gallery/text_labels_and_annotations/custom_legends.html#sphx-glr-gallery-text-labels-and-annotations-custom-legends-py
#     color_cycle = plt.get_cmap('Pastel2')(np.linspace(0, 1, 3))
#     color_cycle = ['#E0BBE4', '#957DAD', '#D291BC']
    color_cycle = ['#FFCDCD', '#FFC961', '#FF927A']
    ax1.set_prop_cycle(color=color_cycle)
    ax2.set_prop_cycle(color=color_cycle)

    ax1.set_aspect('equal')
    ax1.set_ylim(-6, 6)
    ax1.set_xlim(-6, 6)

    ax2.set_aspect('equal')
    ax2.set_ylim(-6, 6)
    ax2.set_xlim(-6, 6)

    trapezoid = make_polygon([[-3, -3], [3, -1.5], [3, 1.5], [-3, 3]], 10**6)
    trapezoid_x = trapezoid[:,0].flatten()
    trapezoid_y = trapezoid[:,1].flatten()
    dist = trapezoid_x + trapezoid_y

    # Plot trapezoid, f1 and f1_inv
    ax1.plot(trapezoid_x, trapezoid_y, 'o', markersize=.1)

    trapezoid_f1 = henon(a, b)(trapezoid_x, trapezoid_y)
    ax1.plot(trapezoid_f1[0], trapezoid_f1[1], 'o', markersize=.1)

    trapezoid_f1_inv = henon_inv(a, b)(trapezoid_x, trapezoid_y)
    ax1.plot(trapezoid_f1_inv[0], trapezoid_f1_inv[1], 'o', markersize=.1)

    # Plot trapezoid, f4 and f4_inv
    ax2.plot(trapezoid_x, trapezoid_y, 'o', markersize=.1)

    henon4 = lambda a, b: lambda x, y: recur(henon(a, b), x, y, 4)
    henon4_inv = lambda a, b: lambda x, y: recur(henon_inv(a, b), x, y, 4)

    trapezoid_f4 = henon4(a, b)(trapezoid_x, trapezoid_y)
    trapezoid_f4_inv = henon4_inv(a, b)(trapezoid_x, trapezoid_y)

    ax2.plot(trapezoid_f4[0], trapezoid_f4[1], 'o', markersize=.1)
    ax2.plot(trapezoid_f4_inv[0], trapezoid_f4_inv[1], marker='o', markersize=.1)

# In[125]:

problem_1_fast()

# In[7]:

import functools

def make_polygon_col(corners, total_pts):
    pts_per_side = total_pts // len(corners)
    points = np.array([]).reshape(2, 0)

    for i in range(len(corners)):
        (x_start, y_start) = corners[i]
        (x_end, y_end) = corners[(i+1) % len(corners)]

        side_points = np.array([np.linspace(x_start, x_end, pts_per_side),
                                np.linspace(y_start, y_end, pts_per_side)])

        points = np.concatenate((points, side_points), axis=1)

    return points

def make_ifs(fns, probabilities=[]):
    # create prob thresholds
    if not probabilities:
        probabilities = [1/len(fns)] * len(fns)
    thresholds = functools.reduce(lambda l, el: l + [l[-1] + el], probabilities, [0])[1:]

    def apply_ifs(x): # let x be a vector
        random = np.random.random()
        for i in range(len(thresholds)):
            if random < thresholds[i]:
                return fns[i](x)
        else:
            return fns[len(fns) - 1](x)

    return apply_ifs

def iterate_map_vec(f, x_0, n):
    # extended for multi-dim case
    # trajectory = [x_0] * (n+1)
    trajectory = np.stack([x_0] * (n+1), axis=0)
    trajectory[0] = x_0
    for i in range(1, n + 1):
        # takes in vector instead of params
        trajectory[i] = f(trajectory[i-1])

    return trajectory

def problem_2():

    square_x, square_y = make_polygon_col([[0, 0], [0, 1], [1, 1], [1, 0]], 10**4)
    square = [square_x, square_y]

    f1 = lambda x: np.array((x[0]/2, x[1]/2))
    f2 = lambda x: np.array((x[0]/2 + 1/4, x[1]/2 + 1/2))
    f3 = lambda x: np.array((x[0]/2 + 1/2, x[1]/2))

    ifs = make_ifs([f1, f2, f3])
    trajectory = iterate_map_vec(ifs, [0.5, 0.5], 10**5)

    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_aspect('equal')

    square_f1 = np.apply_along_axis(f1, 0, square)
    square_f2 = np.apply_along_axis(f2, 0, square)
    square_f3 = np.apply_along_axis(f3, 0, square)

    ax.scatter(square_x, square_y, s=1)
    ax.scatter(square_f1[0], square_f1[1], s=1)
    ax.scatter(square_f2[0], square_f2[1], s=1)
    ax.scatter(square_f3[0], square_f3[1], s=1)
    ax.scatter(trajectory[:,0].flatten(), trajectory[:,1].flatten(), s=0.1, color="mediumpurple")

# In[246]:

problem_2()
# scaling dimension: ln(3) / ln(2)

# In[9]:

# np.apply_along_axis(lambda line: print(line), 0, [[1, 2, 3], [4, 5, 6]])
# np.apply_along_axis(lambda line: print(line), 1, [[1, 2, 3], [4, 5, 6]])

# In[10]:

# deterministic IFS
def make_difs(fns, probabilities=[]):
    # create prob thresholds
    if not probabilities:
        probabilities = [1/len(fns)] * len(fns)
    thresholds = functools.reduce(lambda l, el: l + [l[-1] + el], probabilities, [0])[1:]

    def apply_ifs(x): # let x be a vector of inputs and z at end
        z = x[-1]
        z_shifted = (z*len(fns)) % 1

        for i in range(len(thresholds)):
            if z < thresholds[i]:
                return [*fns[i](x), z_shifted]
        else:
            return [*fns[len(fns) - 1](x), z_shifted]

    return apply_ifs

def problem_3():
    f1 = lambda x: np.array((x[0]/2, x[1]/2))
    f2 = lambda x: np.array((x[0]/2 + 1/4, x[1]/2 + 1/2))
    f3 = lambda x: np.array((x[0]/2 + 1/2, x[1]/2))

    difs = make_difs([f1, f2, f3])
    random_xyz = [np.random.random(), np.random.random(), np.random.random()]

    trajectory = iterate_map_vec(difs, random_xyz, 10**6)

    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_aspect('equal')

    ax.scatter(trajectory[:,0].flatten(), trajectory[:,1].flatten(), s=0.1, color="mediumpurple")

# In[11]:

problem_3()

# In[12]:

def problem_4():
    f1 = lambda x: np.array((x[0]/3, x[1]/3))
    f2 = lambda x: np.array((x[0]/3 + 2/3, x[1]/3))
    f3 = lambda x: np.array((x[0]/3, x[1]/3 + 2/3))
    f4 = lambda x: np.array((x[0]/3 + 2/3, x[1]/3 + 2/3))

    ifs = make_ifs([f1, f2, f3, f4])

    trajectory = iterate_map_vec(ifs, [0.5, 0.5], 10**5)

    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_aspect('equal')

    ax.scatter(trajectory[:,0].flatten(), trajectory[:,1].flatten(), s=1)

# In[13]:

problem_4()

# In[14]:

def problem_5e():
    c = (2 * np.sqrt(2))
    f1 = lambda x: np.asarray([x[0]/c - x[1]/c + 1/4, x[0]/c + x[1]/c + 1/4])
    f2 = lambda x: np.asarray([x[0]/2 + 1/2, x[1]/2])

    ifs = make_ifs([f1, f2])

    trajectory = iterate_map_vec(ifs, [0.5, 0.5], 10**5)

    fig, ax = plt.subplots(figsize=(8, 8))
    ax.set_aspect('equal')

    ax.scatter(trajectory[:,0].flatten(), trajectory[:,1].flatten(), s=0.1, alpha=0.8)

# In[15]:

problem_5e()

# In[244]:

def get_boxes(x, n):
#     return np.floor((x) * boxes)
    return (x) // (1 / n) # round towards zero

def problem_6():
    fn = henon(1.4, 0.3)
    trajectory = iterate_map(fn, [0.2, 0.2], 10**6 - 1)
    traj_x = trajectory[:,0].flatten()
    traj_y = trajectory[:,1].flatten()

    fig, ax = plt.subplots(figsize=(8, 8))
    ax.plot(traj_x, traj_y, 'o', markersize=0.1, color='#FF8686')

    n = 10**5
    boxes = n ** 2

    box_coords = np.array([get_boxes(traj_x, n), get_boxes(traj_y, n)]).T
    covering_boxes = len(np.unique(box_coords))

    print(covering_boxes)

    print("Box counting dimension: ")
    print(np.log(covering_boxes) / np.log(n))

# In[245]:

problem_6()


#
#       Copyright (C) 2015
#       Marshall Dias (mdias@imwirefree.com)
#
#  Credit to pipcan and spoyser
#
#  This Program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2, or (at your option)
#  any later version.
#
#  This Program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with XBMC; see the file COPYING.  If not, write to
#  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
#  http://www.gnu.org/copyleft/gpl.html
#

import xbmc
import xbmcaddon
import xbmcgui
import os

def GetXBMCVersion():
    #xbmc.executeJSONRPC('{ "jsonrpc": "2.0", "method": "Application.GetProperties", "params": {"properties": ["version", "name"]}, "id": 1 }')

    version = xbmcaddon.Addon('xbmc.addon').getAddonInfo('version')
    version = version.split('.')
    return int(version[0]), int(version[1]) #major, minor

ADDONID = 'plugin.program.exodus'
ADDON   =  xbmcaddon.Addon(ADDONID)
HOME    =  ADDON.getAddonInfo('path')
PROFILE =  os.path.join(ADDON.getAddonInfo('profile'), 'Super Favourites')
VERSION = '1.3.0'
ICON    =  os.path.join(HOME, 'icon.png')
FANART  =  os.path.join(HOME, 'fanart.jpg')
BLANK   =  os.path.join(HOME, 'resources', 'media', 'blank.png')
GETTEXT =  ADDON.getLocalizedString
TITLE   =  GETTEXT(30000)
KEYMAP  = 'super_favourites.xml'

MAJOR, MINOR = GetXBMCVersion()
GOTHAM       = (MAJOR == 13) or (MAJOR == 12 and MINOR == 9)

def DialogOK(line1, line2='', line3=''):
    d = xbmcgui.Dialog()
    d.ok(TITLE + ' - ' + VERSION, line1, line2 , line3)

def DialogYesNo(line1, line2='', line3=''):
    d = xbmcgui.Dialog()
    return d.yesno(TITLE + ' - ' + VERSION, line1, line2 , line3) == True

def Verify():
    CheckVersion()
    VerifyKeymap()

def CheckVersion():
    prev = ADDON.getSetting('VERSION')
    curr = VERSION

    if prev == curr:
        return

    ADDON.setSetting('VERSION', curr)

    if prev == '0.0.0':

        folder  = xbmc.translatePath(PROFILE)
        if not os.path.isdir(folder):
            os.makedirs(folder)

        VerifyKeymap()

def DeleteKeymap():
    path = os.path.join(xbmc.translatePath('special://userdata/keymaps'), KEYMAP)

    tries = 5
    while os.path.exists(path) and tries > 0:
        tries -= 1
        try:
            os.remove(path)
            break
        except:
            xbmc.sleep(500)

def VerifyKeymap():
    dest = os.path.join(xbmc.translatePath('special://userdata/keymaps'), KEYMAP)

    if os.path.exists(dest):
        return

    key = ADDON.getSetting('HOTKEY').lower()

    if key not in ['f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12']:
        DeleteKeymap()
        xbmc.sleep(1000)
        xbmc.executebuiltin('Action(reloadkeymaps)')
        return

    cmd = '<keymap><Global><keyboard><%s>XBMC.RunAddon(plugin.program.super.favourites)</%s></keyboard></Global></keymap>'  % (key, key)

    f = open(dest, mode='w')
    f.write(cmd)
    f.close()
    xbmc.sleep(1000)

    tries = 4
    while not os.path.exists(dest) and tries > 0:
        tries -= 1
        f = open(dest, mode='w')
        f.write(t)
        f.close()
        xbmc.sleep(1000)

    xbmc.executebuiltin('Action(reloadkeymaps)')

if __name__ == '__main__':
    pass


import abc
import datetime
import os
import shutil
import time

from grocsvs import log

class StepChunk(object):
    __metaclass__ = abc.ABCMeta

    @staticmethod
    def get_steps(options):
        """ """
        raise Exception("this abstract staticmethod needs to be instantiated by subclasses")

    @abc.abstractmethod
    def __init__(self, options, **kwdargs):
        """
        must take as input the options instance and a dict of arguments
        """
        pass

    @abc.abstractmethod
    def outpaths(self, final):
        """
        return a dictionary of names -> output paths; final indicates if the
        paths should be for the temporary, working version (final=False) or the
        completed final version (final=True)
        """
        return

    @abc.abstractmethod
    def run(self):
        """
        actually runs the pipeline step on the arguments, given the options,
        defined in __init__
        """
        pass

    def __str__(self):
        raise Exception("not implemented")

    @property
    def results_dir(self):
        return self._results_dir(self.options)#return os.path.join(self.options.results_dir, self.__class__.__name__)

    @classmethod
    def _results_dir(cls, options):
        # private method to allow classmethod-level access to results_dir
        return os.path.join(options.results_dir, cls.__name__)

    @property
    def working_dir(self):
        return self._working_dir(self.options)#os.path.join(self.options.working_dir, self.__class__.__name__)

    @classmethod
    def _working_dir(cls, options):
        return os.path.join(options.working_dir, cls.__name__)

    @property
    def log_path(self):
        return os.path.join(self.options.log_dir, self.__class__.__name__, str(self))

    def start_logging(self):
        self.logger = log.Logger(self.log_path)
        self.logger.log("-- starting logging {} --".format(str(self)))
        self._start_time = time.time()

    def stop_logging(self):
        elapsed = time.time() - self._start_time
        self.logger.log("-> finished running step; time elapsed: {}".format(datetime.timedelta(seconds=elapsed)))
        self.logger.log("-- stopping logging --")

    @classmethod
    def clean_all_steps(cls, options):
        import logging
        results_dir = cls._results_dir(options)
        if os.path.exists(results_dir):
            logging.info("Removing {}".format(results_dir))
            shutil.rmtree(results_dir)

        working_dir = cls._working_dir(options)
        if os.path.exists(working_dir):
            logging.info("Removing {}".format(working_dir))
            shutil.rmtree(working_dir)

        log_dir = os.path.join(options.log_dir, cls.__name__)
        if os.path.exists(log_dir):
            logging.info("Removing {}".format(log_dir))
            shutil.rmtree(log_dir)

    def needs_to_run(self):
        """ checks if any of the output files are missing """
        paths = self.outpaths(final=True)
        for name, path in paths.items():
            if not os.path.exists(path):
                return True

        return False

    def finalize(self):
        temp_paths = self.outpaths(final=False)
        final_paths = self.outpaths(final=True)

        for key, working_path in temp_paths.items():
            if working_path != final_paths[key]:
                if not os.path.exists(working_path):
                    raise Exception("{} step failed to produce output: {}".format(
                        self.__class__.__name__, working_path))
                shutil.move(working_path, final_paths[key])
                time.sleep(0.1)

        assert not self.needs_to_run()


from datetime import date

from fastapi import Depends, HTTPException, status
from fastapi_pagination import Params

from code.exceptions import NewsNotFound, UnsupportedSymbol
from code.services import CompanyNewsListService, NewsDetailsService, NewsListService

async def get_news(params: Params = Depends()):
    return await NewsListService(params).do()

async def get_news_by_symbol(
    symbol: str, params: Params = Depends(),
    date_from: date = None, date_to: date = None,
):
    service = CompanyNewsListService(symbol, params, date_from, date_to)

    try:
        return await service.do()
    except UnsupportedSymbol:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)

async def get_news_by_id(news_id: int):
    service = NewsDetailsService(news_id)

    try:
        return await service.do()
    except NewsNotFound:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND)