import os
from typing import Union, List

import h5py
import numpy as np
import torch

from .light_field_dataset import LightFieldDataset

class LightField6x6Dataset(LightFieldDataset):
    """
    Dataset wrapping images and targets for the low res inputs
    """

    def __init__(self,
                 dataset_loc: os.path,
                 testing_set_idx_stop: int,
                 testing_set_idx_start: int,
                 is_training: bool = True,
                 is_timing: bool = False,
                 is_x_cached: bool = False,
                 is_y_cached: bool = False,
                 data_key: str = 'images'):
        super(LightField6x6Dataset, self).__init__()
        self.is_x_cached = is_x_cached
        self.is_y_cached = is_y_cached
        self.dataset_loc = dataset_loc
        self.is_training = is_training
        self.is_timing = is_timing

        self.h5_file = h5py.File(self.dataset_loc, 'r')

        self.x_images = self.h5_file[data_key + '_x']

        self.y_images = self.h5_file[data_key + '_y']

        print("Found x dataset with shape {}".format(self.x_images.shape))
        print("Found y dataset with shape {}".format(self.y_images.shape))

        self.num_samples = self.x_images.shape[-5]
        self.num_channels = self.x_images.shape[-3]

        self.num_views_x = self.x_images.shape[-4]
        self.height_x = self.x_images.shape[-2]
        self.width_x = self.x_images.shape[-1]

        self.num_views_y = self.y_images.shape[-4]
        self.height_y = self.y_images.shape[-2]
        self.width_y = self.y_images.shape[-1]

        self.all_indices = np.arange(self.num_samples, dtype=np.int32)

        self.testing_indices = np.arange(testing_set_idx_start,
                                         testing_set_idx_stop,
                                         dtype=np.int32)

        self.training_indices = \
            np.array(list(set(self.all_indices) - set(self.testing_indices)))

        if self.is_training:
            self.dataset_type = "Train"
            self.image_indices = self.training_indices
        else:
            self.dataset_type = "Test"
            self.image_indices = self.testing_indices

        print("{} idxs {} ... {}".format(self.dataset_type,
                                         self.image_indices[:10],
                                         self.image_indices[-10:]))

        if self.is_x_cached:
            print("Caching x dataset please wait...")
            self.x_images = self.x_images[self.image_indices.tolist()]

        if self.is_y_cached and not self.is_timing:
            print("Caching y dataset please wait...")
            self.y_images = self.y_images[self.image_indices.tolist()]

        self.item_fn = self.calc_item_fn()

    def calc_item_fn(self):
        if self.is_x_cached and self.is_y_cached:
            item_fn = self._get_sample_x_y_cached
        elif self.is_x_cached:
            item_fn = self._get_sample_x_cached
        elif self.is_y_cached:
            item_fn = self._get_sample_y_cached
        else:
            item_fn = self._get_sample_no_cached

        return item_fn

    def set_only_x_dataset(self):
        if not self.is_x_cached:
            print("Caching x dataset please wait...")
            self.x_images = self.x_images[self.image_indices.tolist()]
            self.is_x_cached = True

        self.item_fn = self._get_only_x

    def revert_only_x_dataset(self):
        self.item_fn = self.calc_item_fn()

    def _get_only_x(self, idx):
        return self.x_images[idx]

    def get_only_y(self, idx):
        if self.is_y_cached:
            image_idx = idx
        else:
            image_idx = self.image_indices[idx]

        return self.y_images[image_idx]

    def __len__(self):
        return self.image_indices.shape[0]

    def _get_sample_x_cached(self, idx):
        image_idx_y = self.image_indices[idx]

        return self.x_images[idx], self.y_images[image_idx_y]

    def _get_sample_y_cached(self, idx):
        image_idx_x = self.image_indices[idx]

        return self.x_images[image_idx_x], self.y_images[idx]

    def _get_sample_x_y_cached(self, idx):
        return self.x_images[idx], self.y_images[idx]

    def _get_sample_no_cached(self, idx):
        image_idx = self.image_indices[idx]

        return self.x_images[image_idx], self.y_images[image_idx]

    def _get_sample_timing_cached(self, idx):
        return self.x_images[idx]

    def __getitem__(self, idx):
        return self.item_fn(idx=idx)

    def get_x_images(self, to_list=True):
        if self.is_x_cached:
            if to_list:
                return self.x_images.tolist()
            else:
                return self.x_images
        else:
            print("Caching x dataset please wait...")
            if to_list:
                return self.x_images[self.image_indices.tolist()].tolist()
            else:
                return self.x_images[self.image_indices.tolist()]

    def get_y_images(self, to_list: bool = True) -> Union[np.ndarray, List]:
        if self.is_x_cached:
            if to_list:
                return self.x_images.tolist()
            else:
                return self.x_images
        else:
            print("Caching y dataset please wait...")
            if to_list:
                return self.x_images[self.image_indices.tolist()].tolist()
            else:
                return self.x_images[self.image_indices.tolist()]

    def fast_collate(self, batch):
        input_lfs = [lf[0] for lf in batch]
        output_lfs = [lf[1] for lf in batch]

        input_batch_tensor = torch.empty(
            (len(input_lfs),
             self.num_views_x,
             self.num_channels,
             self.height_x,
             self.width_x),
            dtype=torch.uint8,
            requires_grad=False
        )

        for idx, input_lf in enumerate(input_lfs):
            input_batch_tensor[idx] = torch.from_numpy(input_lf)

        output_batch_tensor = torch.empty(
            (len(output_lfs),
             self.num_views_y,
             self.num_channels,
             self.height_y,
             self.width_y),
            dtype=torch.uint8,
            requires_grad=False
        )

        for idx, output_lf in enumerate(output_lfs):
            output_batch_tensor[idx] = torch.from_numpy(output_lf)

        return input_batch_tensor, output_batch_tensor


from sklearn import preprocessing  # import StandardScaler
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
import numpy as np
from sklearn import linear_model

# (a)
X = [4.0,5.0,5.6,6.8,7.0,7.2,8.0,0.8,1.0,1.2,2.5,2.6,3.0,4.3]
X = np.asarray(X).transpose()
t = [1,1,1,1,1,1,1,0,0,0,0,0,0,0]
t = np.asarray(t).transpose().reshape(len(t),1)

standardized_X = preprocessing.scale(X)

plt.scatter(standardized_X,t,color='green')

# (b)
xtest = np.arange(-1.5,1.5,0.1)
xtest_matrix = xtest.reshape(len(xtest),1)

# ridge regression
ridgeModel = linear_model.Ridge(alpha=0.1)
ridgeModel.fit(standardized_X.reshape(len(X),1),t)
predRidge = ridgeModel.predict(xtest_matrix)

# logistic regression L2 regurlization

lr = LogisticRegression(C=10)
lrModel = lr.fit(standardized_X.reshape(len(X),1),np.ravel(t))

predLog = lrModel.predict(xtest_matrix)
plt.plot(xtest,predRidge,'r')
plt.plot(xtest,predLog,'y')
plt.title('problem2 part(b) plot')
plt.show()

print('w1 for logit is: ',lrModel.coef_)
print('w0 for logit is: ',lrModel.intercept_)
print('w1 for ridge is: ',ridgeModel.coef_)
print('w0 for ridge is: ',ridgeModel.intercept_)

# (c)
new_a = np.append(X,12)
new_a = preprocessing.scale(new_a)
x_new = np.asarray(new_a).transpose()

new_b = np.append(t,1)
t_new = np.asarray(new_b).transpose()

### new ridge regression
ridgeModel2 = linear_model.Ridge(alpha=0.1)
ridgeModel2.fit(x_new.reshape(len(X)+1,1),np.ravel(t_new))

predRidge2 = ridgeModel2.predict(xtest_matrix)
plt.plot(xtest,predRidge2,'b')

# logistic regression L2 regurlization

lr2 = LogisticRegression(C=10)
lrModel2 = lr2.fit(x_new.reshape(len(X)+1,1),t_new)

predLog2 = lrModel2.predict(xtest_matrix)
plt.scatter(x_new,t_new,color='green')
plt.plot(xtest,predLog2,'g')
plt.title('problem2 part(c) plot')
plt.show()

print('w1 for logit is: ',lrModel2.coef_)
print('w0 for logit is: ',lrModel2.intercept_)
print('w1 for ridge is: ',ridgeModel2.coef_)
print('w0 for ridge is: ',ridgeModel2.intercept_)


#! /usr/bin/env python
import os, sys
import shutil
import subprocess
import json, glob

class NotebookFinder(object):
    """
    Given the path to a notebook.json file, locate the notebooks
    given the supplied command-line options (projects and suites).
    """

    def __init__(self, spec_path, projects=[], suites=[]):
        spec = json.load(open(spec_path, 'r'))
        # Assuming file in doc/nbpublisher
        root = os.path.abspath(os.path.join(__file__, '..', '..', '..'))
        # Get current project name from spec
        project = [k for k in spec.keys() if k != 'links'][0]
        # If not projects specified, test current project
        projects = [project] if not projects else projects
        # Dictionary of root directories by project
        root_dirs = self.root_directories(root, spec, projects)

        # Relative to top level of each project
        self.spec = self.expand_spec(spec, root_dirs)
        self.files = self.expand_paths(self.spec, root_dirs, suites)

    def root_directories(self, root, spec, projects):
        """
        Return a dictionary of root directories for the required
        projects.
        """
        root_dirs = {}
        for project in projects:
            relpath =  spec['links'].get(project, False)
            if relpath:
                link_path = os.path.abspath(os.path.join(root, relpath))
                path = link_path
            elif project in spec:
                path = root
            elif relpath is False:
                continue

            if not os.path.isdir(path):
                raise Exception("Invalid link path: %s" % link_path)
            root_dirs[project] = path

        return root_dirs

    def expand_spec(self, spec, root_dirs, location=['doc', 'notebook.json']):
        """
        Follow the links to other notebook.json files to create an
        expanded specification dictionary with all the projects to
        test.
        """
        spec = dict((k,v) for (k,v) in spec.items() if k != 'links')
        for name, root in root_dirs.items():
            specpath = os.path.join(root,  *location)
            if (name not in spec) and not os.path.isfile(specpath):
                print("Could not find %r in %s" % (os.path.sep.join(location), root))
            elif (name not in spec):
                spec[name] = json.load(open(specpath, 'r'))[name]
        return spec

    def expand_paths(self, spec, root_dirs, suites=[]):
        """
        Using the expanded spec, find all the specified notebooks,
        filtering by the specified suites (if specified).
        """
        files = []
        for project, group in spec.items():
            selection = [s for s in suites if s in group]
            selection = selection if suites else group.keys()
            for suite in selection:
                key = (str(project), str(suite))
                group_list = []
                for relpath in group[suite]:
                    pattern = os.path.join(root_dirs[project], relpath)
                    for match in glob.glob(pattern):
                        group_list.append(str(match))
                files.append((key, group_list))
        return files

def switch_reference_branch(ref_dir, project):
    # Not yet implemented...
    pass

def run_notebook_test(notebook, project, suite, ref_dir, test_dir, regen=False):
    path, filename = os.path.split(__file__)
    test_script = os.path.abspath(os.path.join(path, 'nbtest.py'))

    notebook_name =  os.path.split(notebook)[1]
    if not notebook_name.endswith('.ipynb'):
        print("Not an IPython notebook (%s)...skipping." % notebook_name)
        return 0

    py_version =  "_py%d" % sys.version_info[0]
    ref_dir = os.path.join(ref_dir, project + '_' + notebook_name[:-6] + py_version)
    test_dir = os.path.join(test_dir, project + '_' + notebook_name[:-6] + py_version)
    if regen and os.path.isdir(ref_dir):  shutil.rmtree(ref_dir)

    cmds = ['ipython', test_script, notebook, ref_dir, test_dir, str(regen)]
    proc = subprocess.Popen(cmds,
                            stderr=subprocess.PIPE,
                            cwd=os.path.split(notebook)[0])
    _,stderr = proc.communicate()
    print(str(stderr.decode()))
    for idx, line in enumerate(stderr.splitlines()[-10:]):
        error = 'FAILED (errors=' in str(line)
        failure = 'FAILED (failures=' in str(line)
        if error or failure:
            return 1
    return proc.returncode

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('projects', nargs='*', default=[])
    parser.add_argument('-s', '--suites', nargs='*', default=[])
    parser.add_argument('-p', '--paths', nargs='*')
    parser.add_argument('-r', '--regen', action="store_true")
    args = parser.parse_args()

    if args.paths is not None:
        assert len(args.projects) == 1
        file_list = [((args.projects[0], "custom"), args.paths)]
    else:
        file_list = NotebookFinder('../notebook.json', projects=args.projects,
                                   suites=args.suites).files

    ref_dir = os.path.abspath(os.path.join(__file__, '..', '..','reference_data'))
    test_dir = os.path.abspath(os.path.join(__file__, '..', '..','test_data'))

    if not os.path.isdir(ref_dir):
        raise Exception("No reference directory: %s" % ref_dir)
    if not os.path.isdir(test_dir):
        raise Exception("No test directory: %s" % test_dir)

    retcode = 0
    for (project, suite), paths in sorted(file_list):
        for path in paths:
            switch_reference_branch(ref_dir, project)
            retcode |= run_notebook_test(path, project, suite, ref_dir, test_dir, regen=args.regen)
    sys.exit(retcode)


#! python
'''A simple way to run your python code from the CLI
The module uses introspection to try and expose your code to the command line. It won't work in all cases, it depends on the complexity of your code.

This is the executable part.
'''

import logging
import sys

LOGGER = logging.getLogger(__name__)

try:
	import simplifiedapp
except ModuleNotFoundError:
	import __init__ as simplifiedapp

target, sys_argv = simplifiedapp, []
if len(sys.argv) > 1:
	if ((len(sys.argv[1]) == 2) and sys.argv[1].startswith('-')) or sys.argv[1].startswith('--'):
		sys_argv = sys.argv[1:]
	elif len(sys.argv) > 2:
		target = sys.argv[1]
		sys_argv = sys.argv[2:]

simplifiedapp.main(target = target, sys_argv = sys_argv)


import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import pandas as pd
import random
import glob
import re
import os
import seaborn as sns
import scipy.stats as stats

# Function, ........................................................................................
def find_columns_with_selected_dtype(*, df, dtype="numerical"):
    """
        =================   ===============================================================================
        Property            Description
        =================   ===============================================================================

        * Function          Finds column names of columns with requested dtype numeric or text,

        Parameters/Input
        _________________   _______________________________________________________________________________

        . Input .
        * df                DataFrame, with unique column names
        * dtype             data type that we with to find {'text', 'numeric'}

        Returns
        _________________   _______________________________________________________________________________

        * col_names         list wiht column names, that have eaither numeric or text dtape
    """
    col_names = []

    if dtype=="numerical":
        from pandas.api.types import is_numeric_dtype # if is_string_dtype(df[j]):
        for col_name in list(df.columns):
            if is_numeric_dtype(df[col_name]):
                col_names.append(col_name)

    if dtype=="text":
        from pandas.api.types import is_string_dtype # if is_string_dtype(df[j]):
        for col_name in list(df.columns):
            if is_string_dtype(df[col_name]):
                col_names.append(col_name)

    return col_names

# Function, ..............................................................................................

def clean_data_and_find_correlations(*, df, row_Filter=None, colName_main_variable, verbose=False):

    """
        =================   ===============================================================================
        Property            Description
        =================   ===============================================================================

        * Function          This function, takes df, with numeric data in each column,
                            computes all column to one of them, called colName_main_variable
                            the data are formatted and cleaned in each pari of column individually, (by removing na)

        Parameters/Input
        _________________   _______________________________________________________________________________

        . Input .
        * df                DataFrame, with unique column names
        * row_filter        list[bool], with len(row_filter )==df.shape[0]
        * verbose           bool, if True, shows info

        Returns
        _________________   _______________________________________________________________________________

        * comparisons_dct   dictionary, where key=column name in df,
                            inside each entry, there is another dict wiht condition names, raw and filtered data
                            and results of correlation made with 3 different methods (pearson, spearman and kendal)

    """

    #### filter the data:
    if row_Filter==None:
        row_Filter = [True]*df.shape[0]
    # ........
    filtered_df_main_col    = pd.Series(df.loc[row_Filter,colName_main_variable]) # pd.Series
    filtered_df             = df.loc[row_Filter, :]                               # pd Series or pd.DataFrame

    #### rename column with main group to avoid having duplicates,
    filtered_df.rename(columns={str(colName_main_variable):"".join([str(colName_main_variable),"_"])}, inplace=True) # to ensute, that we have no columns with the same name,

    #### Loop over each columns and compare it with main group,
    comparisons_dct = dict()
    # .......
    for i, colName in enumerate(list(filtered_df.columns)):

        if verbose==True:
            print(i, colName_main_variable, " - with - ", colName)

        # some columns had to many repeats r nan. i remove them later, but warningn  were annoying, thus I removed them;
        import warnings
        warnings.filterwarnings("ignore", category=RuntimeWarning)

        # --------------------------
        # Prepare dct for the data,
        # --------------------------

        # new dict,
        one_comparison_dct = dict()
        one_comparison_dct["X_main_group_sliced_with"]    = row_Filter
        one_comparison_dct["X_main_group"]                = colName_main_variable
        one_comparison_dct["Y_compared_with"]             = colName

        # --------------------------
        # PREPARE THE DATA
        # --------------------------

        # ................... raw data, .........................

        # prepare the data,
        filtered_df_one_col         = filtered_df.loc[:,colName]
        data_for_comparison_df      = pd.concat([filtered_df_main_col, filtered_df_one_col], axis=1)
        data_for_comparison_df_full = data_for_comparison_df.copy() # for hitograms

        # remove missing data,
        data_for_comparison_df      = data_for_comparison_df.dropna(how="any",axis=0) # to create X/Y_cleaned

        # names, and basic data to display,
        sample_number               = int(data_for_comparison_df.shape[0])  # int
        atribute_names              = list(data_for_comparison_df.columns) # list

        # ................... control, ............................

        # check if you can continue,
        if sample_number <= 2:
            # add to dict,
            one_comparison_dct["X_total"]                = [None]
            one_comparison_dct["X_cleaned"]              = [None]
            one_comparison_dct["X_cleaned_log"]          = [None]
            # .......
            one_comparison_dct["Y_total"]                = [None]
            one_comparison_dct["Y_cleaned"]              = [None]
            one_comparison_dct["Y_cleaned_log"]          = [None]
            # .......
            one_comparison_dct["pearson_results"]        = [None]
            one_comparison_dct["sperman_results"]        = [None]
            one_comparison_dct["kendalltau_results"]     = [None]
            # ..........
            one_comparison_dct["linregress_results"]     = [None]
            one_comparison_dct["linregress_results_log"] = [None]

            if verbose==True:
                print(f"Caution, (column cobination nr {i}) -ie.- {atribute_names[0]},vs{atribute_names[1]}, has less then 3 items to compare !")
            ############################################################################
            comparisons_dct[colName] = one_comparison_dct
            ############################################################################

        # else,
        if sample_number > 2:

            # ... X,Y data for plots and correlation, ................

            # all data, without removing NaN in each row, - for hist,
            X_total = data_for_comparison_df_full.iloc[:, 0]
            X_total = X_total.dropna(how="any").values.flatten()
            Y_total = data_for_comparison_df_full.iloc[:, 1]
            Y_total = Y_total.dropna(how="any").values.flatten()

            # data,
            X_cleaned = data_for_comparison_df.iloc[:, 0].values.flatten()
            Y_cleaned = data_for_comparison_df.iloc[:, 1].values.flatten()

            # transform values into log(x+2), +2 to avoid having log= inf, or zero
            X_cleaned_log = np.log(X_cleaned+16)
            Y_cleaned_log = np.log(Y_cleaned+16)

            # .....

            # add to dict,
            one_comparison_dct["X_total"]       = X_total
            one_comparison_dct["X_cleaned"]     = X_cleaned
            one_comparison_dct["X_cleaned_log"] = X_cleaned_log
            # .......
            one_comparison_dct["Y_total"]       = Y_total
            one_comparison_dct["Y_cleaned"]     = Y_cleaned
            one_comparison_dct["Y_cleaned_log"] = Y_cleaned_log

            # --------------------------
            # FIND CORR.
            # --------------------------

            # ... Correlation, ....................................

            # correlations,
            pearson_results    = stats.pearsonr(X_cleaned_log, Y_cleaned_log)   # linear
            sperman_results    = stats.spearmanr(X_cleaned, Y_cleaned)          # rank, with rho value,
            kendalltau_results = stats.kendalltau(X_cleaned, Y_cleaned)   # rank,based on orientation of pairs of ranks
            # ............
            one_comparison_dct["pearson_results"]      = pearson_results
            one_comparison_dct["sperman_results"]      = sperman_results
            one_comparison_dct["kendalltau_results"]   = kendalltau_results

            # Compute a least-squares regression for two sets of measurements.
            LR_slope, LR_intercept, LR_r_value, LR_p_value, LR_std_err = stats.linregress(X_cleaned, X_cleaned)
            # ..........
            linregress_results = {"slope": LR_slope,
                                      "intercept": LR_intercept,
                                      "r_value": LR_r_value,
                                      "p_value": LR_p_value,
                                      "std_err": LR_std_err}
            # ..........
            one_comparison_dct["linregress_results"] = linregress_results

            # Compute a least-squares regression for two sets of measurements.
            LR_slope, LR_intercept, LR_r_value, LR_p_value, LR_std_err = stats.linregress(X_cleaned_log, Y_cleaned_log)
            # ..........
            linregress_results_log = {"slope": LR_slope,
                                      "intercept": LR_intercept,
                                      "r_value": LR_r_value,
                                      "p_value": LR_p_value,
                                      "std_err": LR_std_err}
            # ..........
            one_comparison_dct["linregress_results_log"] = linregress_results_log

            ############################################################################
            comparisons_dct[colName] = one_comparison_dct
            ############################################################################

    return comparisons_dct

# Function, ..............................................................................................

def make_plots_and_table_with_corr(*, data_dct, N_plots_to_show=5, display_table=True, display_plots=True,
                                   N_plots_from=0, N_plots_to=2, figure_size= (12, 4), N_rows_in_table_to_show=10,
                                   return_rable=False, select_group_name=None, verbose=False):

    """
        =================   ===============================================================================
        Property            Description
        =================   ===============================================================================

        * Function          Larger function that makes four things:
                            - takes data_dct ie. dictionary generated with clean_data_and_find_correlations()
                              it collects and order all the data, in df
                            - if display_plots=True, display requested number of plots wiht scatter plot
                              and LR line representing relationship etwen compared values, and two histogram plots,
                              showsing distribusion of values in a given variable before and sfter selection (na removal)
                              required for calculating correlation between these values
                            - if return_rable=False, it will return a df, with all values such as rvalua and tau value
                              that were collected form the dictionary,
                            - if display_table=True, it will display, top number of requested correlations with the hisest absolute values
                              (either negative or positive)

        Parameters/Input
        _________________   _______________________________________________________________________________

        . Input .
        * data_dct          distionary, created with clean_data_and_find_correlations()
        * display_plots     bool, =True, see above
        * N_plots_from      int, =0, from whereto start displaying plots, 1st pair (0), is a correlation between main value itself
        * N_plots_to        int, =3, how many plots fdo display, not included,
        * figure_size       tuple(int, int)
        * return_rable      bool, =False, see above
        * select_group_name str, depreciated....
        * N_rows_in_table_to_show=10,  : if display_table=True, how many rows to display,
        * verbose           bool, =False

        Returns
        _________________   _______________________________________________________________________________

        * ≥1 plots          each with 3 subplots, see above
        * df image          df.head(), see above
        * df                unordered, df with data used for plots, in all conditiona, even if not displayed.
    """

    # collect and order the data from dict,
    dct_keys          = list(data_dct.keys())
    dct_rvalues       = list()
    dct_pvalues       = list()
    dct_rhovalues     = list()
    dct_tauvalues     = list()
    dct_sample_number = list()
    # .......
    for i, key in enumerate(dct_keys):
        if data_dct[key]["X_total"][0]!=None:
            dct_rvalues.append(data_dct[key]["pearson_results"][0])
            dct_pvalues.append(data_dct[key]["pearson_results"][1])
            dct_rhovalues.append(data_dct[key]["sperman_results"][0])
            dct_tauvalues.append(data_dct[key]["kendalltau_results"][0])
            dct_sample_number.append(data_dct[key]["X_cleaned"].shape[0])

        else:
            dct_rvalues.append(np.nan)
            dct_pvalues.append(np.nan)
            dct_rhovalues.append(np.nan)
            dct_sample_number.append(np.nan)
            dct_tauvalues.append(np.nan)

    # ....... join them into df,
    order_in_df = pd.concat([pd.Series(dct_keys),
                             pd.Series(dct_rvalues),
                             pd.Series(dct_pvalues),
                             pd.Series(dct_rhovalues),
                             pd.Series(dct_tauvalues),
                             pd.Series(dct_sample_number)
                            ],axis=1) # to use functions below

    # ...... re-name,
    order_in_df.columns=["keys", "rvalues", "pvalues", "rho", "tau", "sample nr"] # for my info

    # ...... save to return
    df_with_all_results_unordered = order_in_df.copy()

    # ...... re-order, to find the
    order_in_df["rvalues_abs"] = pd.Series(np.abs(order_in_df.iloc[:,1].values)) # use absolute values
    order_in_df.sort_values(by="rvalues_abs", ascending=False, inplace=True) # to get tghe order

    # --------------------------
    # DATA FOR PLOTS
    # --------------------------
    if display_plots==True:

        # show plots, of N top values
        for i,key in enumerate(list(order_in_df.loc[:,"keys"])):

            if i >= N_plots_from and i<=N_plots_to:

                # --------------------------
                # DATA FOR PLOTS
                # --------------------------

                X_cleaned_log = data_dct[key]["X_cleaned_log"]
                Y_cleaned_log = data_dct[key]["Y_cleaned_log"]
                # ......
                X_cleaned = data_dct[key]["X_cleaned"]
                Y_cleaned = data_dct[key]["Y_cleaned"]
                # ......
                X_total = data_dct[key]["X_total"]
                Y_total = data_dct[key]["Y_total"]

                # ......
                atribute_names = [data_dct[key]["X_main_group"], data_dct[key]["Y_compared_with"]]

                # .....
                LR_intercept = data_dct[key]["linregress_results_log"]["intercept"]
                LR_slope = data_dct[key]["linregress_results_log"]["slope"]
                LR_r_value = data_dct[key]["linregress_results_log"]["r_value"]
                LR_p_value = data_dct[key]["linregress_results_log"]["p_value"]

                # .....
                pearson_results = data_dct[key]["pearson_results"]

                # INFO:
                if verbose==True:
                    print(i, key, atribute_names)

                # --------------------------
                # PLOTS
                # --------------------------

                # ... fig, ax, .............................................................................

                fig, axs = plt.subplots(nrows=1, ncols=3, figsize=figure_size, facecolor="white")

                if select_group_name==None:
                    plt.suptitle(f"[{i}] {atribute_names[0]} - vs - {atribute_names[1]}", fontsize=17)
                else:
                    plt.suptitle(f"[{i}] {atribute_names[0]} - vs - {atribute_names[1]}, ({select_group_name})", fontsize=17)

                # colors
                selected_data_color = "deepskyblue"
                total_data_color    = "black"
                scatter_color       = "forestgreen"
                line_color          = "black"

                # ... SUBPLOT 1, scatterplot ..............................................................

                # data,

                X = X_cleaned_log
                Y = Y_cleaned_log

                # title,
                subplot_title = f'Correlation and LR:\nr = {LR_r_value:.2f}, p = {np.round(list(pearson_results)[1],3)}\ny = {LR_intercept:.2f}+{LR_slope:.2f}x'

                # plot and add line
                axs[0].plot(X, Y, linewidth=0, marker='.', color=scatter_color )
                axs[0].plot(X, LR_intercept + LR_slope * X, color=line_color, linestyle="--", linewidth=3)
                axs[0].set_title(subplot_title)

                # axes labels
                axs[0].set_xlabel(str(atribute_names[0])+"\nlog values")
                axs[0].set_ylabel(str(atribute_names[1])+"\nlog values")
                axs[0].set_xlim(X.min(), X.max())
                axs[0].set_ylim(Y.min(), Y.max())

                # Remove ticks, and axes that you dot'n want, format the other ones,
                axs[0].yaxis.set_ticks_position('left')# shows only that
                axs[0].xaxis.set_ticks_position('bottom')# shows only that
                axs[0].spines['top'].set_visible(False) # remove ...
                axs[0].spines['right'].set_visible(False) # remove ...
                axs[0].spines['bottom'].set_linewidth(1) # x axis width
                axs[0].spines['left'].set_linewidth(1) # y axis width

                # grid and labels,
                axs[0].xaxis.grid(True,color='grey', linestyle='--', linewidth=0.5)
                axs[0].yaxis.grid(True,color='grey', linestyle='--', linewidth=0.5)

                #................................................................................
                # ... kwars for histograms ......................................................
                hist_kwargs         = dict(histtype="stepfilled", density=True, bins=20)
                # ... kwars for histograms ......................................................
                #................................................................................

                # ... SUBPLOT 2, X data histogram ....................................

                # title,
                perc_value = np.round((X_cleaned.shape[0]/X_total.shape[0])*100,2)
                subplot_title = f"{X_total.shape[0]}, non-NaN items in df\n{X_cleaned.shape[0]} items used for Corr ({perc_value}%)"

                # plot and add line
                axs[1].hist(X_total, color=total_data_color,edgecolor=total_data_color, alpha=0.6, **hist_kwargs, label="all data")
                axs[1].hist(X_cleaned, color=selected_data_color,edgecolor=selected_data_color, alpha=0.5, **hist_kwargs, label="data used for LR")
                axs[1].set_title(subplot_title)

                # axes labels
                axs[1].set_xlabel(str(atribute_names[0]))
                axs[1].set_ylabel("Density")

                # recompute x/y limits
                axs[1].relim() # recompute the ax.dataLim
                axs[1].autoscale_view() # update ax.viewLim using the new dataLim

                # Remove ticks, and axes that you dot'n want, format the other ones,
                axs[1].yaxis.set_ticks_position('left')# shows only that
                axs[1].xaxis.set_ticks_position('bottom')# shows only that
                axs[1].spines['top'].set_visible(False) # remove ...
                axs[1].spines['right'].set_visible(False) # remove ...
                axs[1].spines['bottom'].set_linewidth(1) # x axis width
                axs[1].spines['left'].set_linewidth(1) # y axis width

                # ... SUBPLOT 3, Y data histogram ....................................

                # title,
                perc_value = np.round((Y_cleaned.shape[0]/Y_total.shape[0])*100,2)
                subplot_title = f"{Y_total.shape[0]}, non-NaN items in df\n{Y_cleaned.shape[0]} items used for Corr ({perc_value}%)"

                # plot and add line
                axs[2].hist(Y_total, color=total_data_color,edgecolor=total_data_color, alpha=0.6, **hist_kwargs, label= "all data")
                axs[2].hist(Y_cleaned, color=selected_data_color,edgecolor=selected_data_color, alpha=0.5, **hist_kwargs, label="data used for LR")
                axs[2].set_title(subplot_title)

                # axes labels
                axs[2].set_xlabel(str(atribute_names[1]))
                axs[2].set_ylabel("Density")

                # recompute x/y limits
                axs[2].relim() # recompute the ax.dataLim
                axs[2].autoscale_view() # update ax.viewLim using the new dataLim

                # Remove ticks, and axes that you dot'n want, format the other ones,
                axs[2].yaxis.set_ticks_position('left')# shows only that
                axs[2].xaxis.set_ticks_position('bottom')# shows only that
                axs[2].spines['top'].set_visible(False) # remove ...
                axs[2].spines['right'].set_visible(False) # remove ...
                axs[2].spines['bottom'].set_linewidth(1) # x axis width
                axs[2].spines['left'].set_linewidth(1) # y axis width

                # .. create patch list for legend, ..................

                # . import
                import matplotlib.patches as mpatches
                patch_list_for_legend =[]

                # .1.
                ingr_patch      = mpatches.Patch(color=total_data_color, label=f"all available data")
                patch_list_for_legend.append(ingr_patch)

                # .2.
                ingr_patch      = mpatches.Patch(color=selected_data_color, label=f"data used for Corr(X,Y), and LR")
                patch_list_for_legend.append(ingr_patch)

                # . Add legend to the figure
                l = fig.legend(handles=patch_list_for_legend, loc="center", frameon=False, scatterpoints=1, ncol=2, bbox_to_anchor=(0.5, 0.82), title="", fontsize=12)
                l.set_title("histograms with:")
                l.get_title().set_fontsize('12')

                # ... FIG aestetics ....................................
                sns.despine()
                fig.tight_layout()
                fig.subplots_adjust(top=0.6)# to adjust for main title, amd legend
                plt.show();

    # .... DISPLAY TABLE WITH MANY RESULTS BELOW PLOTS, .................
    if display_table == True:

        # remove unwanted
        df_with_corr_results_ordered = order_in_df.drop(["rvalues_abs"], axis=1)
        df_with_corr_results_ordered.reset_index(drop=True, inplace=True)

        from IPython.display import display
        print(f"""{"".join(["-"]*80)}\nTable with all corelations, \n{"".join(["-"]*80)}""")
        display(df_with_corr_results_ordered.head(N_rows_in_table_to_show))

    # ... RETURN .................................... I PLACED IT HERE TO NOT WRITE ANY FUNCTIONS AGAIN !
    if return_rable == True:

        return df_with_all_results_unordered

# Function, ..............................................................................................
def corr_heatmap(*, corr_res_dct, method="rvalues"):
    """
        =================   ===============================================================================
        Property            Description
        =================   ===============================================================================

        * Function          plots a histogram with correlation values generated for multiple comparisons
                            clean_data_and_find_correlations(), and then with make_plots_and_table_with_corr()

        Parameters/Input
        _________________   _______________________________________________________________________________

        . Input .
        * corr_res_dct      pd dataFrame, rows = condition name,
                            make_plots_and_table_with_corr() from dict, generated with clean_data_and_find_correlations()
        * method            str, {'rvalues', 'rho', 'tau'}

        Returns
        _________________   _______________________________________________________________________________

        * heatmap

    """

    #### collect and transpose values for heatmap, .................................................

    # . loop
    for i, key in enumerate(list(corr_res_dct.keys())):

        # extract,
        corr_table_one_col         = corr_res_dct[key].loc[:,method].copy()
        corr_table_one_col_for_na  = corr_res_dct[key].loc[:,method]

        # replace na with zero (to simplify histogram making),
        corr_table_one_col[corr_table_one_col_for_na.isna()] = 0

        # add to final table
        if i==0:
            corr_table      =  corr_table_one_col
        else:
            corr_table      = pd.concat([corr_table, corr_table_one_col], axis=1)

    # . add index to df,
    corr_table["variable"]  = pd.Series(list(corr_res_dct[key]["keys"]))
    corr_table.set_index("variable", inplace=True)

    # . add column names,
    corr_table.columns = list(corr_res_dct.keys())

    # . re-order the rows, for hist:
    row_order          = pd.Series(corr_table.sum(axis=1))
    row_order          = list(row_order.sort_values(ascending=False).index)
    row_order
    # . .......
    corr_table         = corr_table.loc[row_order,:]

    # . transpose, to have better layout of a heatmap
    corr_table        = corr_table.transpose()

    #### create annotated heatmap, ..............................................................

    # .. figure,
    sns.set_context(None)
    fig, ax = plt.subplots(nrows=1, ncols=1, figsize=(20,10), facecolor="white")
    im = plt.imshow(corr_table.values, aspect = 1, origin="upper", interpolation='nearest', cmap="seismic")
    fig.suptitle(f"Heatmap with correlation results: {method}", fontsize=20)

    # . We want to show all ticks...
    ax.set_xticks(list(range(corr_table.shape[1])))
    ax.set_yticks(list(range(corr_table.shape[0])))

    # . and label them with the respective list entries
    ax.set_xticklabels(list(corr_table.columns))
    ax.set_yticklabels(list(corr_table.index))

    # . Rotate the tick labels and set their alignment.
    plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor", fontsize=15)
    plt.setp(ax.get_yticklabels(), fontsize=15)

    # . Remove ticks, and axes that you dot'n want, format the other ones,
    ax.spines['top'].set_visible(False) # remove ...
    ax.spines['right'].set_visible(False) # remove ...
    ax.yaxis.set_ticks_position('none')# shows only that
    ax.xaxis.set_ticks_position('none')# shows only that

    # .. spine description
    ax.set_ylabel("PNNS group", fontsize=20)
    ax.set_xlabel("Variable", fontsize=20)

    # colorbar, .......................................................................................

    # . create an axes on the right side of ax. The width of cax will be 5%
    #   of ax and the padding between cax and ax will be fixed at 0.05 inch.
    from mpl_toolkits.axes_grid1 import make_axes_locatable
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("top", size="10%", pad=0.5)
    clb = plt.colorbar(im, cax=cax, orientation="horizontal", label="Cor(X,Y), r value", extend="both")

    # control the stuff,
    fig.tight_layout()
    fig.subplots_adjust(top=0.85)# to adjust for main title, amd legend
    plt.show()


from utreview import int_or_none
from utreview.models.others import ScheduledCourse

class ScheduledCourseInfo:
	"""
	Class used to parse and filter information from dictionary containing ScheduledCourse data
	"""
	def __init__(self, s_course):
		"""
		initialize ScheduledCourseInfo with dictionary info
		:param s_course: dictionary containing ScheduledCourse data.
		:type s_course: dict{
			"Year": str,
			"Semester": str,
			"Dept-Abbr": str,
			"Course Nbr": str,
			"Title": str,
			"Topic": str,
			"Instructor EID": str,
			"Unique": str,
			"Days": str,
			"From": str,
			"To": str,
			"Building": str,
			"Room": str,
			"Max Enrollment": str,
			"Seats Taken": str,
			"X-Listings": str
		}
		"""
		# sem info
		self.yr = int(s_course["Year"].strip())
		self.sem = int(s_course["Semester"].strip())

		# course info
		self.dept = s_course["Dept-Abbr"].strip().upper()
		c_num_raw = s_course["Course Nbr"].strip().upper()
		self.title = s_course["Title"].strip()
		self.topic = s_course["Topic"].strip()

		try:
			self.topic = int(self.topic)
		except ValueError:
			self.topic = -1

		if not c_num_raw:
			# print("Populate scheduled course: no course number. Skipping...")
			raise ValueError("No course number.")

		self.c_num = c_num_raw[1:] if c_num_raw[0].isalpha() else c_num_raw

		# prof info
		self.prof_eid = s_course["Instructor EID"].strip().lower()

		# scheduled info
		self.session = c_num_raw[0] if c_num_raw[0].isalpha() else None
		self.unique_no = s_course["Unique"].strip()
		try:
			self.unique_no = int(self.unique_no)
		except ValueError:
			raise ValueError("No unique number.")

		self.days = s_course["Days"].strip().upper()
		self.time_from = int_or_none(s_course["From"].strip())

		self.time_to = int_or_none(s_course["To"].strip())
		building = s_course["Building"].strip().upper()
		room = s_course["Room"].strip().upper()
		self.location = parse_location(self.title, building, room)
		self.max_enrollment = int_or_none(s_course["Max Enrollment"].strip())
		self.seats_taken = int_or_none(s_course["Seats Taken"].strip())
		self.x_listings = [listing.strip() for listing in s_course["X-Listings"].strip().split(",")]

	def to_scheduled_course(self, scheduled_course, semester, course, prof, x_list):
		"""
		Populate a ScheduledCourse with data from this class
		:param scheduled_course: object to populate data
		:type scheduled_course: ScheduledCourse
		:param course: course object related to scheduled_course
		:type course: Course
		:param semester: model object containing semester id related to scheduled course
		:type semester: Semester
		:param prof: prof object related to scheduled_course
		:type prof: Prof
		:param x_list: cross_listed object related to scheduled_course
		:type x_list: CrossListed
		:return: reference back to scheduled_course parameter
		:rtype: ScheduledCourse
		"""
		scheduled_course.unique_no = self.unique_no
		scheduled_course.session = self.session
		scheduled_course.days = self.days
		scheduled_course.time_from = self.time_from
		scheduled_course.time_to = self.time_to
		scheduled_course.location = self.location
		scheduled_course.max_enrollment = self.max_enrollment
		scheduled_course.seats_taken = self.seats_taken
		scheduled_course.sem_id = semester.id
		scheduled_course.course_id = course.id
		scheduled_course.prof_id = prof.id if prof else None
		scheduled_course.cross_listed = None if x_list is None else x_list.id
		return scheduled_course

	def build_scheduled_course(self, semester, course, prof, x_list):
		"""
		Populate a new ScheduledCourse with data from this class
		:param semester: model object containing semester id related to scheduled course
		:type semester: Semester
		:param course: course object related to scheduled_course
		:type course: Course
		:param prof: prof object related to scheduled_course
		:type prof: Prof
		:param x_list: cross_listed object related to scheduled_course
		:type x_list: CrossListed
		:return: reference to scheduled_course containing data
		:rtype: ScheduledCourse
		"""
		scheduled_course = ScheduledCourse()
		return self.to_scheduled_course(scheduled_course, semester, course, prof, x_list)

def parse_location(title, building, room):
	"""
	Parse location from parameters with default being WEB
	:param title: title of course
	:type title: str
	:param building: building for scheduled_course
	:type building: str
	:param room: room number for building for scheduled_course
	:type room: str
	:return: location of course
	:rtype: str
	"""
	__default = "N/A"
	__web_tag = "-w"

	if not building or not room:
		return 'WEB' if __web_tag in title.lower() else 'N/A'

	return f'{building} {room}'


# --------------
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Load the dataframe
df = pd.read_csv(path)

#Code starts here
#Task 1
p_a=df[df['fico']>700].shape[0]/df.shape[0]
print("Probability of fico credit score greater than 700 is ",p_a)

p_b=df[df['purpose']=='debt_consolidation'].shape[0]/df.shape[0]
print("Probability of purpose equal to debt_consolidation is ",p_b)

df1=df[df['purpose']=='debt_consolidation']
# df1.head()
p_a_b=df1[df1['fico']>700].shape[0]/df1.shape[0]
print("probablityp(B|A)",p_a_b)

result= (p_a_b==p_a)
print(result)

#Task 2
prob_lp=df[df['paid.back.loan']=='Yes'].shape[0]/df.shape[0]
print(prob_lp)

prob_cs=df[df['credit.policy']=='Yes'].shape[0]/df.shape[0]
print(prob_cs)

new_df=df[df['paid.back.loan']=='Yes']
# new_df.head()

prob_pd_cs=new_df[new_df['credit.policy']=='Yes'].shape[0]/new_df.shape[0]
print(prob_pd_cs)

bayes=prob_pd_cs*prob_lp/prob_cs
print("value of bayes is",round(bayes,4))

#Task 3
pur=df['purpose'].value_counts(normalize=True)
pur.plot(kind='bar')
plt.xlabel("purpose")
plt.ylabel("P(purl)")
plt.show()

df1=df[df['paid.back.loan'] == 'No']
# df1.head()

purpl=df1['purpose'].value_counts(normalize=True)
purpl.plot(kind='bar')
plt.xlabel("purpose where paid.back.loan is No")
plt.ylabel("p(purpl)")
plt.show()

#Task 4
inst_median=df['installment'].median()
print(inst_median)

inst_mean=df['installment'].mean()
print(inst_mean)

df['installment'].hist(normed=True,bins=50)
plt.axvline(x=inst_median,color='r')
plt.axvline(x=inst_mean,color='g')
plt.xlabel("installment")
plt.ylabel("Probability")
plt.show()

df['log.annual.inc'].hist(normed=True,bins=50)
plt.xlabel("log.annual.inc")
plt.ylabel("Probability")
plt.show()


import random

suits=('Diamond','Club','Hearts','spades')
ranks=('Two','Three','Four','Five','Six','Seven','Eight','Nine','Ten','King','Queen','Jack','Ace')
values={'Two':2,'Three':3,'Four':4,'Five':5,'Six':6,'Seven':7,'Eight':8,'Nine':9,'Ten':10,'King':10,'Queen':10,'Jack':10,
        'Ace':11}
total = 0

class Deck():
    def __init__(self):
        deck = []
        for suit in suits:
            for rank in ranks:
                deck.append((suit,rank))

        bet_amt = None

        self.deck = deck
        self.bet_amt = bet_amt

    def shuffle(self):
        random.shuffle(self.deck)

    def accounts(self):
        global total
        if total == 0:
            total = int(input('Enter the total balance in your account: '))
        else:
            print(f"Your current account balance is {total}")

        self.bet_amt = int(input('Enter the betting amount: '))
        if self.bet_amt > total:
            print(f'Insufficient Balance !!!')
            exit(0)
        else:
            pass

class Hand(Deck):

    def __init__(self):
        Deck.__init__(self)
        Deck.shuffle(self)
        Deck.accounts(self)

        dealer_list = []
        player_list = []
        self.dealer_list = dealer_list
        self.player_list = player_list

        player_list_1 = []
        player_list_2 = []

        self.player_list_1 = player_list_1
        self.player_list_2 = player_list_2

        temp1_list = []
        temp2_list = []

        self.temp1_list = temp1_list
        self.temp2_list = temp2_list

        dealer_sum = 0
        player_sum = 0
        self.dealer_sum = dealer_sum
        self.player_sum = player_sum

        k = 0
        self.k = k
        j = 0
        self.j = j
        p = 0
        self.p = p

        player_sum_1 = 0
        player_sum_2 = 0

        self.player_sum_1 = player_sum_1
        self.player_sum_2 = player_sum_2

    def distribute(self):

        global total

        for i in range(0,2):
            self.dealer_list.append(self.deck[i])

        for j in range(2,4):
            self.player_list.append(self.deck[j])

        print("Dealers_Cards: ")
        print(self.dealer_list[0])
        self.dealer_sum = values[self.dealer_list[0][1]]
        print(self.dealer_sum)

        print("Players_Cards: ")

        self.player_sum = values[self.player_list[0][1]] + values[self.player_list[1][1]]
        print(self.player_list)
        print(self.player_sum)
        if (self.player_list[0][1] == 'Ace' and values[self.player_list[1][1]] == 10) or\
                (self.player_list[1][1] == 'Ace' and values[self.player_list[0][1]] == 10):
            print("BLACKJACK!!")
            total = total + self.bet_amt * 1.5
            print(f"total money is {total}")
            clear()
            cont()

    def hit(self):
                    global total

                    t = 0

                    if self.player_list_2 != []:
                        t = self.k

                    for i in range(len(self.dealer_list)+ 2 + t , 53):
                        ans = input('Do you want to hit(h)/stand(s)? ')

                        if ans.lower() == 'h':
                            self.player_list.append(self.deck[i])
                            self.player_sum += (values[self.deck[i][1]])
                            print("Players_Cards: ")
                            print(self.player_list)
                            print(self.player_sum)

                            if self.player_list_2 == []:
                                self.k += 1
                            else:
                                self.j += 1

                            if self.player_sum > 21:
                                print("It is the players Bust")
                                total = total-self.bet_amt
                                print(f"total money left is {total}")
                                if self.player_list[1] != self.deck[3]:
                                    return

                                else:
                                    clear()
                                    cont()

                        elif ans.lower() == 's':
                            break

    def stand(self):
        global total

        print("Dealers_Cards: ")
        print(self.dealer_list)

        self.dealer_sum = values[self.dealer_list[0][1]] + values[self.dealer_list[1][1]]
        print(self.dealer_sum)

        if (self.dealer_list[0][1] == 'Ace' and values[self.dealer_list[1][1]] == 10) or (self.dealer_list[1][1] == 'Ace' and values[self.dealer_list[0][1]] == 10):
            print("BLACKJACK!!")

        p = 0

        if self.player_list_2 == []:
            p  = self.k + self.j

        if self.dealer_sum<17:

            if self.player_list_2 == []:

                for i in range(len(self.player_list)-1+2 , 53):
                    self.dealer_list.append(self.deck[i])
                    self.dealer_sum += values[self.deck[i][1]]
                    if (self.dealer_sum<17):
                        print(self.dealer_list)
                        print(self.dealer_sum)
                        continue
                    elif(self.dealer_sum>=17):
                        print(self.dealer_list)
                        print(self.dealer_sum)
                        break
            else:
                for i in range(len(self.player_list_1)+len(self.player_list_2)+2 , 53):
                    self.dealer_list.append(self.deck[i])
                    self.dealer_sum += values[self.deck[i][1]]
                    if (self.dealer_sum<17):
                        print(self.dealer_list)
                        print(self.dealer_sum)
                        continue
                    elif(self.dealer_sum>=17):
                        print(self.dealer_list)
                        print(self.dealer_sum)
                        break

        if (self.dealer_sum >=17 and self.dealer_sum < 21) and self.dealer_sum<self.player_sum and self.player_sum != 21:
            print("The player wins!!")
            total = total + self.bet_amt
            print(f"total money left is {total}")

        elif (self.dealer_sum >=17 and self.dealer_sum < 21) and self.dealer_sum>self.player_sum and self.player_sum != 21:
            print("The dealer wins!!")
            total = total-self.bet_amt
            print(f"total money left is {total}")

        elif (self.dealer_sum == 21 and self.player_sum != 21):

            print("The dealer wins!!")
            total = total - self.bet_amt
            print(f"total money left is {total}")
        elif (self.dealer_sum > 21):
            print("It is the dealers BUST!!")
            total = total + self.bet_amt
            print(f"total money left is {total}")
        elif (self.dealer_sum == self.player_sum):
            print("PUSH")
            print("The game is a draw. No money deducted or won!!")
            print(f"total money left is {total}")
        elif (self.player_sum == 21):

            print("The player wins!!")
            total = total + self.bet_amt
            print(f"total money left is {total}")

        if self.player_list_2 != []:
            return
        else:
            clear()
            cont()

class SpecialCond(Hand):
        def __init__(self):
            Hand.__init__(self)
            Hand.distribute(self)

            a = 0
            b = 0

            self.a = a
            self.b = b

        def insurance(self):
            global total
            if self.dealer_list[0][1] == 'Ace':
                answer = input("Do you want to take insurance?(Y/N)")
                if answer.lower() == 'y':
                    print("Dealers_Cards: ")
                    print(self.dealer_list)

                    self.dealer_sum = values[self.dealer_list[0][1]] + values[self.dealer_list[1][1]]

                    if (self.dealer_list[0][1] == 'Ace' and values[self.dealer_list[1][1]] == 10):
                        total = total + 2*(self.bet_amt/2) - self.bet_amt
                        print(f"The total balance in account is {total}")
                        print("BLACKJACK!!")
                        print("You won the insurance bet!! ")
                        print("EVEN MONEY!!")
                        clear()
                        cont()
                    elif (self.dealer_list[0][1] == 'Ace' and values[self.dealer_list[1][1]] != 10):
                        total = total - (self.bet_amt/2)
                        print("Insurance bet lost")

                        if self.dealer_sum >=17 and self.dealer_sum <21:
                            if self.dealer_sum > self.player_sum:
                                print("The dealer wins!!")
                                total = total - self.bet_amt
                                print(f"The total balance in account is {total}")
                                clear()
                                cont()
                            elif self.dealer_sum < self.player_sum:
                                print("The player wins!!")
                                total = total + self.bet_amt
                                print(f"The total balance in account is {total}")
                                clear()
                                cont()

                        elif self.dealer_sum <17:
                            SpecialCond.stand(self)
                elif answer.lower() =='n':
                    return
            else:
                return

        def double_down(self):

            if (self.player_sum == 9 and (values[self.dealer_list[0][1]] > 1 and values[self.dealer_list[0][1]] < 7 ))\
                    or (self.player_list[0][1] == 'Ace'
                    and (values[self.player_list[1][1]] == 5 or values[self.player_list[1][1]] == 6  or values[self.player_list[1][1]] == 7))\
                    or ((self.player_sum == 10 or self.player_sum == 11) and (self.player_list[0][1] != 'Ace' or self.player_list[1][1] != 'Ace') ):
                answer = input("Do you want to double down (Y/N)? ")
                if answer.lower() == 'y':

                    self.player_list.append(self.deck[4])

                    print(self.player_list)
                    self.player_sum = self.player_sum + values[self.player_list[2][1]]
                    print(self.player_sum)
                    self.bet_amt = 2*self.bet_amt
                    if self.bet_amt >total:
                        print("Not enough sufficient funds for doubling the bet")
                        clear()
                        cont()

                    print("The bet amount is doubled")
                    SpecialCond.stand(self)

                elif answer.lower() == 'n':
                    return

        def split(self):
            if values[self.player_list[0][1]] == values[self.player_list[1][1]]:
                ans = input("Do you want to split(Y/N): ")
                if ans.lower() == 'y':
                    print('\n')
                    print("This is phase 1")
                    self.temp1_list = self.player_list
                    self.temp2_list = self.player_list

                    self.player_sum_1 = self.player_sum - values[self.temp1_list[1][1]]
                    self.player_sum_2 = self.player_sum - values[self.temp1_list[0][1]]

                    self.player_list_1.append(self.temp1_list[0])

                    self.player_sum = self.player_sum_1
                    self.player_list = self.player_list_1
                    SpecialCond.hit(self)
                    self.a = self.player_sum
                    print('\n')

                    print("This is phase 2")
                    self.player_sum = self.player_sum_2
                    self.player_list_2.append(self.temp2_list[1])
                    self.player_list = self.player_list_2
                    SpecialCond.hit(self)
                    self.b = self.player_sum
                    print('\n')
                    print("Result for phase 2")
                    print(f"The player sum of player_list_1 is: {self.b}")
                    if self.b > 21:
                        pass
                    else:
                        SpecialCond.stand(self)
                    self.player_sum = self.b
                    print("\n")
                    print("Result for phase 1")
                    print(f"The player sum of player_list_2 is: {self.a}")
                    if self.a > 21:
                        pass
                    else:
                        SpecialCond.stand(self)
                    clear()
                    cont()

def clear():
    print('\n'*5)

def cont():

    answer = input('Do you want to play again? (Y/N) ')

    if answer.lower() == 'y':
        special = SpecialCond()
        special.insurance()
        special.double_down()
        special.split()
        special.hit()
        special.stand()
    else:
        exit(0)

def start():
    answer = input('Do you want to play BLACKJACK? (Y/N) ')
    if answer.lower() == 'y':

        special = SpecialCond()
        special.insurance()
        special.double_down()
        special.split()
        special.hit()
        special.stand()

    else:
        exit(0)

def begin():
    print('\n')
    print(' '*40," WELCOME TO BLACKJACK")
    print(' '*40,"-----------------------")
    start()

begin()


__author__ = 'alexcomu'

## Steps
# 1 - Find every pair of movies that were watched by tha same person
# 2 - Measure the similarity of their rating across all users who watched both
# 3 - Sort by movie, then by similarity strength

## MapReduce Problem

## Mapper: Extract User -> (Movie, Rating)
## Reducer: Groups all movie, rating pairs by user

## Mapper: Output all movies viewed by the same user (movie1, movie2) -> (rating1, rating2)
## Reducer: Compute rating-based similarity between each movie pair (movie1, movie2) -> (similarity, number of users who saw both)

## Mapper: Make movie name, similarity score the sorting key
## Reducer: Display the final sorted output

## -> Movie sorted by Name with a list of movie ordered by similaity

from mrjob.job import MRJob
from mrjob.step import MRStep
from math import sqrt
from itertools import combinations # permutations

class MovieSimilarities(MRJob):

    def configure_options(self):
        super(MovieSimilarities, self).configure_options()
        self.add_file_option('--items', help='Insert the path to u.item')

    def load_movie_names(self):
        # Load database of movie names to print pretty name instead ID.
        self.movieNames = {}

        with open("u.item") as f:
            for line in f:
                fields = line.split('|')
                self.movieNames[int(fields[0])] = fields[1]

    def steps(self):
        # 3 steps process
        return [
            MRStep(mapper=self.mapper_parse_input,
                    reducer=self.reducer_ratings_by_user),
            MRStep(mapper=self.mapper_create_item_pairs,
                    reducer=self.reducer_compute_similarity),
            MRStep(mapper=self.mapper_sort_similarities,
                    mapper_init=self.load_movie_names,
                    reducer=self.reducer_output_similarities)]

    def mapper_parse_input(self, key, line):
        # Outputs userID => (movieID, rating)
        (userID, movieID, rating, timestamp) = line.split('\t')
        yield userID, (movieID, float(rating))

    def reducer_ratings_by_user(self, user_id, itemRatings):
        #Group (item, rating) pairs by userID

        ratings = []
        for movieID, rating in itemRatings:
            ratings.append((movieID, rating))

        yield user_id, ratings

    def mapper_create_item_pairs(self, user_id, itemRatings):
        # Find every pair of movies each user has seen, and emit
        # each pair with its associated ratings

        # "combinations" finds every possible pair from the list of movies
        # this user viewed.
        for itemRating1, itemRating2 in combinations(itemRatings, 2):
            movieID1 = itemRating1[0]
            rating1 = itemRating1[1]
            movieID2 = itemRating2[0]
            rating2 = itemRating2[1]

            # Produce both orders so sims are bi-directional
            yield (movieID1, movieID2), (rating1, rating2)
            yield (movieID2, movieID1), (rating2, rating1)

    def cosine_similarity(self, ratingPairs):
        # Computes the cosine similarity metric between two
        # rating vectors.
        numPairs = 0
        sum_xx = sum_yy = sum_xy = 0
        for ratingX, ratingY in ratingPairs:
            sum_xx += ratingX * ratingX
            sum_yy += ratingY * ratingY
            sum_xy += ratingX * ratingY
            numPairs += 1

        numerator = sum_xy
        denominator = sqrt(sum_xx) * sqrt(sum_yy)

        score = 0
        if (denominator):
            score = (numerator / (float(denominator)))

        return (score, numPairs)

    def reducer_compute_similarity(self, moviePair, ratingPairs):
        # Compute the similarity score between the ratings vectors
        # for each movie pair viewed by multiple people

        # Output movie pair => score, number of co-ratings

        score, numPairs = self.cosine_similarity(ratingPairs)

        # Enforce a minimum score and minimum number of co-ratings
        # to ensure quality
        if (numPairs > 10 and score > 0.95):
            yield moviePair, (score, numPairs)

    def mapper_sort_similarities(self, moviePair, scores):
        # Shuffle things around so the key is (movie1, score)
        # so we have meaningfully sorted results.
        score, n = scores
        movie1, movie2 = moviePair

        yield (self.movieNames[int(movie1)], score), \
            (self.movieNames[int(movie2)], n)

    def reducer_output_similarities(self, movieScore, similarN):
        # Output the results.
        # Movie => Similar Movie, score, number of co-ratings
        movie1, score = movieScore
        for movie2, n in similarN:
            yield movie1, (movie2, score, n)

if __name__ == '__main__':
    MovieSimilarities.run()


# demo/files.py
import os
import sys

print ('Script name ', sys.argv[0] ) # $0
print ('First argument ', sys.argv[1] ) # $1 etc

os.getcwd() # pwd, use help os
r'c:\users\kolya' # not format, raw string
os.listdir() # ls

sys.path

# 10 work with fs
#open('data.txt', 'r') # read file data.txt
#open('data.txt', 'r+') # read and overwrite file data.txt FROM FIRST SYMBOL
#open('data.txt', 'w') # only write file data.txt (>)
#open('data.txt', 'w+') # only write file data.txt
#open('data.txt', 'a') # write file data.txt (>>)
#open('data.txt', 'a+') # read and write file data.txt
#f = open('data.txt', 'r', encoding='utf-8')
#f.close() # close file after work is done
#lst = []
import csv

with open('data.txt', 'r', encoding='utf-8') as f: # = f = open('data.txt', 'r', encoding='utf-8') (auto close)
    #print(f.read(5)) # 5 first symbols of file
    #print(f.read()) # whole file
    #print(f.readline()) # strings of file (first string if no argument)
    #s = f.readline()
    #while s:
        #lst.append(s)
        #s = f.readline()
   #lst = f.readlines() # strings to list
   for line in f: # f.readlines too
       print(line)

with open('data.txt', 'a', encoding='utf-8') as f:
    #f.write('\nLine seven') # write to file
    #f.tell() # where is my cursor
    #f.seek() # move cursor from beginning of file
    print(f.closed) # file closed (True) or no (False), use without
    print(f.mode) # mode of work with file (r w a)
    print(f.name) # name of file
    print(f.encoding) # encoding of file

with open('users.csv', 'r', encoding='utf-8') as f:
    #reader = csv.reader(f, delimiter = ',')
    writer = csv.writer(f, delimiter = ',')
    writer.writerow(['mike', 'bazinsky', 33,'1-800-call-merlini'])
    #for row in reader:
        #print(row)


import sys
import numpy as np

def make_iterable(x):
    try:
        _ = iter(x)
    except TypeError as _:
        return [x]
    return x

def print_lb_values(slices):
    slices = make_iterable(slices)

    strs = []

    for sl in slices:
        try:
            x = int(sl)
            strs.append(str(x))
        except TypeError as _:
            if isinstance(sl, slice) and sl.start is not None:
                strs.append(str(sl.start))
            else:
                strs.append('0')
    return ','.join(strs)

def print_ub_values(slices):
    slices = make_iterable(slices)

    strs = []

    for sl in slices:
        if isinstance(sl, slice) and sl.stop is not None:
            strs.append(str(sl.stop))
        else:
            strs.append('0')
    return ','.join(strs)

def print_stride_values(slices):
    slices = make_iterable(slices)

    strs = []

    for sl in slices:
        if isinstance(sl, slice) and sl.step is not None:
            strs.append(str(sl.step))
        else:
            strs.append('1')
    return ','.join(strs)

def print_lb_mask_axes(slices):
    slices = make_iterable(slices)

    mask_strs = []
    i = 0

    for sl in slices:
        if isinstance(sl, slice) and sl.start is None:
            mask_strs.append(str(i))
        i += 1
    return ','.join(mask_strs)

def print_ub_mask_axes(slices):
    slices = make_iterable(slices)

    mask_strs = []
    i = 0

    for sl in slices:
        if isinstance(sl, slice) and sl.stop is None:
            mask_strs.append(str(i))
        i += 1
    return ','.join(mask_strs)

def print_new_mask_axes(slices):
    slices = make_iterable(slices)

    mask_strs = []
    i = 0

    for sl in slices:
        if sl is None:
            mask_strs.append(str(i))
        i += 1
    return ','.join(mask_strs)

def print_shrink_mask_axes(slices):
    slices = make_iterable(slices)

    mask_strs = []
    i = 0

    for sl in slices:
        try:
            _ = int(sl)
            mask_strs.append(str(i))
        except TypeError as _:
            pass
        i += 1
    return ','.join(mask_strs)

def print_ellipsis_mask_axes(slices):
    slices = make_iterable(slices)

    mask_strs = []
    i = 0

    for sl in slices:
        if sl is Ellipsis:
            mask_strs.append(str(i))
        i += 1
    return ','.join(mask_strs)

def np_dt_to_c(dtype):
    if dtype=='int8':
        return 'int8_t'
    elif dtype=='uint8':
        return 'uint8_t'
    elif dtype=='int16':
        return 'int16_t'
    elif dtype=='uint16':
        return 'uint16_t'
    elif dtype=='int32':
        return 'int32_t'
    elif dtype=='uint32':
        return 'uint32_t'
    elif dtype=='int64':
        return 'int64_t'
    elif dtype=='uint64':
        return 'uint64_t'
    elif dtype=='float16':
        return 'float16'
    elif dtype=='float32':
        return 'float'
    elif dtype=='float64':
        return 'double'
    elif dtype=='bool':
        return 'char'
    else:
        raise ValueError('Unsupported numpy data type: %s' % dtype)

def np_dt_to_ng(dtype):
    if dtype=='int8':
        return 'element::i8'
    elif dtype=='uint8':
        return 'element::u8'
    elif dtype=='int16':
        return 'element::i16'
    elif dtype=='uint16':
        return 'element::u16'
    elif dtype=='int32':
        return 'element::i32'
    elif dtype=='uint32':
        return 'element::u32'
    elif dtype=='int64':
        return 'element::i64'
    elif dtype=='uint64':
        return 'element::u64'
    elif dtype=='float16':
        return 'element::f16'
    elif dtype=='float32':
        return 'element::f32'
    elif dtype=='float64':
        return 'element::f64'
    elif dtype=='bool':
        return 'element::boolean'
    else:
        raise ValueError('Unsupported numpy data type: %s' % dtype)

def print_values(values):
    values = make_iterable(values)
    strs = []

    for v in values:
        strs.append(str(v))

    return ','.join(strs)

def print_shape(dims):
    dims = make_iterable(dims)
    strs = []

    for d in dims:
        strs.append(str(d))

    return 'Shape{' + ','.join(strs) + '}'

def print_slice(sl):
    if sl is None:
        return 'newaxis'
    elif sl is Ellipsis:
        return "..."
    elif isinstance(sl, slice):
        s = ''
        if sl.start is not None:
            s += str(sl.start)
        s += ':'
        if sl.stop is not None:
            s += str(sl.stop)
        if sl.step is not None:
            s += ':'
            s += str(sl.step)
        return s
    else:
        return str(sl)

def print_slices(slices):
    slices = make_iterable(slices)
    strs = []

    for sl in slices:
        strs.append(print_slice(sl))

    return '[' + ','.join(strs) + ']'

#
# Class to intercept indexing operations and write an nGraph C++ test case. The
# generated test case will ensure that the output is identical to that which
# would be produced by numpy on a "linspaced" array of the given shape and
# dtype. If numpy throws an exception when the slice is attempted, the test
# checks that nGraph throws some exception somewhere in graph construction or
# execution, but does not attempt to make sure that the exception is the
# "correct" one.
#
# Example usage:
#
#    w = SliceTestWriter(stream=sys.stdout)
#
#    # behave as if slicing a 4x5x6 input array of data type int32
#    w.set_shape(4,5,6)
#    w.set_dtype('int32')
#
#    # generate test cases for various behaviors, writing C++ code to sys.stdout
#    w[0,:,:]
#    w[...,-1:-3,:]
#    w[-3,...,-1:-6:-3]
#    w[1,2,3,4]        # note: in numpy this throws exception (too many
#                      # indices), so generated test case will check that
#                      # nGraph throws some exception too.
#
class SliceTestWriter:
    def __init__(self, shape=(), dtype='int32', stream=sys.stdout):
        self._shape = shape
        self._dtype = dtype
        self._stream = stream
        self._test_counter = 0

    def __getitem__(self, slices):
        self.write_test(slices)

    def write_test(self, slices):
        data_in = np.linspace(0,np.prod(self._shape)-1,np.prod(self._shape),dtype=self._dtype).reshape(self._shape)

        self._stream.write('\n')
        self._stream.write('// slices are: %s\n' % print_slices(slices))
        self._stream.write('// dtype is: %s\n' % self._dtype)
        self._stream.write('// input shape is: %s\n' % print_shape(self._shape))

        try:
            data_out = data_in.__getitem__(slices)
        except Exception:
            self._stream.write('// failure is expected\n'
                               'NGRAPH_TEST(${BACKEND_NAME}, dyn_slice_%d)\n'
                               '{\n'
                               '    check_failure<%s>\n'
                               '                 (%s,\n'
                               '                  %s,\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s});\n'
                               '}\n'
                                  % (self._test_counter,
                                     np_dt_to_c(self._dtype),
                                     np_dt_to_ng(self._dtype),
                                     print_shape(data_in.shape),
                                     print_lb_values(slices),
                                     print_ub_values(slices),
                                     print_stride_values(slices),
                                     print_lb_mask_axes(slices),
                                     print_ub_mask_axes(slices),
                                     print_new_mask_axes(slices),
                                     print_shrink_mask_axes(slices),
                                     print_ellipsis_mask_axes(slices)))
        else:
            self._stream.write('// expected output shape is %s\n'
                               'NGRAPH_TEST(${BACKEND_NAME}, dyn_slice_%d)\n'
                               '{\n'
                               '    check_success<%s>\n'
                               '                 (%s,\n'
                               '                  %s,\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  std::vector<int64_t>{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  AxisSet{%s},\n'
                               '                  %s,\n'
                               '                  std::vector<%s>{%s});\n'
                               '}\n'
                                  % (print_shape(data_out.shape),
                                     self._test_counter,
                                     np_dt_to_c(self._dtype),
                                     np_dt_to_ng(self._dtype),
                                     print_shape(data_in.shape),
                                     print_lb_values(slices),
                                     print_ub_values(slices),
                                     print_stride_values(slices),
                                     print_lb_mask_axes(slices),
                                     print_ub_mask_axes(slices),
                                     print_new_mask_axes(slices),
                                     print_shrink_mask_axes(slices),
                                     print_ellipsis_mask_axes(slices),
                                     print_shape(data_out.shape),
                                     np_dt_to_c(self._dtype), print_values(data_out.reshape(-1))))

        self._test_counter += 1

    def set_shape(self,shape):
        self._shape = shape

    def set_dtype(self,dtype):
        self._dtype = dtype

def write_header(f):
    f.write('''\
//*****************************************************************************
// Copyright 2017-2020 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//*****************************************************************************

// !!!!!!!!!!!!!! THIS FILE IS AUTOGENERATED OUTSIDE OF THE BUILD PROCESS !!!!!!!!!!!!!!
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DO NOT EDIT THIS FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// DO NOT EDIT THIS FILE. If you want to add new tests, you should edit
//  test/ref_generators/generate_dyn_slice_ref.py and regenerate this file.
//
// To regenerate:
//
//   $ cd <ngraph source dir>/test
//   $ ./update_dyn_slice_reference.sh
//
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! DO NOT EDIT THIS FILE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// !!!!!!!!!!!!!! THIS FILE IS AUTOGENERATED OUTSIDE OF THE BUILD PROCESS !!!!!!!!!!!!!!
//
// clang-format off

#include <algorithm>
#include <cmath>

#include "gtest/gtest.h"

#include "ngraph/ngraph.hpp"
#include "util/test_tools.hpp"
#include "util/autodiff/numeric_compare.hpp"
#include "util/all_close_f.hpp"
#include "util/test_control.hpp"

using namespace std;
using namespace ngraph;

static string s_manifest = "${MANIFEST}";

namespace
{
    template <typename T>
    void check_failure(const element::Type& input_element_type,
                    const Shape& input_shape,
                    const std::vector<int64_t>& lb_values,
                    const std::vector<int64_t>& ub_values,
                    const std::vector<int64_t>& strides_values,
                    const AxisSet& lb_mask,
                    const AxisSet& ub_mask,
                    const AxisSet& new_mask,
                    const AxisSet& shrink_mask,
                    const AxisSet& ellipsis_mask)
    {
        auto arg = std::make_shared<op::v0::Parameter>(input_element_type, input_shape);
        auto lb = std::make_shared<op::v0::Parameter>(element::i64, Shape{lb_values.size()});
        auto ub = std::make_shared<op::v0::Parameter>(element::i64, Shape{ub_values.size()});
        auto strides = std::make_shared<op::v0::Parameter>(element::i64, Shape{strides_values.size()});

        std::vector<T> input_values(shape_size(input_shape));
        std::iota(input_values.begin(), input_values.end(), static_cast<T>(0));

        auto slice = std::make_shared<op::v0::DynSlice>(
            arg, lb, ub, strides, lb_mask, ub_mask, new_mask, shrink_mask, ellipsis_mask);

        auto f = std::make_shared<Function>(OutputVector{slice}, ParameterVector{arg, lb, ub, strides});

        auto backend = runtime::Backend::create("${BACKEND_NAME}", true);
        auto ex = backend->compile(f);

        auto input_arg = backend->create_tensor(input_element_type, input_shape);
        auto input_lb = backend->create_tensor(element::i64, Shape{lb_values.size()});
        auto input_ub = backend->create_tensor(element::i64, Shape{ub_values.size()});
        auto input_strides = backend->create_tensor(element::i64, Shape{strides_values.size()});
        copy_data(input_arg, input_values);
        copy_data(input_lb, lb_values);
        copy_data(input_ub, ub_values);
        copy_data(input_strides, strides_values);

        auto output = backend->create_dynamic_tensor(input_element_type, PartialShape::dynamic());

        try
        {
            ex->call_with_validate({output}, {input_arg, input_lb, input_ub, input_strides});
            FAIL() << "No exception thrown";
        }
        catch (exception& err)
        {
            string s = err.what();
            s = s.substr(0, 5);
            if (s != "Check")
            {
                auto p = std::current_exception();
                rethrow_exception(p);
            }
        }
    }

    template <typename T>
    void check_success(const element::Type& input_element_type,
                    const Shape& input_shape,
                    const std::vector<int64_t>& lb_values,
                    const std::vector<int64_t>& ub_values,
                    const std::vector<int64_t>& strides_values,
                    const AxisSet& lb_mask,
                    const AxisSet& ub_mask,
                    const AxisSet& new_mask,
                    const AxisSet& shrink_mask,
                    const AxisSet& ellipsis_mask,
                    const Shape& expected_output_shape,
                    const std::vector<T>& expected_values)
    {
        auto arg = std::make_shared<op::v0::Parameter>(input_element_type, input_shape);
        auto lb = std::make_shared<op::v0::Parameter>(element::i64, Shape{lb_values.size()});
        auto ub = std::make_shared<op::v0::Parameter>(element::i64, Shape{ub_values.size()});
        auto strides = std::make_shared<op::v0::Parameter>(element::i64, Shape{strides_values.size()});

        std::vector<T> input_values(shape_size(input_shape));
        std::iota(input_values.begin(), input_values.end(), static_cast<T>(0));

        auto slice = std::make_shared<op::v0::DynSlice>(
            arg, lb, ub, strides, lb_mask, ub_mask, new_mask, shrink_mask, ellipsis_mask);

        auto f = std::make_shared<Function>(OutputVector{slice}, ParameterVector{arg, lb, ub, strides});

        auto backend = runtime::Backend::create("${BACKEND_NAME}", true);
        auto ex = backend->compile(f);

        auto input_arg = backend->create_tensor(input_element_type, input_shape);
        auto input_lb = backend->create_tensor(element::i64, Shape{lb_values.size()});
        auto input_ub = backend->create_tensor(element::i64, Shape{ub_values.size()});
        auto input_strides = backend->create_tensor(element::i64, Shape{strides_values.size()});
        copy_data(input_arg, input_values);
        copy_data(input_lb, lb_values);
        copy_data(input_ub, ub_values);
        copy_data(input_strides, strides_values);

        auto output = backend->create_dynamic_tensor(input_element_type, PartialShape::dynamic());

        ex->call_with_validate({output}, {input_arg, input_lb, input_ub, input_strides});

        EXPECT_EQ(output->get_element_type(), input_element_type);
        EXPECT_EQ(output->get_output_shape(0), expected_output_shape);

        auto output_values = read_vector<T>(output);

        EXPECT_EQ(output_values, expected_values);
    }
}
''')

def write_footer(f):
    f.write('''\
// clang-format on
''')

def main():
    if len(sys.argv) < 2:
        sys.stderr.write('Output filename is required\n')
        sys.exit(1)

    f = open(sys.argv[1], 'w')
    write_header(f)

    t = SliceTestWriter(stream=f)

    t.set_shape((4,))
    for dt in ['int32','int64','float32','uint32']:
        t.set_dtype(dt)

        t[np.newaxis,3:0:-1]
        t[...]
        t[1:3]
        t[2]
        t[3:0:-2]
        t[3::-2]
        t[4::-2]
        t[5::-2]
        t[-9000:-8000:2]
        t[-9000:8000:2]
        t[-5:5:2]
        t[np.newaxis]
        t[np.newaxis,np.newaxis]
        t[np.newaxis,np.newaxis,...,np.newaxis]

        t.set_shape((5,))
        t[3:0:-2]
        t[0:3:2]
        t[0:4:2]
        t[0:5:2]
        t[0:6:2]
        t[0:100:2]
        t[4:0:-2]
        t[4:0:-3]
        t[3:2:1]
        t[4::-2]
        t[1:-5:-1]
        t[1:-1:-1]
        t[1:None]
        t[1:None:-1]
        t[-5:5:2]
        t[-1:5:1]
        t[-1:1:1]

        #
        # A couple of tests for negative-stride slicing. The issue we want to
        # be on the lookout for is this:
        #
        #  [ORIGINAL]
        #   01234567
        #   ..1..0..   [5:0:-3]  # suppose we start with this, want to convert
        #    _____               # to pos stride. suppose that our stride is
        #                        # "uneven" wrt the slicing region, i.e. the
        #                        # start-to-end distance is not an even
        #                        # multiple of the strides (e.g. here: we get
        #                        # elements 5 and 2.)
        #
        #  [INCORRECT]
        #   01234567
        #   .0..1...   [1:6:3]   # if we just reverse the sign of the stride
        #    _____               # and flip the start/end indices while
        #                        # traversing, we will get out the wrong
        #                        # elements. (e.g. here: we get elements 1 and
        #                        # 4, which are not what we want.)
        #
        #  [CORRECT]
        #   01234567
        #   ..0..1..   [2:6:3]   # the correct thing to do is to adjust the
        #     ____               # start of our reversed slice to be the last
        #                        # element that is *actually* touched by the
        #                        # original negative striding, not the
        #                        # boundary of the region. (e.g. here: we get
        #                        # elements 2 and 5, which are what we want.)
        #
        # There's some logic to do this transformation in DynElimination, but
        # it feels a bit delicate.
        #
        t.set_shape((8,))
        t[5:2:-3]
        t[5:1:-3]
        t[5:0:-3]
        t[5::-3]
        t[6:3:-3]
        t[6:2:-3]
        t[6:1:-3]
        t[6::-3]
        t[7:1:-3]
        t[7:0:-3]
        t[7::-3]

    t.set_dtype('int32')
    t[80000] # error expected (shrink-axis OOB)
    t[-80000] # error expected (shrink-axis OOB)
    t[:,:] # error expected (too many indices)
    t[0:0:0] # error expected (stride==0)
    t[0:1:0] # error expected (stride==0)
    t[0:2:0] # error expected (stride==0)
    t[::0] # error expected (stride==0)

    t.set_shape((2,3,4))
    for dt in ['int32','int64','float32','uint32']:
        t.set_dtype(dt)

        t[1,np.newaxis]
        t[-1,-1,np.newaxis]

    t.set_shape((2,4,6,8,2,2,2))
    for dt in ['int32','int64','float32','uint32']:
        t.set_dtype(dt)
        t[0:,:4,2:6:2,7:3:-2,np.newaxis,...,1]

    t.set_dtype('int32')
    t[...,...] # error expected (too many ellipses)

    write_footer(f)
    f.close()

if __name__ == "__main__":
    main()


#Asynchronous file I/O and database operation

import asyncio
import aiofiles

async def read_file(file_path):
    async with aiofiles.open(file_path, mode='r') as file:
        content = await file.read()
        print(f"Read from {file_path}: {content}")

async def write_file(file_path, content):
    async with aiofiles.open(file_path, mode='w') as file:
        await file.write(content)
        print(f"Wrote to {file_path}")

async def main():
    file_path = 'example.txt'
    content_to_write = 'Hello, Async I/O!'

    await write_file(file_path, content_to_write)

    await read_file(file_path)

# Run the asynchronous event loop
asyncio.run(main())


v1 = float(input('digite um número: '))
v2 = float(input("digite ooutro número: "))

if v1 > v2:
    print('O primeiro número é o maior!')
elif v1 < v2:
    print('O segundo numero é maior!')
elif v1 == v2:
    print('Os numeros são iguais!')
else:
    print('INFORMAÇÃO INVÁLIDA, TENTE NOVAMENTE!')


class Console:
    def __init__(self, ai_controller):
        self.__game = ai_controller
        self.__startwithai = False
        self.__in_turn = 1

    def start(self):
        continue_game = True
        while continue_game:
            try:
                x = int(input("\nGive the dimension of the board: \nheight = "))
                y = int(input("width = "))

                if x < 1 or y < 1:
                    raise ValueError
                set_player = input("\nDo you want to be the one that starts the game?\n a) Yes\n b) No\n ")
                # Settind the size of the board
                self.__game.set_row(x)
                self.__game.set_column(y)
                self.__game.create_board()

                if set_player == 'a':
                    self.__in_turn = 1  # 1 will be the player
                    self.__startwithai = False
                elif set_player == 'b':
                    self.__in_turn = 2  # 2 will be the computer
                    self.__startwithai = True
                while self.__game.game_over() is True:
                    if self.__in_turn == 1:
                        try:
                            print(str(self.__game.get_board()))
                            x = input("x = ")
                            y = input("y = ")
                            self.__game.make_move_player(x, y)
                            self.__in_turn = 2
                        except Exception as error:
                            print(error)
                    elif self.__in_turn == 2:
                        self.__game.make_move_ai(self.__startwithai, x, y)
                        self.__in_turn = 1
                print(str(self.__game.get_board()))
                if self.__in_turn == 2:
                    print("Player has won!")
                else:
                    print("Computer has won!")

                cont = input("\n Do you wish to play again?\n a) Yes\n b) No\n")
                if cont == 'a':
                    self.__game.destroy_board()
                elif cont == 'b':
                    continue_game = False
                else:
                    print("Invalid option!")
            except ValueError:
                print("Invalid coordinates!")