import os, torch
import numpy as np
from PIL import ImageFile
import random
import numpy as np
import pandas as pd
from random import shuffle
import random
# from augmentations import *
import traceback
from tqdm import tqdm
from torch import float32, save, load
from torch.utils.data import Dataset
from nibabel import Nifti1Image
from nilearn.image import load_img, smooth_img, clean_img
from nilearn.maskers import NiftiLabelsMasker
from nilearn.datasets import fetch_atlas_schaefer_2018, fetch_atlas_aal, fetch_atlas_destrieux_2009, fetch_atlas_harvard_oxford
from sklearn.model_selection import StratifiedKFold
from multiprocessing.pool import ThreadPool # TODO change to Parallel

from loguru import logger

ImageFile.LOAD_TRUNCATED_IMAGES = True

class ADNI(Dataset):

    def __init__(self,
        data_dir='../data',
        data_type='t1_linear',
        atlas_dir='../data',
        splits_dir= '../splits',
        mode='Train',
        num_classes=3,
        depth_of_slice=40,
        k_fold=1,
    #  smoothing_fwhm=None,
        slicing_stride=5,
        parallize_brains=True
    ):
        if data_type == "t1_volume":
            self.suffix = 't1/spm/segmentation/normalized_space'
            self.seg_substring = 'Space_T1w'
        elif data_type == 't1_linear':
            self.suffix = 't1_linear'
            self.seg_substring = 'Sym_res-1x1x1_T1w.nii'
        else:
            raise Exception(f'Data type {data_type} is not supported')

        self.parallized_brains = parallize_brains
        self.n_processes = os.cpu_count() - 1
        logger.debug(f'ADNI ({mode}) {data_type} prepared dataset to preprocess using {self.n_processes} processes')
        if self.parallized_brains:
            logger.debug('..Parellizing over brains processing')
        else:
            logger.debug('..Parallizing over slices processing')
        self.threading_pool = ThreadPool(self.n_processes)

        self.num_classes = num_classes
        if self.num_classes == 3:
            LABEL_MAPPING = ["CN", "MCI", "AD"]
        elif self.num_classes == 2:
            LABEL_MAPPING = ["CN", "AD"]
        self.LABEL_MAPPING = LABEL_MAPPING

        subject_tsv = pd.io.parsers.read_csv(
            os.path.join(splits_dir, mode + '_diagnosis_ADNI.tsv'),
            sep='\t'
        )

        # Clean sessions without labels
        indices_not_missing = []
        for i in range(len(subject_tsv)):
            if mode == 'Train':
                if (subject_tsv.iloc[i].diagnosis in LABEL_MAPPING):
                    indices_not_missing.append(i)
            else:
                if (subject_tsv.iloc[i].diagnosis in LABEL_MAPPING):
                    indices_not_missing.append(i)

        self.subject_tsv = subject_tsv.iloc[indices_not_missing]
        self.dir_to_scans = os.path.join(data_dir, 'processed', 'subjects')
        self.mode = mode
        self.slicing_stride = slicing_stride
        self.depth_of_slice = depth_of_slice
        self.experiment_name = f'prepared_adni_{self.mode}_{self.num_classes}classes_{self.depth_of_slice}ds'
        self.load_atlases(atlas_dir)

        if os.path.isfile(os.path.join(data_dir, f'{self.experiment_name}.pth')):
            self.slices_stack_dict = load(os.path.join(data_dir, f'{self.experiment_name}.pth'))
            logger.success(f'Loaded saved slices.')

        else:
            logger.info('Preparing data and generating slices..')
            self.slices_stack_dict = {}
            def get_slices(item):
                idx, row = item
                participant_id = row['participant_id']
                session_id = row['session_id']
                slices = self.get_prepared_sample(participant_id, session_id)
                return idx, slices

            if self.parallized_brains:
                for idx, slices in tqdm(
                    self.threading_pool.imap_unordered(
                        get_slices,
                        subject_tsv.iterrows(),
                        chunksize=self.n_processes
                    ),
                    ncols=60, total=subject_tsv.shape[0],
                    desc="Preparing subject brains in parallel"
                ):
                    self.slices_stack_dict[idx] = slices
            else:
                for idx, row in tqdm(
                    subject_tsv.iterrows(), ncols=60, total=subject_tsv.shape[0],
                    desc="Preparing subject brains sequentially"
                ):
                    _, self.slices_stack_dict[idx] = get_slices((idx, row))

            save(self.slices_stack_dict, os.path.join(data_dir, f'{self.experiment_name}.pth'))

            logger.success('Saved succesfully generated prepared slices.')

        _somekey = list(self.slices_stack_dict.keys())[0]
        self.nums_nodes = []
        for i in range(len(self.roi_maskers)):
            self.nums_nodes.append(self.slices_stack_dict[_somekey][i].shape[1])

        logger.success(f'Retrived # of nodes: {self.nums_nodes}')

        self.full_sample_id_list = list(self.slices_stack_dict.keys())

        if k_fold <= 1:
            self.k_fold = 1
            self.sample_id_list = self.full_sample_id_list
            logger.success(f'No fold / 1-fold applied hence sample_id_list=full_sample_id_list')
        else:
            self.k_fold = StratifiedKFold(k_fold, shuffle=True, random_state=0)
            self.k = None
            logger.success(f'Using stratified shuffled KFold of {k_fold}')

        self.threading_pool.close()

    def set_fold(self, fold, train=True):
        # TODO merge with logic from above to encapsulate logic
        assert self.k_fold is not None and self.k_fold > 1
        self.k = fold

        labels = self.subject_tsv.iloc[self.full_sample_id_list]['diagnosis'].to_list()
        train_idx, test_idx = list(
            self.k_fold.split(self.full_sample_id_list, labels)
        )[fold]
        if train:
            shuffle(train_idx)
            self.sample_id_list = [
                self.full_sample_id_list[idx]
                for idx in train_idx
            ]
        else:
            self.sample_id_list = [
                self.full_sample_id_list[idx]
                for idx in test_idx
            ]

    def load_atlases(self, atlas_dir):
        # if roi=='schaefer':
        self.atlases_names = ['aal', 'cc200', 'schaefer']
        self.roi_maskers: NiftiLabelsMasker = []
        atlases = [
            fetch_atlas_aal(data_dir=os.path.join(atlas_dir, 'roi')),
            # Commented out as orginally  meant for ADHD
            {
                "maps": os.path.join(atlas_dir, 'roi', 'cc200', 'cc200_roi_atlas.nii.gz'),
                "labels": 200
            },
            fetch_atlas_schaefer_2018(data_dir=os.path.join(atlas_dir, 'roi')),
        ]
        for atlas in atlases:
            # TODO Add other sides/rotations
            atlas_img = load_img(atlas['maps'])
            # Note Standarize to false as already applied.. if applied again will lead to zeros
            atlas_labels = atlas.get('labels')
            self.roi_maskers.append(
                NiftiLabelsMasker(
                    atlas_img, labels=atlas_labels, standardize=False,
                    # memory="nilearn_cache",
                    # verbose=5,
                    resampling_target="labels"
                )
            )
            self.roi_maskers[-1].fit()

    def __len__(self):
        return len(self.sample_id_list)

    def __getitem__(self, idx):
        sample_id = self.sample_id_list[idx]
        if self.subject_tsv.iloc[sample_id].diagnosis == 'CN':
            label = 0
        elif self.subject_tsv.iloc[sample_id].diagnosis == 'MCI':
            label = 1
        elif self.subject_tsv.iloc[sample_id].diagnosis == 'AD':
            if self.LABEL_MAPPING == ["CN", "AD"]:
                label = 1
            else:
                label = 2
        else:
            logger.error('WRONG LABEL VALUE!!!')
            label = -100

        slices = self.slices_stack_dict[sample_id]

        # TODO ensure it works
        return {
            "id": sample_id,
            "slices_per_atlas": slices,
            "label": label
        }
        # return image.astype(np.float32),label,idx_out,mmse,cdr_sub,age, os.path.join(path,seg_name)

    def get_prepared_sample(self, participant_id, session_id):
        sample_id = f"{participant_id}_{session_id}"
        try:

            path = os.path.join(self.dir_to_scans,participant_id,
                    session_id, self.suffix)
            all_segs = list(os.listdir(path))
            img_filename = [
                seg_name for seg_name in all_segs
                if self.seg_substring in seg_name
            ]
            assert len(img_filename) == 1
            img_filename = img_filename[0]
            slices = self.prepare_brain(
                load_img(os.path.join(path, img_filename))
            )

        except Exception as e:
            logger.error(f"Failed to load #{sample_id}: {path}")
            logger.error(f"Errors encountered: {e}")
            print(traceback.format_exc())
            return None

        return slices

    def prepare_brain(self, img_nib):
        def preprocess_slice(img_slice):
            img_nib_slice = Nifti1Image(img_slice.squeeze(), affine=img_nib.affine)
            img_slice_trans = []
            for masker in self.roi_maskers:
                img_slice_trans.append(masker.transform(img_nib_slice).squeeze())
            return img_slice_trans

        # Dropping one 2d slice to have equal 3D slices later
        img = img_nib.get_data()
        # img = img[:,:,:-1]
        img[np.isnan(img)] = 0.0
        img = (img - img.min())/(img.max() - img.min() + 1e-6)
        # img = np.expand_dims(img, axis=0)
        # if self.mode == 'Train':
        #     img = self.augment_image(img)
        # if self.mode == 'Train':
        #     img = self.randomCrop(img,96,96,96)
        # else:
        #     img = self.centerCrop(img,96,96,96)

        # breakpoint()
        # slices = np.array_split(img, self.num_slices, axis=-1)

        slices = np.lib.stride_tricks.sliding_window_view(
            img,
            window_shape=(*img.shape[:-1], self.depth_of_slice),
            axis=(0,1,2)
        ).squeeze()

        slices = slices[::self.slicing_stride, :, :, :]

        data = [[] for _ in self.roi_maskers]

        if self.parallized_brains:
            mapping_func = map
            # logger.info(f'processing {len(slices)} slices sequentially')
        else:
            mapping_func = lambda func, itr: self.threading_pool.map(
                func, itr, chunksize=self.n_processes
            )
            # logger.info(f'processing {len(slices)} slices in parallel')

        for ss in mapping_func(preprocess_slice, slices):
            for r_i, s in enumerate(ss):
                data[r_i].append(s)

        for r_i in range(len(self.roi_maskers)):
            data[r_i] = torch.tensor(
                np.array(data[r_i]),
                dtype=float32
            ).to_dense()
        return data

    # def centerCrop(self, img, length, width, height):
    #     assert img.shape[1] >= length
    #     assert img.shape[2] >= width
    #     assert img.shape[3] >= height

    #     x = img.shape[1]//2 - length//2
    #     y = img.shape[2]//2 - width//2
    #     z = img.shape[3]//2 - height//2
    #     img = img[:,x:x+length, y:y+width, z:z+height]
    #     return img

    # def randomCrop(self, img, length, width, height):
    #     assert img.shape[1] >= length
    #     assert img.shape[2] >= width
    #     assert img.shape[3] >= height
    #     x = random.randint(0, img.shape[1] - length)
    #     y = random.randint(0, img.shape[2] - width)
    #     z = random.randint(0, img.shape[3] - height )
    #     img = img[:,x:x+length, y:y+width, z:z+height]
    #     return img


import ast
import os.path as osp
import platform
import shutil
import sys
import warnings
import tempfile
from argparse import ArgumentParser
from collections import abc
from importlib import import_module
import re
from addict import Dict
from mmcv.utils.misc import import_modules_from_strings
from mmcv.utils.path import check_file_exist
from yapf.yapflib.yapf_api import FormatCode

if platform.system() == "Windows":
    import regex as re
else:
    import re
from src.utils.web_utils import load_text_from_web, check_url_exist
from src.utils.web_utils import shortname as web_shortcut

BASE_KEY = "_base_"
DELETE_KEY = "_delete_"
RESERVED_KEYS = ["filename", "text", "pretty_text"]

def check_path_type(filepath):
    if ":" in filepath:
        if filepath.startswith("file:"):
            return "abs_path"
        elif filepath.startswith("https:") or filepath.startswith("http:"):
            return "web_url"
        elif filepath.split(":")[0] in web_shortcut:
            return "web_shortcut"
        else:
            raise ValueError(f"Unrecognized file type for file {filepath}")

    else:
        return "rel_path"

def find_correct_path(cur_dir, filepath):
    path_type = check_path_type(filepath)
    if path_type == "rel_path":
        return osp.join(cur_dir, filepath)
    elif path_type == "abs_path":
        # remove indicator
        return filepath.split(":")[1]
    elif path_type == "web_shortcut":
        # replace indicator
        shortcut = filepath.split(":")[0]
        return osp.join(web_shortcut[shortcut], filepath.split(":")[1])
    else:
        return filepath

def load_file(filepath):
    path_type = check_path_type(filepath)
    if path_type in ["abs_path", "rel_path"]:
        with open(filepath, "r") as f:
            config_file = f.read()
    elif path_type in ["web_url", "web_shortcut"]:
        return load_text_from_web(filepath)
    else:
        raise NotImplementedError(f"load {filepath} failed")
    return config_file

def copy_file(src_path, target_path):
    path_type = check_path_type(src_path)
    if path_type in ["abs_path", "rel_path"]:
        shutil.copyfile(src_path, target_path)
    elif path_type == "web_url":
        content = load_file(src_path)
        with tempfile.TemporaryDirectory() as temp_config_dir:
            temp_config_file = tempfile.NamedTemporaryFile(
                dir=temp_config_dir, suffix=osp.splitext(src_path)[1]
            )
            if platform.system() == "Windows":
                temp_config_file.close()
            with open(temp_config_file.name, "w") as f:
                f.write(content)
            shutil.copyfile(temp_config_file.name, target_path)
    else:
        raise NotImplementedError(f"{src_path} to {target_path} failed")

class ConfigDict(Dict):
    def __missing__(self, name):
        raise KeyError(name)

    def __getattr__(self, name):
        try:
            value = super(ConfigDict, self).__getattr__(name)
        except KeyError:
            ex = AttributeError(
                f"'{self.__class__.__name__}' object has no " f"attribute '{name}'"
            )
        except Exception as e:
            ex = e
        else:
            return value
        raise ex

class VariableParser(object):
    def __init__(self, symbol=["\{", "\}"]):
        self.symbol = symbol
        self.pattern = re.compile(self.general_pattern)

    def find_all(self, string):
        vars = self.pattern.findall(string)
        return vars

    def replace(self, string, values):
        for k, v in values.items():
            string = re.sub(self.specfic_pattern(k), v, string)
        return string

    @property
    def general_pattern(self):
        return self.symbol[0] + "(.+?)" + self.symbol[1]

    def specfic_pattern(self, word):
        return self.symbol[0] + word + self.symbol[1]

    def fetch_value(self, cfg_dict, key):
        if key not in cfg_dict:
            warnings.warn(
                "Attr {} not found in {}. Set it to None by default.".format(
                    key, type(cfg_dict)
                )
            )
            return "none"
        return cfg_dict[key]

    def parse(self, cfg, total_cfg_dict: ConfigDict):
        if isinstance(cfg, dict):
            return ConfigDict(
                {k: self.parse(v, total_cfg_dict) for k, v in cfg.items()}
            )
        elif isinstance(cfg, list):
            return [self.parse(v, total_cfg_dict) for v in cfg]
        elif isinstance(cfg, str):
            expr = self.find_all(cfg)
            if expr is not None:
                return self.replace(
                    cfg, {e: self.fetch_value(total_cfg_dict, e) for e in expr}
                )
        else:
            return cfg
        return cfg

def add_args(parser, cfg, prefix=""):
    for k, v in cfg.items():
        if isinstance(v, str):
            parser.add_argument("--" + prefix + k)
        elif isinstance(v, int):
            parser.add_argument("--" + prefix + k, type=int)
        elif isinstance(v, float):
            parser.add_argument("--" + prefix + k, type=float)
        elif isinstance(v, bool):
            parser.add_argument("--" + prefix + k, action="store_true")
        elif isinstance(v, dict):
            add_args(parser, v, prefix + k + ".")
        elif isinstance(v, abc.Iterable):
            parser.add_argument("--" + prefix + k, type=type(v[0]), nargs="+")
        else:
            print(f"cannot parse key {prefix + k} of type {type(v)}")
    return parser

class Config:
    """A facility for config and config files.

    It supports common file formats as configs: python/json/yaml. The interface
    is the same as a dict object and also allows access config values as
    attributes.

    Example:
        >>> cfg = Config(dict(a=1, b=dict(b1=[0, 1])))
        >>> cfg.a
        1
        >>> cfg.b
        {'b1': [0, 1]}
        >>> cfg.b.b1
        [0, 1]
        >>> cfg = Config.fromfile('tests/data/config/a.py')
        >>> cfg.filename
        "/home/kchen/projects/mmcv/tests/data/config/a.py"
        >>> cfg.item4
        'test'
        >>> cfg
        "Config [path: /home/kchen/projects/mmcv/tests/data/config/a.py]: "
        "{'item1': [1, 2], 'item2': {'a': 0}, 'item3': True, 'item4': 'test'}"
    """

    @staticmethod
    def _substitute_predefined_vars(filename, temp_config_name):
        file_dirname = osp.dirname(filename)
        file_basename = osp.basename(filename)
        file_basename_no_extension = osp.splitext(file_basename)[0]
        file_extname = osp.splitext(filename)[1]
        support_templates = dict(
            fileDirname=file_dirname,
            fileBasename=file_basename,
            fileBasenameNoExtension=file_basename_no_extension,
            fileExtname=file_extname,
        )
        config_file = load_file(filename)
        for key, value in support_templates.items():
            regexp = r"\{\{\s*" + str(key) + r"\s*\}\}"
            value = value.replace("\\", "/")
            config_file = re.sub(regexp, value, config_file)
        with open(temp_config_name, "w") as tmp_config_file:
            tmp_config_file.write(config_file)

    @staticmethod
    def _validate_py_syntax(filename):
        content = load_file(filename)
        try:
            ast.parse(content)
        except SyntaxError as e:
            raise SyntaxError(
                "There are syntax errors in config " f"file {filename}: {e}"
            )

    @staticmethod
    def _file2dict(filename, use_predefined_variables=True):
        file_type = check_path_type(filename)
        if file_type in ["rel_path", "abs_path"]:
            filename = osp.abspath(osp.expanduser(filename))
            check_file_exist(filename)
        else:
            check_url_exist(filename)

        fileExtname = osp.splitext(filename)[1]
        if fileExtname not in [".py", ".json", ".yaml", ".yml"]:
            raise IOError("Only py/yml/yaml/json type are supported now!")

        with tempfile.TemporaryDirectory() as temp_config_dir:
            temp_config_file = tempfile.NamedTemporaryFile(
                dir=temp_config_dir, suffix=fileExtname
            )
            if platform.system() == "Windows":
                temp_config_file.close()
            temp_config_name = osp.basename(temp_config_file.name)
            # Substitute predefined variables
            if use_predefined_variables:
                Config._substitute_predefined_vars(filename, temp_config_file.name)
            else:
                copy_file(filename, temp_config_file.name)

            if filename.endswith(".py"):
                temp_module_name = osp.splitext(temp_config_name)[0]
                sys.path.insert(0, temp_config_dir)
                Config._validate_py_syntax(filename)
                mod = import_module(temp_module_name)
                sys.path.pop(0)
                cfg_dict = {
                    name: value
                    for name, value in mod.__dict__.items()
                    if not name.startswith("__")
                }
                # delete imported module
                del sys.modules[temp_module_name]
            elif filename.endswith((".yml", ".yaml", ".json")):
                import mmcv

                cfg_dict = mmcv.load(temp_config_file.name)
            # close temp file
            temp_config_file.close()

        cfg_text = filename + "\n"
        cfg_text += load_file(filename)

        if BASE_KEY in cfg_dict:
            cfg_dir = osp.dirname(filename)
            base_filename = cfg_dict.pop(BASE_KEY)
            base_filename = (
                base_filename if isinstance(base_filename, list) else [base_filename]
            )

            cfg_dict_list = list()
            cfg_text_list = list()
            for f in base_filename:
                ref_file_path = find_correct_path(cfg_dir, f)
                _cfg_dict, _cfg_text = Config._file2dict(ref_file_path)
                cfg_dict_list.append(_cfg_dict)
                cfg_text_list.append(_cfg_text)

            base_cfg_dict = dict()
            for c in cfg_dict_list:
                if len(base_cfg_dict.keys() & c.keys()) > 0:
                    raise KeyError("Duplicate key is not allowed among bases")
                base_cfg_dict.update(c)

            base_cfg_dict = Config._merge_a_into_b(cfg_dict, base_cfg_dict)
            cfg_dict = base_cfg_dict

            # merge cfg_text
            cfg_text_list.append(cfg_text)
            cfg_text = "\n".join(cfg_text_list)

        return cfg_dict, cfg_text

    @staticmethod
    def _merge_a_into_b(a, b, allow_list_keys=False):
        """merge dict ``a`` into dict ``b`` (non-inplace).

        Values in ``a`` will overwrite ``b``. ``b`` is copied first to avoid
        in-place modifications.

        Args:
            a (dict): The source dict to be merged into ``b``.
            b (dict): The origin dict to be fetch keys from ``a``.
            allow_list_keys (bool): If True, int string keys (e.g. '0', '1')
              are allowed in source ``a`` and will replace the element of the
              corresponding index in b if b is a list. Default: False.

        Returns:
            dict: The modified dict of ``b`` using ``a``.

        Examples:
            # Normally merge a into b.
            >>> Config._merge_a_into_b(
            ...     dict(obj=dict(a=2)), dict(obj=dict(a=1)))
            {'obj': {'a': 2}}

            # Delete b first and merge a into b.
            >>> Config._merge_a_into_b(
            ...     dict(obj=dict(_delete_=True, a=2)), dict(obj=dict(a=1)))
            {'obj': {'a': 2}}

            # b is a list
            >>> Config._merge_a_into_b(
            ...     {'0': dict(a=2)}, [dict(a=1), dict(b=2)], True)
            [{'a': 2}, {'b': 2}]
        """
        b = b.copy()
        for k, v in a.items():
            if allow_list_keys and k.isdigit() and isinstance(b, list):
                k = int(k)
                if len(b) <= k:
                    raise KeyError(f"Index {k} exceeds the length of list {b}")
                b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
            elif isinstance(v, dict) and k in b and not v.pop(DELETE_KEY, False):
                allowed_types = (dict, list) if allow_list_keys else dict
                if not isinstance(b[k], allowed_types):
                    raise TypeError(
                        f"{k}={v} in child config cannot inherit from base "
                        f"because {k} is a dict in the child config but is of "
                        f"type {type(b[k])} in base config. You may set "
                        f"`{DELETE_KEY}=True` to ignore the base config"
                    )
                b[k] = Config._merge_a_into_b(v, b[k], allow_list_keys)
            else:
                b[k] = v
        return b

    @staticmethod
    def fromfile(filename, use_predefined_variables=True, import_custom_modules=True):
        cfg_dict, cfg_text = Config._file2dict(filename, use_predefined_variables)
        if import_custom_modules and cfg_dict.get("custom_imports", None):
            import_modules_from_strings(**cfg_dict["custom_imports"])
        return Config(cfg_dict, cfg_text=cfg_text, filename=filename)

    @staticmethod
    def auto_argparser(description=None):
        """Generate argparser from config file automatically (experimental)"""
        partial_parser = ArgumentParser(description=description)
        partial_parser.add_argument("config", help="config file path")
        cfg_file = partial_parser.parse_known_args()[0].config
        cfg = Config.fromfile(cfg_file)
        parser = ArgumentParser(description=description)
        parser.add_argument("config", help="config file path")
        add_args(parser, cfg)
        return parser, cfg

    def __init__(self, cfg_dict=None, cfg_text=None, filename=None):
        if cfg_dict is None:
            cfg_dict = dict()
        elif not isinstance(cfg_dict, dict):
            raise TypeError("cfg_dict must be a dict, but " f"got {type(cfg_dict)}")
        for key in cfg_dict:
            if key in RESERVED_KEYS:
                raise KeyError(f"{key} is reserved for config file")

        super(Config, self).__setattr__("_cfg_dict", ConfigDict(cfg_dict))
        super(Config, self).__setattr__("_filename", filename)
        if cfg_text:
            text = cfg_text
        elif filename:
            with open(filename, "r") as f:
                text = f.read()
        else:
            text = ""
        super(Config, self).__setattr__("_text", text)
        self._cfg_dict["exp_name"] = osp.basename(filename).split(".")[0]

    @property
    def filename(self):
        return self._filename

    @property
    def text(self):
        return self._text

    @property
    def pretty_text(self):

        indent = 4

        def _indent(s_, num_spaces):
            s = s_.split("\n")
            if len(s) == 1:
                return s_
            first = s.pop(0)
            s = [(num_spaces * " ") + line for line in s]
            s = "\n".join(s)
            s = first + "\n" + s
            return s

        def _format_basic_types(k, v, use_mapping=False):
            if isinstance(v, str):
                v_str = f"'{v}'"
            else:
                v_str = str(v)

            if use_mapping:
                k_str = f"'{k}'" if isinstance(k, str) else str(k)
                attr_str = f"{k_str}: {v_str}"
            else:
                attr_str = f"{str(k)}={v_str}"
            attr_str = _indent(attr_str, indent)

            return attr_str

        def _format_list(k, v, use_mapping=False):
            # check if all items in the list are dict
            if all(isinstance(_, dict) for _ in v):
                v_str = "[\n"
                v_str += "\n".join(
                    f"dict({_indent(_format_dict(v_), indent)})," for v_ in v
                ).rstrip(",")
                if use_mapping:
                    k_str = f"'{k}'" if isinstance(k, str) else str(k)
                    attr_str = f"{k_str}: {v_str}"
                else:
                    attr_str = f"{str(k)}={v_str}"
                attr_str = _indent(attr_str, indent) + "]"
            else:
                attr_str = _format_basic_types(k, v, use_mapping)
            return attr_str

        def _contain_invalid_identifier(dict_str):
            contain_invalid_identifier = False
            for key_name in dict_str:
                contain_invalid_identifier |= not str(key_name).isidentifier()
            return contain_invalid_identifier

        def _format_dict(input_dict, outest_level=False):
            r = ""
            s = []

            use_mapping = _contain_invalid_identifier(input_dict)
            if use_mapping:
                r += "{"
            for idx, (k, v) in enumerate(input_dict.items()):
                is_last = idx >= len(input_dict) - 1
                end = "" if outest_level or is_last else ","
                if isinstance(v, dict):
                    v_str = "\n" + _format_dict(v)
                    if use_mapping:
                        k_str = f"'{k}'" if isinstance(k, str) else str(k)
                        attr_str = f"{k_str}: dict({v_str}"
                    else:
                        attr_str = f"{str(k)}=dict({v_str}"
                    attr_str = _indent(attr_str, indent) + ")" + end
                elif isinstance(v, list):
                    attr_str = _format_list(k, v, use_mapping) + end
                else:
                    attr_str = _format_basic_types(k, v, use_mapping) + end

                s.append(attr_str)
            r += "\n".join(s)
            if use_mapping:
                r += "}"
            return r

        cfg_dict = self._cfg_dict.to_dict()
        text = _format_dict(cfg_dict, outest_level=True)
        # copied from setup.cfg
        yapf_style = dict(
            based_on_style="pep8",
            blank_line_before_nested_class_or_def=True,
            split_before_expression_after_opening_paren=True,
        )
        text, _ = FormatCode(text, style_config=yapf_style, verify=True)

        return text

    def __repr__(self):
        return f"Config (path: {self.filename}): {self._cfg_dict.__repr__()}"

    def __len__(self):
        return len(self._cfg_dict)

    def __getattr__(self, name):
        return getattr(self._cfg_dict, name)

    def __getitem__(self, name):
        return self._cfg_dict.__getitem__(name)

    def __setattr__(self, name, value):
        if isinstance(value, dict):
            value = ConfigDict(value)
        self._cfg_dict.__setattr__(name, value)

    def __setitem__(self, name, value):
        if isinstance(value, dict):
            value = ConfigDict(value)
        self._cfg_dict.__setitem__(name, value)

    def __iter__(self):
        return iter(self._cfg_dict)

    def __getstate__(self):
        return (self._cfg_dict, self._filename, self._text)

    def __setstate__(self, state):
        _cfg_dict, _filename, _text = state
        super(Config, self).__setattr__("_cfg_dict", _cfg_dict)
        super(Config, self).__setattr__("_filename", _filename)
        super(Config, self).__setattr__("_text", _text)

    def dump(self, file=None):
        cfg_dict = super(Config, self).__getattribute__("_cfg_dict").to_dict()
        if self.filename.endswith(".py"):
            if file is None:
                return self.pretty_text
            else:
                with open(file, "w") as f:
                    f.write(self.pretty_text)
        else:
            import mmcv

            if file is None:
                file_format = self.filename.split(".")[-1]
                return mmcv.dump(cfg_dict, file_format=file_format)
            else:
                mmcv.dump(cfg_dict, file)

    def merge_from_dict(self, options, allow_list_keys=True):
        """Merge list into cfg_dict.

        Merge the dict parsed by MultipleKVAction into this cfg.

        Examples:
            >>> options = {'model.backbone.depth': 50,
            ...            'model.backbone.with_cp':True}
            >>> cfg = Config(dict(model=dict(backbone=dict(type='ResNet'))))
            >>> cfg.merge_from_dict(options)
            >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict')
            >>> assert cfg_dict == dict(
            ...     model=dict(backbone=dict(depth=50, with_cp=True)))

            # Merge list element
            >>> cfg = Config(dict(pipeline=[
            ...     dict(type='LoadImage'), dict(type='LoadAnnotations')]))
            >>> options = dict(pipeline={'0': dict(type='SelfLoadImage')})
            >>> cfg.merge_from_dict(options, allow_list_keys=True)
            >>> cfg_dict = super(Config, self).__getattribute__('_cfg_dict')
            >>> assert cfg_dict == dict(pipeline=[
            ...     dict(type='SelfLoadImage'), dict(type='LoadAnnotations')])

        Args:
            options (dict): dict of configs to merge from.
            allow_list_keys (bool): If True, int string keys (e.g. '0', '1')
              are allowed in ``options`` and will replace the element of the
              corresponding index in the config if the config is a list.
              Default: True.
        """
        option_cfg_dict = {}
        for full_key, v in options.items():
            d = option_cfg_dict
            key_list = full_key.split(".")
            for subkey in key_list[:-1]:
                d.setdefault(subkey, ConfigDict())
                d = d[subkey]
            subkey = key_list[-1]
            d[subkey] = v

        cfg_dict = super(Config, self).__getattribute__("_cfg_dict")
        super(Config, self).__setattr__(
            "_cfg_dict",
            Config._merge_a_into_b(
                option_cfg_dict, cfg_dict, allow_list_keys=allow_list_keys
            ),
        )

    def build(self):
        var_parser = VariableParser()
        cfg_dict = var_parser.parse(self._cfg_dict, self._cfg_dict)
        super(Config, self).__setattr__("_cfg_dict", cfg_dict)


from django.contrib import admin
from django.utils.translation import ugettext_lazy as _

from .models import ServiceCategory, Service

class ServiceCategoryAdmin(admin.ModelAdmin):
	pass

class ServiceAdmin(admin.ModelAdmin):
    list_display = (
        'name', 'category',
        'price',
        'due_period', 'reminder_day_count_before_due_date', 'reminder_day_count_after_due_date',
    )
    list_filter = ('category',)
    search_fields = ('name',)
    fieldsets = (
        (_('Basic Profile'), {'fields': ('category', 'name',)}),
        (_('Price Fields'), {'fields': ('price',)}),
        (_('Reminder Fields'),
        	{'fields': ('due_period', 'reminder_day_count_before_due_date', 'reminder_day_count_after_due_date',)}),
    )

admin.site.register(ServiceCategory, ServiceCategoryAdmin)
admin.site.register(Service, ServiceAdmin)


"""
Aida Muratoglu
4/6/21
M6: Poetry Slam

Writes new poems and evaluates them using natural language processing tools
and poetic lineation rules.
"""

from numpy import random
import numpy as np
from build_library import build_library
from Poem import Poem
import pyttsx3

def write_poetry():
    """Writes and evaluates 10 new poems. Returns the best one.
        Returns:
            poems (list): 10 new poems.
    """
    x = 0
    new_poems = []
    frags = call_fragments()
    verbs = get_verbs(frags)
    nouns = get_noun_phrases(frags)
    print("Writing poems...")

    while x < 10:
        num_nouns = random.randint(1, 20)
        num_verbs = random.randint(1, 20)
        # Makes a proto-poem (nouns and verbs in a list).
        list_poem = make_list_poem(verbs, nouns, num_nouns, num_verbs)
        line_breaks = random.randint(0, 15)
        y = 0
        while y < line_breaks:
            list_poem.insert(random.randint(0, len(list_poem)), "\n]")
            y += 1
        text = " ".join(list_poem)
        new_poem = Poem(text, num_nouns, num_verbs)
        new_poems.append(new_poem)
        x += 1

    return new_poems

def make_list_poem(verbs, nouns, num_nouns, num_verbs):
    """Populates list with random number of noun phrases and verbs at random
        intervals (i.e. noun noun verb noun verb verb, etc.).
        Args:
            verbs (list): verbs to use
            nouns (list): nouns to use
            num_nouns (int): number of noun phrases
            num_verbs (int): number of verbs

        Returns:
            list_poem (list): noun phrases and verbs as list
    """
    # Chooses random number of verbs and nouns for new poem
    list_poem = []
    y = 0
    while y < num_nouns + num_verbs:
            # If randint is False (0), place a verb from list
            if (random.randint(0, 1) == False):
                list_poem.append(random.choice(verbs))
            # If randint is True (1), place a noun from list
            list_poem.append(random.choice(nouns))
            y += 1
    return list_poem

def call_fragments():
    """Chooses a random number of Poems to pull from the library and returns
        them as a list.
        Returns:
            frags (list): list of Poems
    """
    print("Choosing fragments...")
    # Generates a random number
    num_frag = random.randint(1, 15)
    frags = []
    library = build_library()
    x = 0
    # Populates a list with the number of fragments (chosen at random)
    while x < num_frag:
        frags.append(random.choice(library))
        x += 1
    return frags

def get_verbs(frags):
    """Uses  natural language processing calls from the Poem class to return
        all verbs in the given fragments.
        Args:
            frags (list): list of Poems

        Returns:
            verbs (list): list of verbs from fragments
    """
    print("Getting verbs...")
    verbs = []
    x = 0
    while x < len(frags):
        verbs.extend(frags[x].get_verbs())
        x += 1
    # If no verbs, start over
    if (len(verbs) == 0):
        write_poetry()
    return verbs

def get_noun_phrases(frags):
    """Uses  natural language processing calls from the Poem class to return
        all noun phrases in the given fragments.
        Args:
            frags (list): list of Poems

        Returns:
            nouns (list): list of noun phrases from fragments
    """
    print("Getting nouns...")
    nouns = []
    x = 0
    while x < len(frags):
        nouns.extend(frags[x].get_noun_phrases())
        x += 1
    # If no nouns, start over
    if (len(nouns) == 0):
        write_poetry()
    return nouns

def get_all_scores(poems):
    """Prints all Poems with corresponding fitness scores.
        Returns:
            fitness_scores (dict): Poem objects and fitness scores.
    """
    fitness_scores = {}
    for poem in poems:
        fitness_scores[poem._evaluate()] = poem
    return fitness_scores

def choose_best_poem(poems):
    """Evaluates all poems using two parameters: ratio of noun phrases to verbs
        and line length. Returns best poem.

        Returns:
            best poem (according to fitness scores)
    """
    print("Choosing best poems...")
    fitness_scores = {}
    for poem in poems:
        fitness_scores[poem.evaluate()] = poem
        max_score = max(fitness_scores.keys())
    return fitness_scores[max_score]

def read_aloud(poem):
    """Reads poems aloud using text-to-speech software.

        Args:
            poem (str): poem to read aloud
    """
    engine = pyttsx3.init()
    engine.setProperty('rate', 150)
    engine.say(poem.text)
    engine.runAndWait()

def main():
    poems = write_poetry()
    print("Running program...")

    best = choose_best_poem(poems)

    best_or_rank = input("See best poem or rank all poems? Enter b or r: ")
    while best_or_rank != "b" and best_or_rank != "r":
        best_or_rank = input("See best poem or rank all poems? Enter b or r: ")

    print_or_save = input("Print or save the poem? Enter p or s: ")
    while print_or_save != "p" and print_or_save != "s":
        print_or_save = input("Print or save the poem? Enter p or s: ")

    if best_or_rank == "b":
        if print_or_save == "p":
            print(best)

        if print_or_save == "s":
            best.make_text_file()

    if best_or_rank == "r":
        if print_or_save == "p":
            for poem in poems:
                print(poem)
                print("Fitness score = ", poem.evaluate())

        if print_or_save == "s":
            poem_files = []
            for poem in poems:
                poem_files.append(poem.make_text_file())

    print("Reading best poem out loud...")
    # read out loud!
    read_aloud(best)

if __name__ == "__main__":
    main()


from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
from django.utils.functional import wraps
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from cyano.models import Entry

import cyano.models as models
from cyano.helpers import render_queryset_to_response_error, getModel

def __assign_if_not_false(kw, key, obj):
    if obj:
        kw[key] = obj

def resolve_to_objects(function):
    @wraps(function)
    def wrapper(request, *args, **kw):
        # False instead of None, None is default value for passed but not set
        species_wid = kw.pop("species_wid", False)
        model_type = kw.pop("model_type", False)
        wid = kw.pop("wid", False)

        species = False
        model = False
        item = False

        if species_wid:
            species = models.Species.objects.for_wid(species_wid, get=False)
            try:
                species = species.get()
            except ObjectDoesNotExist as e:
                return render_queryset_to_response_error(
                    request,
                    error=404,
                    msg="The requested species \"{}\" was not found.".format(species_wid),
                    msg_debug=repr(e))
            except MultipleObjectsReturned as e:
                return render_queryset_to_response_error(
                    request,
                    error=500,
                    msg="Database error for species \"{}\". Please report this to an administrator!.".format(
                        species_wid),
                    msg_debug=repr(e))
        if model_type:
            model = getModel(model_type)
            # issubclass test needed to allow /add/Species/ but not /species_wid/Species/
            if model is None or (species_wid and not issubclass(model, models.SpeciesComponent)):
                return render_queryset_to_response_error(
                    request,
                    species=species,
                    error=404,
                    msg="The requested species \"{}\" has no model \"{}\".".format(species_wid, model_type))
        if model and wid:
            item = model.objects.for_species(species).for_wid(wid, get=False)
            try:
                item = item.get()
            except ObjectDoesNotExist as e:
                return render_queryset_to_response_error(
                    request,
                    error=404,
                    species=species,
                    model=model,
                    msg="The requested species \"{}\" has no item \"{}\" of type \"{}\".".format(species_wid, wid,
                                                                                model_type),
                    msg_debug=repr(e))
            except MultipleObjectsReturned as e:
                return render_queryset_to_response_error(
                    request,
                    error=500,
                    species=species,
                    model=model,
                    msg="Database error for species \"{}\" accessing item \"{}\" of type \"{}\"."
                        " Please report this to an administrator!".format(
                        species_wid, wid, model_type),
                    msg_debug=repr(e))

        # Prepare keyword arguments
        __assign_if_not_false(kw, "species", species)
        __assign_if_not_false(kw, "model", model)
        __assign_if_not_false(kw, "item", item)

        return function(request, *args, **kw)

    return wrapper

def global_permission_required(permission):
    """Checks whether a user has a permission for the specified entry
    """

    def decorator(function):
        @wraps(function)
        def wrapper(request, *args, **kw):
            from cyano.views import login

            user = request.user

            if not user.is_authenticated():
                # Special handling for guests
                user = User.objects.get(username="guest")

            # Admins always have full access
            if user.is_superuser:
                return function(request, *args, **kw)

            # Global perm check
            if user.profile.has_perm(permission):
                return function(request, *args, **kw)

            # No perm fallthrough
            msg = "You need global permission \"{}\"".format(permission)

            return login(
                request,
                species=None,
                error=403,
                message=msg,
                force_next=request.build_absolute_uri(),
                required_perm=permission)

        return wrapper

    return decorator

def permission_required(permission):
    """Checks whether a user has a permission for the specified entry
    """

    def decorator(function):
        @wraps(function)
        def wrapper(request, *args, **kw):
            from cyano.views import login

            species = kw.get("species", False)
            item = kw.get("item", False)

            user = request.user

            if not user.is_authenticated():
                # Special handling for guests
                user = User.objects.get(username="guest")
            else:
                pass

            # Admins always have full access
            if user.is_superuser:
                return function(request, *args, **kw)

            if species:
                species_old_cls = species.__class__
                species.__class__ = Entry
                allow_item = None

                if item:
                    # __class__ assignment: 'Entry' object layout differs from 'ModelBase'#
                    #item_old_cls = item.__class__
                    #item = item.__class__ = Entry
                    entry_item = Entry.objects.get(pk=item.pk)
                    allow_item = user.profile.has_perm(permission, entry_item)
                    #item.__class__ = item_old_cls

                allow_species = user.profile.has_perm(permission, species)
                species._class__ = species_old_cls

                if allow_species or allow_item:
                    # Has permission
                    return function(request, *args, **kw)
            else:
                raise ValueError("Species required for permission check. Use global_permission_required to check for global permissions.")

            # No perm fallthrough
            msg = "You need permission \"{}\" to access ".format(permission)

            if item:
                msg += "item {} of ".format(item.wid)

            msg += "species {}".format(species.wid)

            return login(
                request,
                species=species,
                error=403,
                message=msg,
                force_next=request.build_absolute_uri(),
                required_perm=permission)

        return wrapper

    return decorator

def ajax_required(f):
    """
    AJAX request required decorator
    use it in your views:

    @ajax_required
    def my_view(request):
    ....

    via http://djangosnippets.org/snippets/771/
    """
    def wrap(request, *args, **kwargs):
        from django.http import HttpResponseBadRequest
        if not request.is_ajax():
            return HttpResponseBadRequest("Ajax only")
        return f(request, *args, **kwargs)
    wrap.__doc__ = f.__doc__
    wrap.__name__ = f.__name__
    return wrap

def redirect_api_request(target, target_args=list()):
    def decorator(function):
        @wraps(function)
        def wrapper(request, *args, **kw):
            format = request.GET.get('format', 'html')
            if format in ['xml', 'json']:
                return HttpResponseRedirect("%s?format=%s" % (
                    reverse(target, args=map(lambda x: kw[x], target_args)), format)
                )

            return function(request, *args, **kw)

        return wrapper

    return decorator


# -*- coding:utf-8 -*-
import sys
import re

def deletingForDelComments(code, listIter, delList):
    for iter in listIter:
        delList.append(code[iter.span()[0]:iter.span()[1]])
    for item in delList:
        code = code.replace(item, '')
    return  code

def delComments(code):
    delList = []
    listIter = re.finditer(r'\'[^\']*\'', code)
    code = deletingForDelComments(code, listIter, delList)

    listIter = re.finditer(r'\"[^\"]*\"', code)
    for iter in listIter:
        delList.append(code[iter.span()[0]:iter.span()[1]])
    for item in delList:
        code = code.replace(item, '')

    delList = []
    listIter = re.finditer(r'//.*\n', code)
    for iter in listIter:
        delList.append(code[iter.span()[0]:iter.span()[1]])
    for item in delList:
        code = code.replace(item[:len(item) - 1], '')

    delList = []
    listIter = re.finditer(r'/\*[\s\S]*\*/', code)
    code = deletingForDelComments(code, listIter, delList)

    print('-----------------corrected code------------------------\n'+code)
    return code

def allocateVaraible(listVarOfType, listOfString,regPhrase):
    for i in range(len(listOfString)):
        listOfString[i] = listOfString[i][len(regPhrase)+1:].strip()
        listVarOfType += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', listOfString[i])
        for j in range(len(listVarOfType)):
            if '[' in listVarOfType[j]:
                listVarOfType[j] = listVarOfType[j][:listVarOfType[j].find('[')]
    return listVarOfType

def checkOfInputVarType(regPhrase,code):
    listOfString = []
    listVarOfType=[]
    listOfString += re.findall(r'%s\(.*\)' % regPhrase, code)
    allocateVaraible(listVarOfType, listOfString, regPhrase)
    return listVarOfType

def checkOfThirdVarType(regPhrase,code):
    listOfNeedString=[]
    listVarOfType = []
    listOfNeedString += re.findall(r'%s\s*\(.*\)' % regPhrase, code)
    allocateVaraible(listVarOfType, listOfNeedString, regPhrase)
    return listVarOfType

def workWithTypesOfVal (listVarOfType,allVariable,firstColumn,secondColumn):
    columnOfVar = 0
    for i in range(len(listVarOfType)):
        for j in range(len(allVariable)):
            if listVarOfType[i] in allVariable[j][columnOfVar]:
                allVariable[j][firstColumn] = 1
                allVariable[j][secondColumn] = 0
                #len(listVarOfType[i]) == len(allVariable[j][columnOfVar]) and

def chepinMetrick(code):
    constList = []
    constListString = []
    listOfVariableString = []
    columOfVarName = 0
    columOfInputVar = 1
    columOfModyfyVar = 2
    columOfRuledVar = 3
    columOfEmptyVar = 4
    listOfType = ['int', 'signed',  'unsigned', 'short', 'long',  'char', 'float', 'double']

    for item in listOfType:
        listOfVariableString += (re.findall(r'%s \s*.*[^{];' % item, code))
        for i in range(len(listOfVariableString)):
             if item in listOfVariableString[i]:
                listOfVariableString[i] = listOfVariableString[i][len(item):].strip()

    constListString += (re.findall(r'DEFINE \s*.*[^{];|define \s*.*[^{];', code))
    for i in range(len(constListString)):
        if 'define' in constListString[i]:
            constListString[i] = constListString[i][len('DEFINE'):].strip()

    listOfVariableTemp=[]
    for item in listOfVariableString:
        listOfVariableTemp += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', item)

    for i in range(len(listOfVariableTemp)):
        if '[' in listOfVariableTemp[i]:
            listOfVariableTemp[i] = listOfVariableTemp[i][:listOfVariableTemp[i].find('[')]

    listOfVariable = []
    for item in listOfVariableTemp:
        if item not in listOfVariable:
            listOfVariable += [item]

    for i in range(len(listOfVariable)):
        if '[' in listOfVariable[i]:
            listOfVariable[i] = listOfVariable[i][:listOfVariable[i].find('[')]

    for item in constListString:
        constList += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', item)

    for item in constList:
        listOfVariable += [item]

    variableInMetrick = []
    for item in listOfVariable:
        variableInMetrick += [[item,0,0,0,1]]

    listVarOfThirdtype=[]

    listVarOfThirdtype += checkOfThirdVarType('if',code)
    listVarOfThirdtype += checkOfThirdVarType('while',code)
    listVarOfThirdtype += checkOfThirdVarType('for',code)

    workWithTypesOfVal(listVarOfThirdtype, variableInMetrick, columOfRuledVar, columOfEmptyVar)

    listVarOfType = []
    metricList = []
    metricList += re.findall(r'=[^;\n]*',code)
    for i in range(len(metricList)):
        metricList[i] = metricList[i].strip()
        listVarOfType += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', metricList[i])
        for j in range(len(listVarOfType)):
            if '[' in listVarOfType[j]:
                listVarOfType[j] = listVarOfType[j][:listVarOfType[j].find('[')]

    workWithTypesOfVal(listVarOfType,variableInMetrick,columOfEmptyVar,columOfEmptyVar)

    metricList = []
    listVarOfType=[]
    metricList += re.findall(r'scanf\(.*\)',code)
    for i in range(len(metricList)):
        metricList[i] = metricList[i][len('scanf ('):].strip()
        listVarOfType += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', metricList[i])
        for j in range(len(listVarOfType)):
            if '&' in listVarOfType[j]:
                listVarOfType[j] = listVarOfType[j][:listVarOfType[j].find('[')]
            if '[' in listVarOfType[j]:
                listVarOfType[j] = listVarOfType[j][:listVarOfType[j].find('[')]

    workWithTypesOfVal(listVarOfType, variableInMetrick, columOfInputVar, columOfEmptyVar)

    listOfRegExpOfInputVar = ['gets', 'getchar', 'printf', 'puts', 'putchar']
    listVarOfType=[]

    for item in listOfRegExpOfInputVar:
        listVarOfType += checkOfInputVarType(item, code)

    workWithTypesOfVal(listVarOfType, variableInMetrick,columOfInputVar,columOfEmptyVar)

    listOfSecondType = []
    for i in range(len(variableInMetrick)):
        if  variableInMetrick[i][1] == 1:
            listOfSecondType += [variableInMetrick[i][0]]

    metricList = []
    listVarOfType=[]
    metricList += re.findall(r'[[a-zA-Z_]+[0-9]*]*[=+-][^;\n]*',code)
    for i in range(len(metricList)):
        metricList[i] = metricList[i][:metricList[i].find('=')].strip()
        listVarOfType += re.findall(r'[[a-zA-Z_]+[0-9]*]*(?=\s*,*=*\s*;*)', metricList[i])
        for j in range(len(listVarOfType)):
            if '[' in listVarOfType[j]:
                listVarOfType[j] = listVarOfType[j][:listVarOfType[j].find('[')]

    for i in range(len(listVarOfType)):
        for j in range(len(variableInMetrick)):
            if len(listVarOfType[i]) == len(variableInMetrick[j][0]) and listVarOfType[i] in variableInMetrick[j][0] and listVarOfType[i] in listOfSecondType :
                variableInMetrick[j][columOfModyfyVar] = 1
                variableInMetrick[j][columOfEmptyVar] = 0

    for item in variableInMetrick:
        print(item)

    delList=[]
    for i in range(len(variableInMetrick)):
        if ''== variableInMetrick[i][0]:
            delList.append(variableInMetrick[i])
    for item in delList:
        variableInMetrick.remove(item)

    countOfInput=0
    countOfModify=0
    countOfRuled=0
    countOfEmpty = 0
    print('\n')
    for item in variableInMetrick:
        if item[columOfInputVar] == item[columOfModyfyVar] == item[columOfRuledVar] == item[columOfEmptyVar] == 0:
            countOfModify += 1
            #print(item[columOfVarName],' - Modify variable')
        elif item[columOfRuledVar] == 1:
            countOfRuled += 1
            #print(item[columOfVarName],' - Ruled variable')
        elif item[columOfModyfyVar] == 1 and item[columOfRuledVar] == 0 :
            countOfModify += 1
            #print(item[columOfVarName],' - Modify variable')
        elif item[columOfInputVar] == 1 and item[columOfModyfyVar] == 0 and item [columOfRuledVar] == 0:
            countOfInput += 1
            #print(item[columOfVarName],' - Input variable')
        elif item[columOfEmptyVar]== 1 :
            countOfEmpty +=1
            #print(item[columOfVarName],' - Empty variable')

    constCoefficientOfInputVar = 1
    constCoefficientOFModifyVar = 2
    constCoefficientOFRuledVar = 3
    constCoefficientOfEmptyVar = 0.5
    valueOfMetick = constCoefficientOfInputVar*countOfInput+constCoefficientOFModifyVar*abs(countOfModify)+constCoefficientOFRuledVar*countOfRuled+constCoefficientOfEmptyVar*countOfEmpty
    print('\nP(count of Input )=',countOfInput,';\n M(count of Modify)=', countOfModify, ';\n C(count of Ruled)=', countOfRuled, ';\n T(count of Empty)=', countOfEmpty,'\n')
    print('Value of Chepin Metrick = ', valueOfMetick)

def main():
    choice = None
    code = None
    while choice != 0:
        print("""
        1 - Проанализировать метрикой Чепина
        2 - Открыть файл с кодом (язык C)
        0 - Выйти
        """)
        try:
            choice = input("Ваш выбор: ")
        except:
            choice = None
        if choice == '1':
            if code:
                chepinMetrick(code)
            else:
                print("File with code is not loaded!")
        elif choice == '2':
            code = openfile()
            if code:
                print("-*-*-*-*-*-*-*-*-*code-*-*-*-*-*-*-*-*-*-*-*-")
                print(code)
                print('\n')
                code = delComments(code)

            else:
                print("Код недоступен")
        elif choice == '0':
            sys.exit()

def openfile():
    fileName ='test3.txt'
       #input("Введите имя файла: ")

    try:
        f = open(fileName, "r")
        code = f.read()
    except FileNotFoundError:
        code = None
        f = None
    if f:
        print("File is loaded!")

    return code

if __name__ == "__main__":
    main()


from euclid import Point3, Vector3, Matrix4
import game.model.collision as collision
from game.model import components
from game.graphics import shapes
from game import settings
import game

class ColorObject(components.Position):
    def __init__(self, *args, **kwargs):
        if "color" in kwargs:
            self.color = kwargs["color"]
            del kwargs["color"]
        else:
            self.color = (0.5, 0.5, 0.5)
        super(ColorObject, self).__init__(*args, **kwargs)

class SpawnPoint(ColorObject):

    fields = ["spawnable"]
    alterable = True

    def __init__(self, team=None, **kwargs):
        super(SpawnPoint, self).__init__(**kwargs)
        x = y = z = 10
        self.oobb = collision.BoundingBox(-x, x, -y, y, -z, z)
        self.aabb = self.oobb.transformed(self.forward, self.up)
        self.team = team
        self.spawnable = True
        self.alive = True

    def __str__(self):
        return "{0} spawn point {1}".format("neutral" if self.is_neutral() else self.team, id(self))
    __repr__ = __str__

    def can_spawn(self):
        return self.spawnable

    def is_neutral(self):
        return self.team is None

    def for_team(self, team, team_name = None):
        if team_name is not None:
            return self.team == team_name
        else:
            return self.is_neutral() or self.team == team.team_name

class SpawnTarget(components.Position):

    fields = ["health"]

    alterable = True

    def __init__(self, gate, position, forward, up, color):
        self.gate = gate
        self.health = 100
        self.side = 5
        super(SpawnTarget, self).__init__(position, forward, up)
        self.color = color
        self.oobb = collision.BoundingBox(-self.side/2, self.side/2, -self.side/2, self.side/2, -self.side/2, self.side/2)
        self.aabb = self.oobb.transformed(self.forward, self.up)

    def hit_by_laser(self, player):
        self.health -= 5

    def render(self):
        shape = shapes.Block.get(self.side, self.side, self.side, 1, 1, 1)
        if self.health > 1:
            tmp_color = Vector3(0,0,0)
            tmp_color.x = self.color.x * (self.health / 100.0)
            tmp_color.y = self.color.y * (self.health / 100.0)
            tmp_color.z = self.color.z * (self.health / 100.0)
            shape.draw(self.position, self.forward, color=tmp_color)
        else:
            shape.draw(self.position, self.forward, color=Vector3(1,1,1))

        if settings.DISPLAY_BBOXES:
            bbox = shapes.BBox.get(self.aabb)
            bbox.draw(self.position)

class SpawnGate(SpawnPoint):

    fields = ["down_gates"]
    alterable = True

    def __init__(self, **kwargs):
        super(SpawnGate, self).__init__(**kwargs)
        self.health = 100
        self.radius = 5
        self.inner_radius = 0.5
        self.slices = 5
        self.inner_slices = 8
        x = y = self.radius + self.inner_radius
        z = self.inner_radius
        self.oobb = collision.BoundingBox(-x, x, -y, y, -z, z)
        self.aabb = self.oobb.transformed(self.forward, self.up)

        self.target_points = None
        self.down_gates = 0

    def update(self, dt):
        if self.target_points is None:
            self.target_points = []
            target = game.manager.room.add_instance(SpawnTarget, gate = self, position = self.position + Vector3(10,0,0), forward = self.forward, up = self.up, color=self.color)
            self.target_points.append(target)
            target = game.manager.room.add_instance(SpawnTarget, gate = self, position = self.position + Vector3(-10,0,0), forward = self.forward, up = self.up, color=self.color)
            self.target_points.append(target)
            target = game.manager.room.add_instance(SpawnTarget, gate = self, position = self.position + Vector3(0,10,0), forward = self.forward, up = self.up, color=self.color)
            self.target_points.append(target)
            target = game.manager.room.add_instance(SpawnTarget, gate = self, position = self.position + Vector3(0,-10,0), forward = self.forward, up = self.up, color=self.color)
            self.target_points.append(target)

            #let team know that a gate of it's team exists
            game.manager.teams.teams[self.team].gates.append(self)

        self.down_gates = self._num_downed_gates()
        if self.down_gates == len(self.target_points):
            self.spawnable = False

    def _num_downed_gates(self):
        downed_targets = 0
        if self.target_points:
            for target in self.target_points:
                if target.health < 1:
                    downed_targets += 1
        return downed_targets

    def render(self):
        shape = shapes.Torus.get(self.radius,
                                 self.inner_radius,
                                 self.slices,
                                 self.inner_slices)
        if self.down_gates < 4:
            shape.draw(self.position, self.forward, color=self.color)
        else:
            shape.draw(self.position, self.forward, color=Vector3(0,0,0))


import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np
import warnings

# Credits to the contributors at fast.ai
def dropout_mask(x, sz, p):
    return x.new(*sz).bernoulli_(1-p).div_(1-p)

class RNNDropout(nn.Module):
    def __init__(self, p=0.5):
        super().__init__()
        self.p=p

    def forward(self, x):
        if not self.training or self.p == 0.:
            return x
        m = dropout_mask(x.data, (x.size(0), 1, x.size(2)), self.p)
        return x * m

class EmbeddingDropout(nn.Module):
    def __init__(self, emb, embed_p):
        super(EmbeddingDropout, self).__init__()
        self.emb = emb
        self.embed_p = embed_p
        self.pad_idx = self.emb.padding_idx
        if self.pad_idx is None: self.pad_idx = -1

    def forward(self, words, scale=None):
        if self.training and self.embed_p != 0:
            size = (self.emb.weight.size(0),1)
            mask = dropout_mask(self.emb.weight.data, size, self.embed_p)
            masked_embed = (self.emb.weight * mask)
        else:
            masked_embed = (self.emb.weight)
        if scale:
            masked_embed.mul_(scale)
        out = F.embedding(words, masked_embed, self.pad_idx, self.emb.max_norm,
                          self.emb.norm_type, self.emb.scale_grad_by_freq, self.emb.sparse)
        return out

class WeightDropout(nn.Module):
    def __init__(self, module, weight_p, layer_names=['weight_hh_l0']):
        super(WeightDropout, self).__init__()
        self.module,self.weight_p,self.layer_names = module,weight_p,layer_names
        for layer in self.layer_names:
            #Makes a copy of the weights of the selected layers.
            w = getattr(self.module, layer)
            self.register_parameter(f'{layer}_raw', nn.Parameter(w.data))
            self.module._parameters[layer] = F.dropout(w, p=self.weight_p, training=False)

    def _setweights(self):
        "Apply dropout to the raw weights."
        for layer in self.layer_names:
            raw_w = getattr(self, f'{layer}_raw')
            self.module._parameters[layer] = F.dropout(raw_w, p=self.weight_p, training=self.training)

    def forward(self, *args):
        self._setweights()
        with warnings.catch_warnings():
            #To avoid the warning that comes because the weights aren't flattened.
            warnings.simplefilter("ignore")
            return self.module.forward(*args)

    def reset(self):
        for layer in self.layer_names:
            raw_w = getattr(self, f'{layer}_raw')
            self.module._parameters[layer] = F.dropout(raw_w, p=self.weight_p, training=False)
        if hasattr(self.module, 'reset'): self.module.reset()


import numpy as np

class Oja:
    def __init__(self, variablesSize, eta=0.01):
        self.eta = eta
        self.w = np.random.random(size=(1,variablesSize))[0]
        self.w /= np.linalg.norm(self.w) # w vector norm = 1

    def trainRule(self, X, epochs):
        inflationUnemploymentW = []
        eta = self.eta #eta value will change
        printing = epochs/5000
        iteration = 0

        while iteration < epochs:
            if iteration % printing == 0:
                aux = [self.w[1], self.w[3]]
                inflationUnemploymentW.append(aux)
            wVal = self.w
            for var in X:
                s = np.dot(self.w, var.T)
                self.w = self.w + eta * s * (var - s * self.w)
            iteration += 1

            if np.abs(wVal - self.w).all() <= eta*10:
                eta /= 10
        return inflationUnemploymentW


import pytest
import subprocess
from deployer.env.go import GoEnvironment as Env

@pytest.fixture(scope="module")
def bootstrap():
    # make settable by env var
    current_env = subprocess.check_output("juju env", shell=True).strip()
    # make switchable by env var
    subprocess.check_call('juju bootstrap --upload-tools')
    return current_env

@pytest.yield_fixture(scope="module")
def juju_environment(bootstrap):

    current_env = subprocess.check_output("juju env", shell=True).strip()
    env = Env(current_env, None)
    yield Env
    env.reset(terminate_machines=True)


import os
import re

from argparse import ArgumentParser

parser = ArgumentParser()

parser.add_argument( '--original_cards', dest='original_cards', required=True, help='path to start from' )
parser.add_argument( '--output_dir', dest='output_dir', required=True, help='location to write new directories' )

options = parser.parse_args()

#mass_points = [200, 250, 300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2400, 2600, 2800, 3000, 3500, 4000]
mass_points = [3500, 4000]

base_name = 'ChargedResonance_WGToLNu'
#base_name = 'ChargedResonance_WGToJJ'

def main() :

    for mass_point in mass_points :

        # this is the new directory name
        dir_name = '%s_M%d' %( base_name, mass_point)

        dir_path = '%s/%s' %( options.output_dir, dir_name )

        # make the directory if it hasn't been made
        # note that if the directory exists it'll be overwritten
        if not os.path.isdir( dir_path ) :
            os.mkdir( dir_path )

        # find the mass point of the original directory
        original_name = options.original_cards.rstrip('/').split('/')[-1]
        original_mass = int(re.match( '%s_M(\d{3,4})' %base_name, original_name ).group(1))

        # copy the cards over, modifying the mass point in the name
        for card_file in os.listdir( options.original_cards ) :

            new_file = card_file.replace( str(original_mass), str( mass_point ) )

            os.system( 'cp %s/%s %s/%s/%s' %( options.original_cards, card_file, options.output_dir, dir_name, new_file ) )

            # modify the mass in the customize_card
            if card_file.count( 'customizecards' ) :
                tmp_cust_card = new_file + '.save'

                os.system( 'mv %s/%s/%s %s/%s/%s ' %( options.output_dir, dir_name, new_file, options.output_dir, dir_name, tmp_cust_card ) )

                infile = open( '%s/%s/%s' %( options.output_dir, dir_name, tmp_cust_card ), 'r' )
                outfile = open( '%s/%s/%s' %( options.output_dir, dir_name, new_file ), 'w' )

                for line in infile :

                    if line.count( '9000007')  :
                        outfile.write( 'set param_card mass 9000007 %f \n' %( float( mass_point ) ) )
                    else :
                        outfile.write( '%s' %line )

                infile.close()
                outfile.close()

            # modify the output in the proc_card
            if card_file.count( 'proc_card' ) :

                tmp_proc_card = new_file + '.save'

                os.system( 'mv %s/%s/%s %s/%s/%s ' %( options.output_dir, dir_name, new_file, options.output_dir, dir_name, tmp_proc_card ) )

                infile = open( '%s/%s/%s' %( options.output_dir, dir_name, tmp_proc_card ), 'r' )
                outfile = open( '%s/%s/%s' %( options.output_dir, dir_name, new_file ), 'w' )

                for line in infile :
                    if line.count('output') > 0 :
                        outfile.write( 'output %s \n' %( dir_name ) )
                    else :
                        outfile.write( line )

                infile.close()
                outfile.close()

main()


#euler63

#naive attempt

def num_dig(n):
	return len(list(str(n)))

base = 2
exp = 1
count = 1
while True:
	base_done = False
	while not base_done:

		result = base**exp
		digits = num_dig(result)

		#print(str(base) + "^" + str(exp) + " = " + str(result) + " has " + str(digits) + " digits")

		if exp == digits:
			print("Solution " + str(count) + ": " + str(base) + "^" + str(exp) + " = " + str(result) + " has " + str(digits) + " digits")
			count += 1
		elif base == 10 and exp == 1:
			print("Solution " + str(count) + ": " + str(base) + "^" + str(exp) + " = " + str(result) + " has " + str(digits) + " digits")
		elif base < 10:
			if exp > digits:
				base += 1
				exp = 0
				base_done = True
		else:
			if exp < digits:
				base += 1
				exp = 0
				base_done = True

		exp += 1
		#input()


import numpy as np
import random
from entities import Agent, Target
import operator
import math
import matplotlib.pylab as plt

class Gridworld():
    def __init__(self, width, height, n_agents):
        self.width = width
        self.height = height
        self.n_agents = n_agents
        self.agent1 = Agent()
        self.agent2 = Agent()
        self.target = Target()
        self.grid = np.zeros([self.width, self.height])

        self.set_agent_pos()
        self.set_target_pos()

        self.obs = []

    def set_agent_pos(self):
        pos_x = self.agent1.pos_x
        pos_y = self.agent1.pos_y
        pos_x2 = self.agent2.pos_x
        pos_y2 = self.agent2.pos_y
        self.grid[pos_x][pos_y] = 1
        self.grid[pos_x2][pos_y2] = 2

    def set_target_pos(self, random_pos=False):
        if random_pos:
            pos_x = random.randint(0, self.width)
            pos_y = random.randint(0, self.height)
            self.target.pos_x = pos_x  #check if this is correct
            self.target.pos_y = pos_y  #check if thi is correct
        else:
            pos_x = self.target.pos_x
            pos_y = self.target.pos_y
        self.grid[pos_x][pos_y] = 8

    def step(self, action, action2):
        done = False
        # Individual rewards
        reward = 0
        reward_2 = 0
        reward -= 1  # negative reward for each step
        reward_2 -= 1

        # generate next action
        self.agent1.update_pos(action)
        self.agent2.update_pos(action2)

        # update positions in the grid world
        self.grid_reset()
        self.set_agent_pos()

        # dist = math.sqrt((self.agent1.pos_x - self.target.pos_x) ** 2 + (self.agent1.pos_y - self.target.pos_y) ** 2)
        # reward = reward - abs(0.1*dist)

        done1 = False
        done2 = False

        # if agent 1 reaches the target
        if self.agent1.pos_x == self.target.pos_x and self.agent1.pos_y == self.target.pos_y:
            reward += 20
            done1 = True

        # if agent2 reaches the target
        if self.agent2.pos_x == self.target.pos_x and self.agent2.pos_y == self.target.pos_y:
            reward_2 += 20
            done2 = True

        if (done1 == True) | (done2 == True):
            done = True

        # create observation vector of agent position(s) and target position
        observations = []
        observations.append(self.agent1.pos_x)  # agent1
        observations.append(self.agent1.pos_y)
        observations.append(self.agent2.pos_x)  # agent2
        observations.append(self.agent2.pos_y)
        observations.append(self.target.pos_x)
        observations.append(self.target.pos_y)

        #self.target.move_random()  # TODO: remove this for stationary target
        self.set_target_pos(random_pos=False)

        return observations, reward, reward_2, done

    def reset(self):
        self.agent1.reset()
        self.agent2.reset(self.agent1.pos_x, self.agent1.pos_y, use_given_start=True)  # true to use same starting location
        self.target.reset()

        observations = []
        observations.append(self.agent1.pos_x)  # agent1
        observations.append(self.agent1.pos_y)
        observations.append(self.agent2.pos_x)  # agent2
        observations.append(self.agent2.pos_y)
        observations.append(self.target.pos_x)
        observations.append(self.target.pos_y)
        return observations

    def grid_reset(self):
        self.grid = np.zeros([self.width, self.height])

if __name__ == "__main__":
    ### Args ###
    num_steps = 200
    num_episodes = 20000
    num_actions = 4

    gamma = 0.99
    lamda = 0.25
    epsilon = 0.05  # e-greedy

    use_system_reward = False

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

    game = Gridworld(10, 5, 1)
    episode_reward_list = []    # Agent1 iterate this is the one to plot per epoch
    episode_reward_list_2 = []  # Agent2
    system_reward_list = []

    # make a Q-table dictionary
    q_table = {}
    q_table_2 = {}

    for i in range(10):  # agent pos x
        for j in range(5):  # agent pos y
            for i2 in range(10):  # agent2 pos x
                for j2 in range(5):  # agent2 pos y
                    for k in range(10):  # target pos x
                        for l in range(5):  # target pos y
                            for a in range(4):  # actions
                                q_table[(i, j, i2, j2, k, l, a)] = 10
                                q_table_2[(i, j, i2, j2, k, l, a)] = 10
    print("Q-table initialized")

    for i_episode in range(num_episodes):
        obs = game.reset()  #reset at start of each episode

        episode_reward = 0  # agent1
        episode_reward_2 = 0  # agent2
        if use_system_reward:
            episode_system_reward = 0

        for t in range(num_steps):  #TODO: make args
            reward = 0
            reward_2 = 0

            ''' get actions for that state '''
            action_values = []
            action_values_2 = []

            # for agent 1
            for n in range(num_actions):
                action_values.append(q_table[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], n)])

            # for agent 2
            for n in range(num_actions):
                action_values_2.append(q_table_2[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], n)])

            # get max value for Agent1
            if all(action_values[0] == v for v in action_values):  # when all values are equal
                action = random.randint(0, num_actions-1)
                current_act_value = action_values[0]
            else:
                index, current_act_value = max(enumerate(action_values), key=operator.itemgetter(1))
                action = index

            # get max value for Agent2
            if all(action_values_2[0] == v for v in action_values_2):  # when all values are equal
                action_2 = random.randint(0, num_actions-1)
                current_act_value_2 = action_values_2[0]
            else:
                index2, current_act_value_2 = max(enumerate(action_values_2), key=operator.itemgetter(1))
                action_2 = index2

            # epsilon greedy agent1
            random_num = random.random()
            if random_num < epsilon:
                action = random.randint(0, 3)
            # epsilon greedy agent2
            if random_num < (1-epsilon):
                action_2 = random.randint(0, 3)

            # take one step
            next_obs, reward, reward_2, done = game.step(action, action_2)

            # accumulate rewards
            episode_reward = episode_reward + reward
            episode_reward_2 = episode_reward_2 + reward_2

            '''Use system reward (Part 3). Comment out if not using system reward'''
            # if use_system_reward:
            if use_system_reward:
                system_reward = reward + reward_2
                reward = system_reward
                reward_2 = system_reward
                episode_system_reward += system_reward

            ''' get value for next state Q(s', a') '''
            action_values = []
            action_values_2 = []

            for n in range(num_actions):
                action_values.append(q_table[(next_obs[0], next_obs[1], next_obs[2], next_obs[3], next_obs[4], next_obs[5], n)])
                action_values_2.append(q_table_2[(next_obs[0], next_obs[1], next_obs[2], next_obs[3], next_obs[4], next_obs[5], n)])

            # get max value
            _, next_act_value = max(enumerate(action_values), key=operator.itemgetter(1))  # we have the value for Q(s',a') now
            _, next_act_value_2 = max(enumerate(action_values_2), key=operator.itemgetter(1))

            # update the q-values
            q_table[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], action)] += lamda*(reward + gamma*next_act_value - current_act_value)
            q_table_2[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], action_2)] += lamda*(reward_2 + gamma*next_act_value_2 - current_act_value_2)
            print(q_table[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], action)])

            obs = next_obs
            if done:
                break

        if i_episode%100==0:
            print("Episode " + str(i_episode) + ". Total reward: " + str(episode_reward_2))
        episode_reward_list.append(episode_reward)
        episode_reward_list_2.append(episode_reward_2)
        if use_system_reward:
            system_reward_list.append(episode_system_reward)

    plt.plot(episode_reward_list)
    plt.title("Agent1 rewards vs episodes while training")
    plt.xlabel("Episodes")
    plt.ylabel("Total Episode Rewards")
    plt.show()

    plt.plot(episode_reward_list_2)
    plt.title("Agent2 rewards vs episodes while training")
    plt.xlabel("Episodes")
    plt.ylabel("Total Episode Rewards")
    plt.show()

    '''IF USING SYSTEM REWARD'''
    if use_system_reward:
        plt.plot(episode_reward_list_2)
        plt.title("System rewards vs episodes while training")
        plt.xlabel("Episodes")
        plt.ylabel("Total Episode Rewards")
        plt.show()

    ''' Now for testing '''
    test_episode_reward_list = []
    test_episode_reward_list_2 = []
    for i_episode in range(num_episodes):
        obs = game.reset()  #reset at start of each episode
        episode_reward = 0
        episode_reward_2 = 0
        for t in range(num_steps):
            ''' get actions for that state '''
            action_values = []
            action_values_2 = []
            for n in range(num_actions):
                action_values.append(q_table[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], n)])
                action_values_2.append(q_table_2[(obs[0], obs[1], obs[2], obs[3], obs[4], obs[5], n)])

            # get max valuefor Agent1
            if all(action_values[0] == v for v in action_values):  # when all values are equal
                action = random.randint(0, num_actions-1)
                current_act_value = action_values[0]
            else:
                index, current_act_value = max(enumerate(action_values), key=operator.itemgetter(1))
                action = index

            # get max value for Agent2
            if all(action_values_2[0] == v for v in action_values_2):  # when all values are equal
                action_2 = random.randint(0, num_actions-1)
                current_act_value_2 = action_values_2[0]
            else:
                index2, current_act_value_2 = max(enumerate(action_values_2), key=operator.itemgetter(1))
                action_2 = index2

            # take one step
            next_obs, reward, reward_2, done = game.step(action, action_2)
            # accumulate rewards
            episode_reward += reward
            episode_reward_2 += reward_2

            obs = next_obs
            # print(game.grid)
            if done:
                break

        # if i_episode % 100 == 0:
        #     print("Test Episode " + str(i_episode) + ". Total reward: " + str(episode_reward))
        test_episode_reward_list.append(episode_reward)
        test_episode_reward_list_2.append(episode_reward_2)

    plt.plot(test_episode_reward_list)
    plt.title("Agent1 rewards vs episodes while testing")
    plt.xlabel("Episodes")
    plt.ylabel("Total Episode Rewards")
    plt.ylim(-250, 50)
    plt.show()

    plt.plot(test_episode_reward_list_2)
    plt.title("Agent2 rewards vs episodes while testing")
    plt.xlabel("Episodes")
    plt.ylabel("Total Episode Rewards")
    plt.ylim(-250, 50)
    plt.show()


import numpy as np
import constants as const
import movement as mv

def data_conv(tup:tuple):
    '''
    Konwertuje dane na słownik. TYLKO DO UŻYCIA W FUNKCJI data_init()
    '''

    # konwersja położenia ze współrzędnych sferycznych na kartezjańskie
    if tup[0]['gravfieldtype'] == 'centralne':
        # położenie
        pos = np.zeros(3)
        pos_r = tup[3] + tup[0]['radius']
        pos[0] = pos_r * np.sin(tup[2][1]) * np.cos(tup[2][0])
        pos[1] = pos_r * np.sin(tup[2][1]) * np.sin(tup[2][0])
        pos[2] = pos_r * np.cos(tup[2][1])
        # rzut położenia na powierzchnię
        pos_t = np.zeros(3)
        pos_rt = tup[0]['radius']
        pos_t[0] = pos_rt * np.sin(tup[2][1]) * np.cos(tup[2][0])
        pos_t[1] = pos_rt * np.sin(tup[2][1]) * np.sin(tup[2][0])
        pos_t[2] = pos_rt * np.cos(tup[2][1])
    else:
        pos = np.zeros(3)
        pos[2] = tup[3]
        pos_t = np.zeros(3)

    # konwerssja prędkości na współrzędne kartezjańskie
    vel = np.zeros(3)
    if tup[4][0] == 1:      # kartezjańskie
        vel[0] = tup[4][1]
        vel[1] = tup[4][2]
        vel[2] = tup[4][3]
    elif tup[4][0] == 2:    # cylindryczne
        vel[0] = tup[4][2] * np.cos(tup[4][1])
        vel[1] = tup[4][2] * np.sin(tup[4][1])
        vel[2] = tup[4][3]
    elif tup[4][0] == 3:    # sferyczne
        vel[0] = tup[4][3] * np.sin(tup[4][2]) * np.cos(tup[4][1])
        vel[1] = tup[4][3] * np.sin(tup[4][2]) * np.sin(tup[4][1])
        vel[2] = tup[4][3] * np.cos(tup[4][2])

    # czy v > I prędkość kosmiczna?
    # w zasadzie to sprawdza czy ciało wróci kiedykolwiek na powierzchnię
    if  tup[0]['gravfieldtype'] == 'centralne':
        vorb = (tup[0]['mass'] * const.GRAV_CONST / (tup[0]['radius'] + tup[3])) ** 0.5
        if mv.vector_value(vel) >= vorb:
            print('Obiekt rzucony nie wróci nigdy na powierzchnię ciała.')
            return {'name': 'error'}

    env = {
        'name': tup[0]['name'],
        'grav_type': tup[0]['gravfieldtype'],
        'surf_acc': tup[0]['surfacegrav'],
        'radius': tup[0]['radius'],
        'v_rot': tup[0]['rotation'],
        'mass': tup[0]['mass'],
        'flag_rot': tup[1],
        'pos': pos,
        'v_lin': vel,
        'pos_temp': pos_t
    }

    return env

def data_input():
    '''
    Przyjmuje dane od użytkownika, a następnie konwertuje je na format przyjazny obliczeniom.

    Zwraca słownik zawierający elementy:
        - name
        - grav_type
        - surf_acc
        - radius
        - v_rot
        - mass
        - flag_rot
        - pos
        - v_lin
    '''
    # '''
    # Wczytuje dane i konwertuje je na bardziej przyjazne obliczeniom.
    # Zwraca krotkę o następujących elementach:
    # [0] - environment, słownik - zawiera elementy: name, gravfieldtype, surfacegrav, radius, rotation, mass \n
    # [1] - flagCoriolis, boolean - czy uwzględnione jest występowanie siły Coriolisa \n
    # [2] - location, ndarray - postaci [a,b], zawiera położenie początkowe w postaci współrzędnych sferycznych azymutalnej i zenitalnej \n
    # [3] - height, float - odległość od powierzchni ziemi \n
    # [4] - velocity, ndarray - postaci [t,x1,x2,x3], gdzie t - typ współrzędnych (1-kartezjańskie, 2-cylindryczne, 3-sferyczne),
    # x1,x2,x3 - kolejne współrzędne prędkości
    #
    # :return: krotka zawierająca informacje o środowisku ruchu, występowaniu siły Coriolisa,
    #          położeniu geograficznym, wysokości nad powierzchnią ziemi, prędkości początkowej
    # '''

    # wybór układu ruchu
    file = open("environments.dat", 'r')
    # envs = np.ndarray()
    envs = []
    for line in file:
        (name, gravfieldtype, surfacegrav, radius, rotation) = line.split()
        mass = float(surfacegrav) * float(radius)**2 / const.GRAV_CONST
        dct = {'name':name, 'gravfieldtype':gravfieldtype, 'surfacegrav':float(surfacegrav), 'radius':float(radius), 'rotation':float(rotation), 'mass':mass}
        # envs = np.append(envs, dct)
        envs.append(dct)
    file.close()
    print('Dostępne środowiska:')
    for element in envs:
        print(element['name'], end='\t')
    environment = input('\nWybierz środowisko ruchu:')
    environment = environment.lower()
    if environment == 'księżyc' or environment == 'księzyc' or environment == 'ksieżyc':
        environment = 'ksiezyc'
    if environment == 'płaszczyzna':
        environment = 'plaszczyzna'
    #todo: inne warunki dla polskich znaków?
    for env in envs:
        if env['name'].lower() == environment:
            environment = env
            break

    # czy uzwględniamy siłę Coriolisa
    while True:
        if environment['gravfieldtype'] == 'centralne':
            flagCoriolis = input('Czy uwzględnić wpływ rotacji ciała na trajektorię? Y/N')
            try:
                if flagCoriolis.lower() == 'n':
                    flagCoriolis = False
                    break
                elif flagCoriolis.lower() == 'y':
                    flagCoriolis = True
                    break
                else:
                    continue
            except:
                continue
        else:
            flagCoriolis = False
            break

    # współrzędne geograficzne położenia początkowego
    location = np.zeros(2)
    if flagCoriolis == True:
        geolength = input('Podaj długość geograficzną położenia startowego w stopniach. Wielkości dodatnie - długość geograficzna wschodnia (E), ujemna - zachodnia (W):')
        geowidth = input('Podaj szerokość geograficzną położenia startowego w stopniach. Wielkości dodatnie - szerokość geograficzna północna (N), ujemne - południowa (S):')
        while True:
            try:
                if location[0] > 180 or location[0] < -180 or location[1] > 90 or location[1] < -90:
                    raise ValueError
                location[0] = float(geolength) / const.DEG_PER_RAD
                location[1] = float(geowidth) / const.DEG_PER_RAD
            except ValueError:
                print('Podaj poprawne współrzędne geograficzne.')
                continue
            except:
                print('Podaj wartości liczbowe.')
                continue
            else:
                break
        # konwersja długości geograficznej na współrzędne sferyczne
        if location[0] < 0:
            location[0] += 2*const.PI
        # konwersja szerokości geograficznej na współrzędne sferyczne
        location[1] *= (-1)
        location[1] += 0.5*const.PI

    # położenie początkowe nad powierzchnią ziemi
    while True:
        height = input('Podaj wysokość (w metrach) nad powierzchnią ziemi w chwili początkowej:')
        try:
            height = float(height)
            if height < 0:
                raise ValueError
        except:
            print('Wprowadź liczbę rzeczywistą nieujemną.')
            continue
        else:
            break

    # prędkość w jednym z typów układów współrzędnych
    velocity = np.zeros(4)
    while True:
        temp = input('Podaj typ współrzędnych prędkości (1-kartezjańskie, 2-cylindryczne, 3-sferyczne):')
        try:
            velocity[0] = float(temp)
            if not (velocity[0] == 1 or velocity[0] == 2 or velocity[0] == 3):
                raise ValueError
        except:
            continue
        else:
            break
    while True:
        if velocity[0] == 1:
            print('Układ współrzędnych kartezjańskich; x, y - współrzędne poziome, z - współrzędna pionowa. Podaj współrzędną x prędkości [m/s]:')
            temp1 = input()
            temp2 = input('Podaj współrzędną y prędkości [m/s]:')
            temp3 = input('Podaj współrzędną z prędkości [m/s]:')
        elif velocity[0] == 2:
            print('Układ współrzędnych cylindrycznych. Podaj kierunek w radianach:')
            temp1 = input()
            temp2 = input('Podaj współrzędną poziomą prędkości [m/s]:')
            temp3 = input('Podaj współrzędną pionową prędkości [m/s]:')
        elif velocity[0] == 3:
            print('Układ współrzędnych sferycznych. Podaj azymut w radianach:')
            temp1 = input()
            temp2 = input('Podaj nachylenie do poziomu w radianach:')
            temp3 = input('Podaj szybkość [m/s]:')
        try:
            if velocity[0] == 2 or velocity[0] == 3:
                if temp1.endswith(('pi', 'PI', 'Pi')):
                    if temp1[0:len(temp1) - 2] == '-':
                        temp1 = temp1.replace('-', '-1')
                    elif temp1[0:len(temp1) - 2] == '':
                        temp1 = '0' + temp1
                    temp1 = float(temp1[0:len(temp1)-2]) * const.PI
                else:
                    temp1 = float(temp1)
                while temp1 >= 2*const.PI:
                    temp1 -= 2 * const.PI
                while temp1 < 0:
                    temp1 += 2 * const.PI
            if velocity[0] == 3:
                if temp2.endswith(('pi', 'PI', 'Pi')):
                    if temp2[0:len(temp2) - 2] == '-':
                        temp2 = temp2.replace('-', '-1')
                    elif temp2[0:len(temp2) - 2] == '':
                        temp2 = '0' + temp2
                    temp2 = float(temp2[0:len(temp2) - 2]) * const.PI
                else:
                    temp2 = float(temp2)
                #konwersja z nachylenia do poziomu na odchylenie od osi pionowej
                temp2 += 0.5*const.PI
                if temp2 < 0:
                    temp2 *= (-1)
                if temp2 > const.PI:
                    temp2 = 2*const.PI - temp2
            velocity[1] = float(temp1)
            velocity[2] = float(temp2)
            velocity[3] = float(temp3)
        except:
            print('Podano niepoprawne współrzędne.')
            continue
        else:
            break

    final =  (environment, flagCoriolis, location, height, velocity)
    return data_conv(final)