from typing import List, Tuple

import numpy as np

import croupier
import dice
from dice_type import DiceType, AVAILABLE_DICES

def calculate_viterbi_probabilities(observations: np.ndarray, fair_dice: dice.Dice, loaded_dice: dice.Dice,
                                    initial_dice_probability: np.ndarray, transition_matrix: np.ndarray) -> np.ndarray:
    observations_rows = observations.shape[0]

    # Initial probabilities of each dices depends on random choice of croupier and probability of an observation
    viterbi_probabilities = np.zeros([observations_rows, AVAILABLE_DICES])
    viterbi_probabilities[0] = np.array([
        initial_dice_probability[DiceType.FAIR] * fair_dice.probabilities[observations[0]],
        initial_dice_probability[DiceType.LOADED] * loaded_dice.probabilities[observations[0]],
    ])

    # Iteratively compute dice probabilities based on previous observations and probabilities
    for t, observation in enumerate(observations[1:], 1):
        viterbi_probabilities[t] = np.array([
            fair_dice.probabilities[observation] * np.max([
                # Previously we've used fair dice and now we use fair dice
                viterbi_probabilities[t-1][DiceType.FAIR] * transition_matrix[DiceType.FAIR, DiceType.FAIR],
                # Previously we've used loaded dice and now we use fair dice
                viterbi_probabilities[t-1][DiceType.LOADED] * transition_matrix[DiceType.LOADED, DiceType.FAIR],
            ]),
            loaded_dice.probabilities[observation] * np.max([
                # Previously we've used fair dice and now we use loaded dice
                viterbi_probabilities[t-1][DiceType.FAIR] * transition_matrix[DiceType.FAIR, DiceType.LOADED],
                # Previously we've used loaded dice and now we use loaded dice
                viterbi_probabilities[t-1][DiceType.LOADED] * transition_matrix[DiceType.LOADED, DiceType.LOADED],
            ]),
        ])

        # Normalize current probabilities, so that values won't be too low over time
        viterbi_probabilities[t] /= np.sum(viterbi_probabilities[t])

    return viterbi_probabilities

def calculate_forward_probabilities(observations: np.ndarray, fair_dice: dice.Dice, loaded_dice: dice.Dice,
                                    initial_dice_probability: np.ndarray, transition_matrix: np.ndarray) -> np.ndarray:
    observations_rows = observations.shape[0]

    # Initial probabilities for alpha equals probability of random choice of a given dice and roll of given observation
    alpha_probabilities = np.zeros([observations_rows, AVAILABLE_DICES])
    alpha_probabilities[0] = np.array([
        initial_dice_probability[DiceType.FAIR] * fair_dice.probabilities[observations[0]],
        initial_dice_probability[DiceType.LOADED] * loaded_dice.probabilities[observations[0]],
    ])

    # Iteratively compute probabilities based on previous ones
    for t, observation in enumerate(observations[1:], 1):
        alpha_probabilities[t] = np.array([
            fair_dice.probabilities[observation] * np.sum([
                # Previously we've used fair dice and now we use fair dice
                alpha_probabilities[t-1][DiceType.FAIR] * transition_matrix[DiceType.FAIR, DiceType.FAIR],
                # Previously we've used loaded dice and now we use fair dice
                alpha_probabilities[t-1][DiceType.LOADED] * transition_matrix[DiceType.LOADED, DiceType.FAIR]
            ]),
            loaded_dice.probabilities[observation] * np.sum([
                # Previously we've used fair dice and now we use loaded dice
                alpha_probabilities[t-1][DiceType.FAIR] * transition_matrix[DiceType.FAIR, DiceType.LOADED],
                # Previously we've used loaded dice and now we use loaded dice
                alpha_probabilities[t-1][DiceType.LOADED] * transition_matrix[DiceType.LOADED, DiceType.LOADED]
            ]),
        ])

        # Normalize current probabilities, so that values won't be too low over time
        alpha_probabilities[t] /= np.sum(alpha_probabilities[t])

    return alpha_probabilities

def calculate_backward_probabilities(observations: np.ndarray, fair_dice: dice.Dice, loaded_dice: dice.Dice,
                                     initial_dice_probability: np.ndarray, transition_matrix: np.ndarray) -> np.ndarray:
    observations_rows = observations.shape[0]

    # Initial probabilities for beta equals to 1 for each dice. Keep in mind that we need to iterate from the back!
    beta_probabilities = np.zeros([observations_rows, AVAILABLE_DICES])
    beta_probabilities[observations_rows-1] = np.array([1, 1])

    for t, observation in reversed(list(enumerate(observations))[1:]):
        beta_probabilities[t-1] = np.array([
            np.sum([
                # In the observation T croupier used fair dice and fair dice in T-1
                transition_matrix[DiceType.FAIR, DiceType.FAIR] * fair_dice.probabilities[observation]
                  * beta_probabilities[t][DiceType.FAIR],
                # In the observation T croupier used loaded dice and fair dice in T-1
                transition_matrix[DiceType.FAIR, DiceType.LOADED] * loaded_dice.probabilities[observation]
                  * beta_probabilities[t][DiceType.LOADED],
            ]),
            np.sum([
                # In the observation T croupier used fair dice and loaded dice in T-1
                transition_matrix[DiceType.LOADED, DiceType.FAIR] * fair_dice.probabilities[observation]
                  * beta_probabilities[t][DiceType.FAIR],
                # In the observation T croupier used loaded dice and loaded dice in T-1
                transition_matrix[DiceType.LOADED, DiceType.LOADED] * loaded_dice.probabilities[observation]
                  * beta_probabilities[t][DiceType.LOADED],
            ]),
        ])

        # Again, as previously, normalize all probabilities, so that values won't be too low over time
        beta_probabilities[t-1] /= np.sum(beta_probabilities[t-1])

    return beta_probabilities

def baum_welch(multiple_observations: List[np.ndarray], epochs: int = 50) -> Tuple[dice.Dice, dice.Dice, np.ndarray, np.ndarray]:
    # Initialize first dice parameters
    first_dice_probabilities = np.random.rand(6)
    first_dice_probabilities /= first_dice_probabilities.sum()  # Make probabilities sum up to 1.0
    first_dice = dice.Dice(first_dice_probabilities)

    # Initialize second dice parameters
    second_dice_probabilities = np.random.rand(6)
    second_dice_probabilities /= second_dice_probabilities.sum()  # Make probabilities sum up to 1.0
    second_dice = dice.Dice(second_dice_probabilities)

    # Initialize probabilities which were used to randomize first dice
    initial_dice_probability = np.random.rand(2)
    initial_dice_probability /= initial_dice_probability.sum()  # Make probabilities sum up to 1.0

    # Initialize transition probabilities
    transition_matrix = np.random.rand(2, 2)
    transition_matrix /= np.sum(transition_matrix, axis=1)[:, None]  # Make probabilities sum up to 1.0 for each dice

    # Train our Hidden Markov Model in epochs
    for epoch in range(epochs):
        # Prepare placeholders for collecting computations from all observations
        new_transition_matrix = np.zeros((2, 2))
        new_initial_dice_probability = np.zeros(2)
        new_first_dice_probabilities = np.zeros(6)
        new_second_dice_probabilities = np.zeros(6)
        fitness = 0

        # In each epoch walk through all collected observations
        for observations in multiple_observations:
            observations_rows = observations.shape[0]

            # E Step - use forward and backward pass to calculate alphas and betas
            alpha_probabilities = calculate_forward_probabilities(observations, first_dice, second_dice,
                                                                  initial_dice_probability, transition_matrix)
            beta_probabilities = calculate_backward_probabilities(observations, first_dice, second_dice,
                                                                  initial_dice_probability, transition_matrix)
            aposteriori_probabilities = np.multiply(alpha_probabilities, beta_probabilities)

            # Collect probabilities for each observations which can be defined as P(O|parameters)
            probability_of_observation = initial_dice_probability[0] * beta_probabilities[0][0] \
                                         + initial_dice_probability[1] * beta_probabilities[0][1]
            fitness += probability_of_observation

            # KSI (Greek Letter) parameters are known as probability of being in state `i` at time `t` and in
            # state `j` at time `t+1`, given the model and the sequence of observations
            ksi = np.zeros([observations_rows, 2, 2])
            for t in range(observations_rows-1):
                denominator = (alpha_probabilities[t][0] * beta_probabilities[t][0] + alpha_probabilities[t][1] * beta_probabilities[t][1])
                ksi[t, 0, 0] = (alpha_probabilities[t][0] * transition_matrix[0, 0]
                                * first_dice_probabilities[observations[t+1]] * beta_probabilities[t+1][0]) / denominator
                ksi[t, 0, 1] = (alpha_probabilities[t][0] * transition_matrix[0, 1]
                                * second_dice_probabilities[observations[t+1]] * beta_probabilities[t+1][1]) / denominator
                ksi[t, 1, 0] = (alpha_probabilities[t][1] * transition_matrix[1, 0]
                                * first_dice_probabilities[observations[t+1]] * beta_probabilities[t+1][0]) / denominator
                ksi[t, 1, 1] = (alpha_probabilities[t][1] * transition_matrix[1, 1]
                                * second_dice_probabilities[observations[t+1]] * beta_probabilities[t+1][1]) / denominator

            # Gamma (another Greek Letter) parameters stands for probability of being in state `i` at time `t`
            gamma = np.zeros([observations_rows, 2])
            for t in range(observations_rows-1):
                gamma[t, 0] = np.sum(ksi[t, 0, :])
                gamma[t, 1] = np.sum(ksi[t, 1, :])

            # Compute initial probabilities for this observation and add it for later use
            new_initial_dice_probability += gamma[0] / np.sum(gamma[0])

            # Compute transition matrix and add it up to for later use
            new_transition_matrix[0, 0] += np.sum(ksi[:, 0, 0]) / np.sum(gamma[:, 0]) / probability_of_observation
            new_transition_matrix[0, 1] += np.sum(ksi[:, 0, 1]) / np.sum(gamma[:, 0]) / probability_of_observation
            new_transition_matrix[1, 0] += np.sum(ksi[:, 1, 0]) / np.sum(gamma[:, 1]) / probability_of_observation
            new_transition_matrix[1, 1] += np.sum(ksi[:, 1, 1]) / np.sum(gamma[:, 1]) / probability_of_observation

            # Compute probabilities for each dice (hidden states) and collect them for later use
            for wall in range(6):
                # Emission probabilities can be computed as expected number of times in state `j` and observing `k-th` wall on dice
                # divided by expected number of times in state `j`
                new_first_dice_probabilities[wall] = np.sum(np.take(aposteriori_probabilities[:, 0], np.where(observations == wall)))
                new_first_dice_probabilities[wall] /= np.sum(aposteriori_probabilities[:, 0]) / probability_of_observation
                new_second_dice_probabilities[wall] = np.sum(np.take(aposteriori_probabilities[:, 1], np.where(observations == wall)))
                new_second_dice_probabilities[wall] /= np.sum(aposteriori_probabilities[:, 1]) / probability_of_observation

        # Normalize all values for probabilities, so that they won't blow up quickly...
        initial_dice_probability = new_initial_dice_probability / np.sum(new_initial_dice_probability)
        transition_matrix = new_transition_matrix / np.sum(new_transition_matrix, axis=1)[:, None]
        first_dice_probabilities = new_first_dice_probabilities / np.sum(new_first_dice_probabilities)
        second_dice_probabilities = new_second_dice_probabilities / np.sum(new_second_dice_probabilities)

        # Put dice probabilities into Dice instances
        first_dice = dice.Dice(first_dice_probabilities)
        second_dice = dice.Dice(second_dice_probabilities)

        # Some logging is always good :)
        print('= EPOCH #{} ='.format(epoch))
        print('Transition Matrix:', transition_matrix)
        print('Initial Dice Probability:', initial_dice_probability)
        print('First Dice:', first_dice_probabilities)
        print('Second Dice:', second_dice_probabilities)
        print('Fitness:', fitness)
        print()

    return first_dice, second_dice, initial_dice_probability, transition_matrix


import shutil

import pydicom
import os
import numpy as np
import json

def apply_boolean_mask(array, array_criterion, criterion, value):
    array.copy()
    if array.shape != array_criterion.shape:
        print("Размеры не совпадают")
        return
    if len(array.shape) == 2:
        for i in range(len(array)):
            for j in range(len(array[i])):
                if criterion(array_criterion[i][j]):
                    array[i][j] = value
    elif len(array.shape) == 3:
        for i in range(len(array)):
            for j in range(len(array[i])):
                for k in range(len(array[i][j])):
                    if criterion(array_criterion[i][j][k]):
                        array[i][j][k] = value
    return array

def tag2int(x):
    # get x[0] as in int is x is a 'pydicom.multival.MultiValue', otherwise get int(x)
    if isinstance(x, pydicom.multival.MultiValue):
        x = x[0]
    x = str(x)
    x = x.split(',')[0]
    return int(float(x))

def data2tags(data):
    windowing = {}
    try:
        windowing['window_center'] = tag2int(data[('0028', '1050')].value)  # window center
        windowing['window_width'] = tag2int(data[('0028', '1051')].value)  # window width
    except KeyError:
        windowing['window_center'] = 2048
        windowing['window_width'] = 4096
    try:
        windowing['intercept'] = tag2int(data[('0028', '1052')].value)  # window center
        windowing['slope'] = tag2int(data[('0028', '1053')].value)  # window width
    except KeyError:
        windowing['intercept'] = 0
        windowing['slope'] = 1
    try:
        windowing['image_mode'] = str(data[('0028', '0004')].value)  # monochrome1 or not
    except KeyError:
        pass
    return windowing

def reformat_survey(src_path):
    data, scan = read_survey(src_path)
    tags = data2tags(data)
    scan = np.array(scan, dtype=np.uint16).tobytes()
    return scan, tags

def read_survey(path):
    survey = pydicom.dcmread(path)
    scan = survey.pixel_array
    return survey, scan

def create_empty_json_files(dicom_paths):
    for dicom_path in dicom_paths:
        json_path = dicom_path.replace('.dcm', '.json')
        with open(json_path, 'w') as f:
            json.dump({"contours": [],
                       "points": [],
                       "rulers": [],
                       "polygons": [[]]}, f)

def get_paths(dst_folder, web_path):
    dicom_file_names = os.listdir(dst_folder)
    dicom_file_names = list(filter(lambda x: x.split('.')[-1] == 'dcm', dicom_file_names))
    dicom_file_paths = ["/".join([web_path, dicom_file_name]) for dicom_file_name in dicom_file_names]
    dicom_file_paths.sort()
    json_file_paths = [x.replace(".dcm", ".json") for x in dicom_file_paths]
    return dicom_file_paths, json_file_paths

def rename_dicoms(paths_to_dicoms):
    new_paths = []
    for path in paths_to_dicoms:
        single_slice = pydicom.dcmread(path)
        number = single_slice.InstanceNumber - 1
        new_path = path.split('/')[:-1]
        new_path.append(str(number).rjust(4, '0') + '.dcm')
        new_path = '/'.join(new_path)
        os.rename(path, new_path)
        new_paths.append(new_path)
    return new_paths


import pandas as pd
import json
import numpy as np
from time import time
from tkinter import Tk, messagebox
from tkinter.filedialog import askopenfilename
from string import ascii_uppercase as upstr
import file_split as fs
from import_csv import FileFinder
import data_formatter as formatter
import enspire_od_join as enspire_od_join
import pt_calculations as pt_calculations
from import_od import import_od

# TODO just create separate inputs module

def concat_projs(df):
    all_projs_df = pd.DataFrame()
    pass

def run_main(proj_dict):
    proj_concat = False
    file_finder = FileFinder()

    proj_dict = fs.split_projects(proj_dict)

    for proj, inner_dict in proj_dict.items():
        project_title = proj
        if "-" in proj:
            inner_dict["proj_name"] = proj.split("-")[0]
            inner_dict["ab_name"] = proj.split("-")[-1]
        else:
            inner_dict["ab_name"] = ""
            inner_dict["proj_name"] = proj

        proj_data = proj_dict[proj]

        source_df = file_finder.data_finder(proj_data)

        df_list = formatter.data_format(source_df, proj_data)

        if proj_data["od_file"]:
            raw_od = import_od(proj_data)
            joined_df_list = enspire_od_join.join_dfs(df_list, raw_od, proj_data)
            main_join_dfs = joined_df_list[:2]
            final_display_df = joined_df_list[2]
            final_display_rep_df = joined_df_list[3]
            completed_main_dfs = pt_calculations.make_calculations(main_join_dfs, proj_data)
        else:
            main_dfs = df_list[:2]
            final_display_df = df_list[2]
            final_display_rep_df = df_list[3]
            completed_main_dfs = pt_calculations.make_calculations(main_dfs, proj_data)

        final_main_df = completed_main_dfs[0]
        final_main_rep_df = completed_main_dfs[1]

        with pd.ExcelWriter(f"../test_files/test_outputs/{project_title}_output_test.xlsx") as writer:
            final_main_df.to_excel(writer, sheet_name="Calculations")
            final_display_df.to_excel(writer, sheet_name="Display_Ready")
            final_main_rep_df.to_excel(writer, sheet_name="Rep_Calculations")
            final_display_rep_df.to_excel(writer, sheet_name="Rep_Display_Ready")
        print(f"Project {project_title} has been output.")

        # Also return these four dataframes into list?
        # clean_df, main_df, clean_rep_df, main_rep_df = test_formatter.data_format(source_df, proj_data)
        # df_list = [clean_df, main_df, clean_rep_df, main_rep_df]
        # dfs_return = join_dfs(df_list, raw_od, proj_data)
        #
        # clean_join_df = dfs_return[0]
        # main_join_df = dfs_return[1]
        # clean_rep_join_df = dfs_return[2]
        # main_rep_join_df = dfs_return[3]
        #
        # complete_df = eight_pt_calculations.make_calculations(main_join_df, proj_data)
        # complete_rep_df = eight_pt_calculations.make_calculations(main_rep_join_df, proj_data)
        #

if __name__ == "__main__":
    start_time = time()
    window = Tk()
    window.withdraw()
    try:
        with open(r"C:\Users\esimonds\parser_data.json", "r") as parser_file:
            proj_data_dict = json.load(parser_file)
    except FileNotFoundError:
        messagebox.showinfo(title="Warning!", message="There's no file, did you complete the parser form?")
    # proj_names = [
    #     "SSF00626",
    # ]
    # proj_data_dict = {
    #     proj: {"plates": "",
    #            "raw_file": "",
    #            "od_file": r"L:\Molecular Sciences\Small Scale Runs\SSF00626 DSS Spaniel_discrete chaperone set\SSF00626 Spaniel (96DW) ELN v2.xlsm",
    #            "std_row": "",
    #            "std_pos": "",
    #            "std_conc": "",
    #            "volumes": vol_sp,
    #            "points": 4
    #            }
    #     for proj in proj_names
    # }
    # for proj, inner in proj_data_dict.items():
    #     add_std = input("Use standard? y/n    ")
    #     if add_std == 'y':
    #         more_ids = True
    #         std_conc = input("Enter standard concentration, e.g. '100, 50, 25, 12, 6, 3': ").split(",")
    #         std_conc_len = len(std_conc)
    #
    #         std_ids = []
    #         count = 1
    #         while more_ids == True:
    #             z = input("Enter 'column', 'row', 'none: ").lower()
    #             y = input("Enter staring well id, e.g. 'A11' or 'G1'").capitalize()
    #
    #             if z == "column":
    #                 col_letter = y[:1]
    #                 letter_idx = upstr.index(col_letter)
    #                 col_num = y[1:]
    #                 std_ids += [f"{upstr[letter]}{str(col_num).zfill(2)}" for letter in range(letter_idx, std_conc_len)]
    #
    #             elif z == 'row':
    #                 row_letter = y[:1]
    #                 row_num = int(y[1:])
    #                 std_ids += [f"{row_letter}{str(num).zfill(2)}" for num in range(row_num, row_num + std_conc_len)]
    #
    #             elif z == 'none':
    #                 break
    #
    #             else:
    #                 print("Sorry, you may not have typed 'column' or 'row'.")
    #
    #             add_more = input("Hit 'enter' to add replicates, or 'n' to continue: ").lower()
    #             if add_more == "n":
    #                 std_conc *= count
    #                 more_ids = False
    #             else:
    #                 count += 1
    #
    #         std_dict = dict(zip(std_ids, std_conc))
    #         inner["std_conc"] = std_dict
    #
    #
    #     if proj == "SSF00626":
    #         inner["plates"] = plate_ids_1
            # inner["volumes"] = vol_ak
            # inner["od_file"] = r"L:\Molecular Sciences\Small Scale Runs\SSF00627 DSS Spaniel003_discrete host screening\Copy of SSF00627 DSS Spaniel ELN_v2 copy.xlsm"
    run_main(proj_data_dict)
    window.destroy()
    end_time = time()
    split = round(end_time - start_time, 2)
    print(f"Program runtime: {split}s")


import os
import pathlib
import tempfile
import subprocess

import pytest

from o2g.osm.exporter import TransitDataExporter
from o2g.gtfs.gtfs_writer import GTFSWriter
from o2g.gtfs import gtfs_dummy

@pytest.fixture
def transit_data(request):
    root_dir = pathlib.Path(__file__).parents[2]
    filepath = root_dir.joinpath('resources', 'osm', 'freiburg.osm.bz2')
    tde = TransitDataExporter(str(filepath))
    tde.process()
    return tde

@pytest.fixture
def dummy_transit_data(transit_data):
    return \
        gtfs_dummy.create_dummy_data(transit_data.routes,
                                     transit_data.stops)

@pytest.fixture
def gtfs_writer(transit_data):
    w = GTFSWriter()

    w.add_agencies(transit_data.agencies)
    w.add_stops(transit_data.stops)
    w.add_routes(transit_data.routes)

    return w

@pytest.fixture
def dummy_gtfs_writer(transit_data, dummy_transit_data):
    w = GTFSWriter()

    # Patch agencies to pass transitfeed check
    patched_agencies = gtfs_dummy.patch_agencies(transit_data.agencies)

    w.add_agencies(patched_agencies)
    w.add_stops(transit_data.stops)
    w.add_routes(transit_data.routes)
    w.add_shapes(transit_data.shapes)

    w.add_trips(dummy_transit_data.trips)
    w.add_stop_times(dummy_transit_data.stop_times)
    w.add_calendar(dummy_transit_data.calendar)

    return w

@pytest.fixture
def dummy_zipfeed(dummy_gtfs_writer):
    filename = '{}.zip'.format(tempfile.mktemp())
    print('Writing GTFS feed to %s' % filename)
    dummy_gtfs_writer.write_zipped(filename)

    return filename

def test_write_zipped(gtfs_writer):
    filename = tempfile.mktemp()
    print('Writing GTFS feed to %s' % filename)
    gtfs_writer.write_zipped(filename)

    assert os.path.exists(filename)

def test_duplicate_trips(dummy_transit_data):
    dups = []
    for trip in dummy_transit_data.trips:
        assert trip['trip_id'] not in dups
        dups.append(trip['trip_id'])

def test_validation(dummy_zipfeed):
    """Run transitfeed over the generated feed."""
    # transitfeed is a python2 application. So we need to run it outside
    # python3 process. Moreover, it is not available in pip repository.
    # Therefore we have to clone it from git and eventually run it in a
    # process of its own. Finally we parse the standard output and look
    # for errors. We ignore the warnings for now.
    if not os.path.exists('transitfeed'):
        subprocess.check_call(
            ['git', 'clone', '-b', '1.2.16', '--single-branch',
             'https://github.com/google/transitfeed'])

    assert os.path.exists('transitfeed/feedvalidator.py')

    p = subprocess.Popen(['python2.7', 'transitfeed/feedvalidator.py', '-n',
                         dummy_zipfeed],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE)
    out, err = p.communicate()

    print("Google Transitfeed's output:\n{}".format(out.decode('utf8')))

    assert 'error' not in out.decode('utf8')
    assert 'errors' not in out.decode('utf8')

def test_shape_id_in_trips(transit_data, dummy_transit_data):
    shape_ids = [shape.shape_id for shape in transit_data.shapes]
    for trip in dummy_transit_data.trips:
        assert trip['shape_id'] in shape_ids


futhark="ᚫᛒᚲᛞᛖᚠᚷᚺᛁᛃᚲᛚᛗᚾᛟᛈᛜᚱᛊᛏᚢᚹᚹᛝᛇᛉ"

def_repl={
    "e":"éëêè",
    "a":"âäà",
    "o":"ôö",
    "u":"ùüû",
    "i":"îï",
    "c":"ç"
    }

repl={}

for c in def_repl:
    for d in def_repl[c]:
        repl[d]=c

def remove_accents(c):
    try:
        return repl[c]
    except KeyError:
        return c

def tr_char(c):
    try:
        if c==" ":
            return "᛫"
        o=ord(remove_accents(c.lower()))-ord("a")
        if o<0:
            raise Exception
        return futhark[o]
    except:
        return c

def tr_str(s):
    return ''.join(map(tr_char,s.replace("th","ᚦ")))

def utr_char(c):
    if c=="ᚦ":
        return "th";
    if c=="᛫":
        return " ";
    try:
        return chr(ord('a')+futhark.index(c))
    except:
        return c

def utr_str(s):
    return ''.join(map(utr_char,s))


import numpy as np
from PIL import Image
import pickle
import operator
import matplotlib.pylab as pylab
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE

imgPath = 'E:/dataset/'
trainNum = 7
testNum = 2
crossValidateNum = 1
dataNum = 40
N = 150

train_data = np.empty((dataNum*trainNum, 2679))
train_label = np.empty(dataNum*trainNum)

test_cross_validation_data = np.empty((dataNum*crossValidateNum, 2679))
test_cross_validation_label = np.empty((dataNum*crossValidateNum))

test_data = np.empty((dataNum*testNum, 2679))
test_label = np.empty(dataNum*testNum)

def covertImg():
    imgName = 'olivettifaces.gif'
    # 读取原始图片并转化为np.ndarray，将灰度值由0～256转换到0～1
    img = Image.open(imgPath+imgName)
    img_ndarray = np.asarray(img, dtype='float64')/256

    # 图片大小时1190*942，一共20*20个人脸图，故每张人脸图大小为（1190/20）*（942/20）即57*47=2679
    # 将全部400个样本存储为一个400*2679的数组，每一行即代表一个人脸图，并且第0～9、10～19、20～29...行分别属于同个人脸
    # 另外，用olivettifaces_label表示每一个样本的类别，它是400维的向量，有0～39共40类，代表40个不同的人。
    olivettifaces = np.empty((400, 2679))
    for row in range(20):
        for column in range(20):
            olivettifaces[row * 20 + column] = np.ndarray.flatten(
                img_ndarray[row * 57:(row + 1) * 57, column * 47:(column + 1) * 47])

    # 建一个olivettifaces_label
    olivettifaces_label = np.empty(400)
    for label in range(40):
        olivettifaces_label[label * 10:label * 10 + 10] = label
    olivettifaces_label = olivettifaces_label.astype(np.int)

    # 保存olivettifaces以及olivettifaces_label到olivettifaces.pkl文件
    write_file = open(imgPath+'olivettifaces.pkl', 'wb')
    pickle.dump(olivettifaces, write_file, -1)
    pickle.dump(olivettifaces_label, write_file, -1)
    write_file.close()

def prepareDataSet():
    read_file=open(imgPath+'olivettifaces.pkl','rb')
    faces = pickle.load(read_file)
    label = pickle.load(read_file)
    read_file.close()

    for i in range(40):
        train_data[i * trainNum:i * trainNum + trainNum] = faces[i * 10:i * 10 + trainNum]
        train_label[i * trainNum:i * trainNum + trainNum] = label[i * 10:i * 10 + trainNum]

        test_cross_validation_data[i*crossValidateNum:i*crossValidateNum+crossValidateNum] = faces[i*10+trainNum+testNum : i*10+trainNum+testNum+crossValidateNum]
        test_cross_validation_data[i*crossValidateNum:i*crossValidateNum+crossValidateNum] = label[i*10+trainNum+testNum : i*10+trainNum+testNum+crossValidateNum]

        test_data[i * testNum: i * testNum + testNum] = faces[i * 10 + trainNum : i*10+trainNum+testNum]
        test_label[i * testNum: i * testNum + testNum] = label[i * 10 + trainNum : i*10+trainNum+testNum]

def compute_eigenValues_eigenVectors(arr,label):
    # 计算协方差矩阵的特征值和特征向量，按从大到小顺序排列

    avgArr = np.mean(arr,1)
    avgArr = avgArr.reshape(avgArr.shape[0],1)
    normArr = arr - avgArr
    #计算arr'T * arr

    temp=np.dot(normArr.T, normArr)
    eigenValues,eigenVectors=np.linalg.eig(temp)
    #将数值从大到小排序
    idx=np.argsort(-eigenValues)
    eigenValues=eigenValues[idx]
    #特征向量按列排
    eigenVectors=eigenVectors[:,idx]
    #label排序
    eigenVectorsLabel = label.copy()
    eigenVectorsLabel = eigenVectorsLabel[idx]
    return eigenValues,np.dot(normArr,eigenVectors),eigenVectors, eigenVectorsLabel

def knnOnce(eigenMat,eigenTrainMat,eigenTrainLabel,k):

    dis = np.sum(np.power(eigenMat.reshape(eigenMat.shape[0],1) - eigenTrainMat,2), axis=0)
    sortedDistIndicies = dis.argsort()
    print(dis[sortedDistIndicies[0]])

    classCount = {}

    for i in range(k):
        voteIlabel = eigenTrainLabel[sortedDistIndicies[i]]

        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

def knnModel(trainData, trainLabel, testData, testLabel):
    prepareDataSet()
    num = testData.shape[0]

    eigenTrainValues, eigenTrainMat, eigenTrainVec, eigenTrainLabel = compute_eigenValues_eigenVectors(trainData.T, trainLabel)
    eigenTrainMat = eigenTrainMat[0:N,:]

    eigenValues, eigenMats, eigenVectors, eigenLabel = compute_eigenValues_eigenVectors(testData.T, testLabel)
    errorCount = 0

    for i in range(num):
        result = knnOnce(eigenMats[0:N, i],eigenTrainMat,eigenTrainLabel,5)
        if result != eigenLabel[i]:
            errorCount+=1

    print(str(float(errorCount/num)))

knnModel(train_data, train_label, test_data, test_label)


# -*- coding: utf-8 -*-

import pytest
import csv
import sys
import os
from filecmp import cmp

from preprocessor.data_trimmer.data_trimmer import DataTrimmer

__author__ = "Harvey Bastidas"
__copyright__ = "Harvey Bastidas"
__license__ = "mit"

class Conf:
    def __init__(self):
        """ Component Tests Constructor """
        fname = os.path.join(os.path.dirname(__file__), "data/test_input.csv")
        self.input_file = fname
        """ Test dataset filename """
        fname = os.path.join(os.path.dirname(__file__), "data/test_output.csv")
        self.output_file = fname
        """ Output dataset filename """
        # fname = os.path.join(os.path.dirname(__file__), "../data/in_config.csv")
        # self.input_config_file = fname
        #""" Input configuration of the proprocessor """
        fname = os.path.join(os.path.dirname(__file__), "data/out_config.csv")
        self.output_config_file = fname
        """ Output configuration of the proprocessor """

class TestDataTrimmer:
    """ Component Tests  """

    def setup_method(self, test_method):
        """ Component Tests Constructor """
        self.conf = Conf()
        self.dt = DataTrimmer(self.conf)
        """ Data trimmer object """
        self.rows_d, self.cols_d = self.get_size_csv(self.conf.input_file)
        """ Get the number of rows and columns of the test dataset """
        try:
            os.remove(self.conf.output_file)
        except:
            print("No test output file found.")
            pass

    def get_size_csv(self, csv_file):
        """ Get the number of rows and columns of a test dataset, used in all tests.

        Args:
        csv_file (string): Path and filename of a test dataset

        Returns:
        (int,int): number of rows, number of columns
        """
        rows = list(csv.reader(open(csv_file)))
        return (len(rows), len(rows[0]))

    def test_C01T01_trim_fixed_rows(self):
        """ Trims a configurable number of rows from the start or end of the input dataset by using the trim_fixed_rows method. Execute trimmer with from_start=10, from_end=10. """
        rows_t, cols_t = self.dt.trim_fixed_rows(10, 10)
        # save output to file
        self.dt.store()
        # get the number of rows and cols from out_file
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the new == old - trimmed
        assert (rows_o + cols_o) == (self.rows_d + self.cols_d) - (rows_t + cols_t)

    def test_C01T02_trim_columns(self):
        """ Trims all the constant columns by using the trim_columns method. Execute trimmer with remove_colums = true. """
        rows_t, cols_t = self.dt.trim_columns()
        # save output to file
        self.dt.store()
        # get the number of rows and cols from out_file
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the new == old - trimmed
        assert (rows_o + cols_o) == (self.rows_d + self.cols_d) - (rows_t + cols_t)

    def test_C01T03_trim_auto(self):
        """ Trims all the constant columns and trims all rows with consecutive zeroes from start and end by using the trim_auto method. Execute trimmer with auto_trim = true.  """
        rows_t, cols_t = self.dt.trim_auto()
        # save output to file
        self.dt.store()
        # get the number of rows and cols from out_file
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the new == old - trimmed
        assert (rows_o + cols_o) == (self.rows_d + self.cols_d) - (rows_t + cols_t)

    def test_C01T04_cmdline_remove_columns(self):
        """ Trims all the constant columns using command line arguments """
        os.system(
            "data_trimmer --remove_columns --no_auto_trim --input_file "
            + self.conf.input_file
            + " --output_file "
            + self.conf.output_file
        )
        # get the size of the original dataset
        rows_d, cols_d = self.get_size_csv(self.conf.input_file)
        # get the size of the output dataset
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the number of rows an colums is less than the input dataset and > 0
        assert ((cols_d - cols_o) > 0) and ((cols_o > 0) and (rows_o > 0))

    def test_C01T05_cmdline_remove_columns_rows(self):
        """ Trims all the constant columns and 10  rows from start and end using command line arguments """
        os.system(
            "data_trimmer --from_start 10 --from_end 10 --remove_columns --no_auto_trim --input_file "
            + self.conf.input_file
            + " --output_file "
            + self.conf.output_file
        )
        # get the size of the original dataset
        rows_d, cols_d = self.get_size_csv(self.conf.input_file)
        # get the size of the output dataset
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the number of rows an colums is less than the input dataset and > 0
        assert ((cols_d - cols_o) > 0) and ((rows_d - rows_o) > 0) and ((cols_o > 0) and (rows_o > 0))

    def test_C01T06_cmdline_remove_columns_rows_auto(self):
        """ Trims all the constant columns and 0 rows from start and end (for pipeline) and auto trimming  using command line arguments """
        os.system(
            "data_trimmer --from_start 0 --from_end 0 --remove_columns --input_file "
            + self.conf.input_file
            + " --output_file "
            + self.conf.output_file
        )
        # get the size of the original dataset
        rows_d, cols_d = self.get_size_csv(self.conf.input_file)
        # get the size of the output dataset
        rows_o, cols_o = self.get_size_csv(self.conf.output_file)
        # assert if the number of rows an colums is less than the input dataset and > 0
        assert ((cols_d - cols_o) > 0) and ((rows_d - rows_o) > 0) and ((cols_o > 0) and (rows_o > 0))

    def test_C01T07_config_save(self):
        """ Save a configuration file and uses it to trim a dataset. Assert that output_config can be loaded and the output_config(loaded) == output_config(saved)"""
        os.system(
            "data_trimmer --input_file --from_start 20"
            + self.conf.input_file
            + " --output_file "
            + self.conf.output_file
            + " --output_config_file "
            + self.conf.output_config_file
        )
        # Uses the output as input for another dataset and compare with desired output.
        os.system(
            "data_trimmer --input_file "
            + self.conf.input_file
            + " --input_config_file "
            + self.conf.output_config_file
            + " --output_file "
            + self.conf.output_file
            + " --output_config_file "
            + self.conf.output_config_file  + ".c02t07"
        )
        assert cmp(self.conf.output_config_file, self.conf.output_config_file  + ".c02t07", shallow=True)

    def test_C01T08_config_load(self):
        """ Load a configuration file and uses it to trim a dataset. Verify that output_config == input_config"""
        fname = os.path.join(os.path.dirname(__file__), "data/in_config.csv")
        input_config_file = fname
        """ Input configuration of the proprocessor """
        os.system(
            "data_trimmer --input_file "
            + self.conf.input_file
            + " --input_config_file "
            +  input_config_file
            + " --output_file "
            + self.conf.output_file
            + " --output_config_file "
            + self.conf.output_config_file
        )
        assert cmp(input_config_file, self.conf.output_config_file, shallow=True)


from unittest import mock

import tensorflow as tf
from tfx.orchestration import mlmd_connection_manager as mlmd_cm

import ml_metadata as mlmd

class MlmdConnectionManagerTest(tf.test.TestCase):

  def setUp(self):
    super().setUp()
    self.mock_store_factory = mock.patch.object(
        mlmd, 'MetadataStore', autospec=True).start()
    self.addCleanup(mock.patch.stopall)

  def test_primary_handle(self):
    with mlmd_cm.MLMDConnectionManager.fake() as cm:
      connection_config = cm.primary_mlmd_handle.connection_config
      self.assertEqual(connection_config.WhichOneof('config'), 'fake_database')
      self.mock_store_factory.assert_called_with(connection_config)

    self.mock_store_factory.reset_mock()

    with mlmd_cm.MLMDConnectionManager.sqlite('foo') as cm:
      connection_config = cm.primary_mlmd_handle.connection_config
      self.assertEqual(connection_config.WhichOneof('config'), 'sqlite')
      self.mock_store_factory.assert_called_with(connection_config)

  def test_unusable_without_enter(self):
    cm = mlmd_cm.MLMDConnectionManager.fake()
    with self.assertRaisesRegex(RuntimeError, 'not entered yet'):
      cm.primary_mlmd_handle  # pylint: disable=pointless-statement

  def test_enter_synced_with_handle(self):
    cm = mlmd_cm.MLMDConnectionManager.fake()
    with cm:
      handle = cm.primary_mlmd_handle
      self.assertIsNotNone(handle.store)
    with self.assertRaisesRegex(
        RuntimeError, 'Metadata object is not in enter state'):
      handle.store  # pylint: disable=pointless-statement

  def test_multiple_enterable(self):
    cm = mlmd_cm.MLMDConnectionManager.fake()
    with cm:
      with cm:
        m1 = cm.primary_mlmd_handle
      m2 = cm.primary_mlmd_handle
      self.assertIs(m1, m2)
    with self.assertRaises(RuntimeError):
      cm.primary_mlmd_handle  # pylint: disable=pointless-statement

if __name__ == '__main__':
  tf.test.main()


import unittest
import itertools

from typing import List
from dbt.exceptions import VersionsNotCompatibleError
from dbt.semver import (
    VersionSpecifier,
    UnboundedVersionSpecifier,
    VersionRange,
    reduce_versions,
    versions_compatible,
    resolve_to_specific_version,
    filter_installable,
)

def semver_regex_versioning(versions: List[str]) -> bool:
    for version_string in versions:
        try:
            VersionSpecifier.from_version_string(version_string)
        except Exception:
            return False
    return True

def create_range(start_version_string, end_version_string):
    start = UnboundedVersionSpecifier()
    end = UnboundedVersionSpecifier()

    if start_version_string is not None:
        start = VersionSpecifier.from_version_string(start_version_string)

    if end_version_string is not None:
        end = VersionSpecifier.from_version_string(end_version_string)

    return VersionRange(start=start, end=end)

class TestSemver(unittest.TestCase):
    def assertVersionSetResult(self, inputs, output_range):
        expected = create_range(*output_range)

        for permutation in itertools.permutations(inputs):
            self.assertEqual(reduce_versions(*permutation), expected)

    def assertInvalidVersionSet(self, inputs):
        for permutation in itertools.permutations(inputs):
            with self.assertRaises(VersionsNotCompatibleError):
                reduce_versions(*permutation)

    def test__versions_compatible(self):
        self.assertTrue(versions_compatible("0.0.1", "0.0.1"))
        self.assertFalse(versions_compatible("0.0.1", "0.0.2"))
        self.assertTrue(versions_compatible(">0.0.1", "0.0.2"))
        self.assertFalse(versions_compatible("0.4.5a1", "0.4.5a2"))

    def test__semver_regex_versions(self):
        self.assertTrue(
            semver_regex_versioning(
                [
                    "0.0.4",
                    "1.2.3",
                    "10.20.30",
                    "1.1.2-prerelease+meta",
                    "1.1.2+meta",
                    "1.1.2+meta-valid",
                    "1.0.0-alpha",
                    "1.0.0-beta",
                    "1.0.0-alpha.beta",
                    "1.0.0-alpha.beta.1",
                    "1.0.0-alpha.1",
                    "1.0.0-alpha0.valid",
                    "1.0.0-alpha.0valid",
                    "1.0.0-alpha-a.b-c-somethinglong+build.1-aef.1-its-okay",
                    "1.0.0-rc.1+build.1",
                    "2.0.0-rc.1+build.123",
                    "1.2.3-beta",
                    "10.2.3-DEV-SNAPSHOT",
                    "1.2.3-SNAPSHOT-123",
                    "1.0.0",
                    "2.0.0",
                    "1.1.7",
                    "2.0.0+build.1848",
                    "2.0.1-alpha.1227",
                    "1.0.0-alpha+beta",
                    "1.2.3----RC-SNAPSHOT.12.9.1--.12+788",
                    "1.2.3----R-S.12.9.1--.12+meta",
                    "1.2.3----RC-SNAPSHOT.12.9.1--.12",
                    "1.0.0+0.build.1-rc.10000aaa-kk-0.1",
                    "99999999999999999999999.999999999999999999.99999999999999999",
                    "1.0.0-0A.is.legal",
                ]
            )
        )

        self.assertFalse(
            semver_regex_versioning(
                [
                    "1",
                    "1.2",
                    "1.2.3-0123",
                    "1.2.3-0123.0123",
                    "1.1.2+.123",
                    "+invalid",
                    "-invalid",
                    "-invalid+invalid",
                    "-invalid.01",
                    "alpha",
                    "alpha.beta",
                    "alpha.beta.1",
                    "alpha.1",
                    "alpha+beta",
                    "alpha_beta",
                    "alpha.",
                    "alpha..",
                    "beta",
                    "1.0.0-alpha_beta",
                    "-alpha.",
                    "1.0.0-alpha..",
                    "1.0.0-alpha..1",
                    "1.0.0-alpha...1",
                    "1.0.0-alpha....1",
                    "1.0.0-alpha.....1",
                    "1.0.0-alpha......1",
                    "1.0.0-alpha.......1",
                    "01.1.1",
                    "1.01.1",
                    "1.1.01",
                    "1.2",
                    "1.2.3.DEV",
                    "1.2-SNAPSHOT",
                    "1.2.31.2.3----RC-SNAPSHOT.12.09.1--..12+788",
                    "1.2-RC-SNAPSHOT",
                    "-1.0.3-gamma+b7718",
                    "+justmeta",
                    "9.8.7+meta+meta",
                    "9.8.7-whatever+meta+meta",
                    "99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12",
                ]
            )
        )

    def test__reduce_versions(self):
        self.assertVersionSetResult(["0.0.1", "0.0.1"], ["=0.0.1", "=0.0.1"])

        self.assertVersionSetResult(["0.0.1"], ["=0.0.1", "=0.0.1"])

        self.assertVersionSetResult([">0.0.1"], [">0.0.1", None])

        self.assertVersionSetResult(["<0.0.1"], [None, "<0.0.1"])

        self.assertVersionSetResult([">0.0.1", "0.0.2"], ["=0.0.2", "=0.0.2"])

        self.assertVersionSetResult(["0.0.2", ">=0.0.2"], ["=0.0.2", "=0.0.2"])

        self.assertVersionSetResult([">0.0.1", ">0.0.2", ">0.0.3"], [">0.0.3", None])

        self.assertVersionSetResult([">0.0.1", "<0.0.3"], [">0.0.1", "<0.0.3"])

        self.assertVersionSetResult([">0.0.1", "0.0.2", "<0.0.3"], ["=0.0.2", "=0.0.2"])

        self.assertVersionSetResult([">0.0.1", ">=0.0.1", "<0.0.3"], [">0.0.1", "<0.0.3"])

        self.assertVersionSetResult([">0.0.1", "<0.0.3", "<=0.0.3"], [">0.0.1", "<0.0.3"])

        self.assertVersionSetResult([">0.0.1", ">0.0.2", "<0.0.3", "<0.0.4"], [">0.0.2", "<0.0.3"])

        self.assertVersionSetResult(["<=0.0.3", ">=0.0.3"], [">=0.0.3", "<=0.0.3"])

        self.assertInvalidVersionSet([">0.0.2", "0.0.1"])
        self.assertInvalidVersionSet([">0.0.2", "0.0.2"])
        self.assertInvalidVersionSet(["<0.0.2", "0.0.2"])
        self.assertInvalidVersionSet(["<0.0.2", ">0.0.3"])
        self.assertInvalidVersionSet(["<=0.0.3", ">0.0.3"])
        self.assertInvalidVersionSet(["<0.0.3", ">=0.0.3"])
        self.assertInvalidVersionSet(["<0.0.3", ">0.0.3"])

    def test__resolve_to_specific_version(self):
        self.assertEqual(
            resolve_to_specific_version(create_range(">0.0.1", None), ["0.0.1", "0.0.2"]), "0.0.2"
        )

        self.assertEqual(
            resolve_to_specific_version(create_range(">=0.0.2", None), ["0.0.1", "0.0.2"]), "0.0.2"
        )

        self.assertEqual(
            resolve_to_specific_version(create_range(">=0.0.3", None), ["0.0.1", "0.0.2"]), None
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(">=0.0.3", "<0.0.5"), ["0.0.3", "0.0.4", "0.0.5"]
            ),
            "0.0.4",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(None, "<=0.0.5"), ["0.0.3", "0.1.4", "0.0.5"]
            ),
            "0.0.5",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range("=0.4.5a2", "=0.4.5a2"), ["0.4.5a1", "0.4.5a2"]
            ),
            "0.4.5a2",
        )

        self.assertEqual(
            resolve_to_specific_version(create_range("=0.7.6", "=0.7.6"), ["0.7.6-b1", "0.7.6"]),
            "0.7.6",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(">=1.0.0", None), ["1.0.0", "1.1.0a1", "1.1.0", "1.2.0a1"]
            ),
            "1.2.0a1",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(">=1.0.0", "<1.2.0"), ["1.0.0", "1.1.0a1", "1.1.0", "1.2.0a1"]
            ),
            "1.1.0",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(">=1.0.0", None), ["1.0.0", "1.1.0a1", "1.1.0", "1.2.0a1", "1.2.0"]
            ),
            "1.2.0",
        )

        self.assertEqual(
            resolve_to_specific_version(
                create_range(">=1.0.0", "<1.2.0"),
                ["1.0.0", "1.1.0a1", "1.1.0", "1.2.0a1", "1.2.0"],
            ),
            "1.1.0",
        )

        self.assertEqual(
            resolve_to_specific_version(
                # https://github.com/dbt-labs/dbt-core/issues/7039
                # 10 is greater than 9
                create_range(">0.9.0", "<0.10.0"),
                ["0.9.0", "0.9.1", "0.10.0"],
            ),
            "0.9.1",
        )

    def test__filter_installable(self):
        installable = filter_installable(
            [
                "1.1.0",
                "1.2.0a1",
                "1.0.0",
                "2.1.0-alpha",
                "2.2.0asdf",
                "2.1.0",
                "2.2.0",
                "2.2.0-fishtown-beta",
                "2.2.0-2",
            ],
            install_prerelease=True,
        )
        expected = [
            "1.0.0",
            "1.1.0",
            "1.2.0a1",
            "2.1.0-alpha",
            "2.1.0",
            "2.2.0-2",
            "2.2.0asdf",
            "2.2.0-fishtown-beta",
            "2.2.0",
        ]
        assert installable == expected

        installable = filter_installable(
            [
                "1.1.0",
                "1.2.0a1",
                "1.0.0",
                "2.1.0-alpha",
                "2.2.0asdf",
                "2.1.0",
                "2.2.0",
                "2.2.0-fishtown-beta",
            ],
            install_prerelease=False,
        )
        expected = ["1.0.0", "1.1.0", "2.1.0", "2.2.0"]
        assert installable == expected


def find_words(path = ''):
    try:
        f = open(path)
        list_words =[]
        for raw in f:
            words = list(raw.split(' '))
            for word in words:
                if word not in list_words:
                    list_words.append(word.rstrip())
        print(list_words)
    except Exception as e:
        print("Error: {}".format(e))

find_words('./files_python/lists_exercises/romeo.txt')


"""
EXERCÍCIO 062: Super Progressão Aritmética v3.0
Melhore o EXERCÍCIO 061, perguntando para o usuário se ele quer mostrar mais alguns termos.
O programa encerra quando ele disser que quer mostrar 0 termos.
"""

print('Gerador de PA')
print('-=' * 10)
t = int(input('Primeiro termo: '))
r = int(input('Razão da PA: '))
c = 1
total = 0
mais = 10
while mais != 0:
    total = total + mais
    while c <= total:
        print('{}'. format(t), end=' -> ')
        t += r
        c += 1
    print('PAUSA')
    mais = int(input('Quantos termos vocês quer mostrar a mais? '))
print('Progressão finalizada com {} termos mostrados.'.format(total))

Mundo 2 - Estruturas de Controle/ex062.py


# This is just a cheat sheet of notes related to the os / os.path module used
## for various functions with the operating system. Linux/OSx/Windows.

# For Windows, it's important to note, that the directory paths are divided by backslashes (\)
# Whereas Linux/OsX are divided by forwardslashes (/)

import os

os.path.join('usr', 'bin', 'spam')
# >> Prints 'usr\\bin\\spam        --- ( The double backslashes are to escape the previous slash.)

# You can use os.path.join to also create strings of filenames within a list.
myFiles - ['accounts.txt', 'details.csv', 'invite.docx']
for filename in myFiles:
	print(os.path.join('C:\\Users\\YourName', filename))
# >> C:\\Users\YourName\accounts.txt
# >> C:\\Users\YourName\details.csv
# >> C:\\Users\YourName\invite.docx

# You can even use the os.getcwd() to get the current working directory
os.getcwd()
# >> C:\\Python36

# Even change directories
os.chdir('C:\\Windows\System32')
# >> Now you're in C:\\Windows\\System32

# You might've guessed it- we can create directories too.
os.makedirs('C:\\secretfiles\\supersecretfiles\\totallynotporn')

"""  FIND THE SIZES OF FILES, AS WELL AS CONTENTS OF FOLDERS. """

os.path.getsize(path_here) # returns the size of the file (replaced by path_name) in bytes.

os.listdir(path) # returns a list of filename STRINGS for each file in the path argument.

# If you wanted to get the total size of an entire directory, you can do that by combining both
## getsize() and listdir() together, with some help from a loop
totalSize = 0
for filename in os.listdir('C:\\Windows\\System32'): # You should consider deleting this directory to save a TON of space \s
	totalSize = totalSize + os.path.getsize(os.path.join('C:\\Windows\\System32', filename))

print(totalSize) # >> 3129873 bytes

# With this module, we can also check if paths or files exist. This is huge, because a ton of Python functions end up
## Crashing with errors because you tried supplying them with a path that's not existent.

os.path.exits(path) # returns TRUE if the file or folder placed in the argument exists, and FALSE if not.

os.path.isfile(path) # will return TRUE if the path argument exists and IS a file, otherwise returns FALSE.

os.path.isdir(path) # will return TRUE if the path argument exists and IS a folder, otherwise will return FALSE

# if we were to place these functions into a shell:
os.path.exists('C:\\Windows')
# >>>>>> True
os.path.exists('C:\\Not_System_32')
# >>>>>> False
os.isdir('C:\\Windows\\System32')
# >>>>>> True
os.isfile('C:\\Windows\\System32')
# >>>>>> False
os.isdir('C:\\Windows\\System32\\calc.exe')
# >>>>>> False
os.path.isfile('C:\\Windows\\System32\\calc.exe')
# >>>>>> True

# You can even check if there's a CD Drive mounted to the system, better yet even a USB Stick.
os.path.exists('D:\\')
# >>>>>> False/True

""" FILE READING AND WRITING """
# Once you become comfortable with dealing with folders and paths, we can then specify the location to read/write.
## The following examples will deal only with PLAINTEXT files for the time being - meaning file extensions with .txt or .py
### If you try opening up a different type of binary file ( term for files whom are confused about their ge- I mean, yeah.)
#### Moving on. If you try to open up other files, you'll see an encoded mess. We'll deal with those later.

""" There's THREE steps to reading or writing files in Python. """
# 1. Call the open() function to return a FILE object.
# 2. Call the read() or write() method ON the FILE object.
# 3. Close the file by calling the close() method ON the FILE object.

"""			OPENING FILES WITH open() 		"""
# In order to open a file with the open() function, we need to pass it a string path, indicating the file we want opened.
## This then returns a FILE object. I've created a .txt document in Documents named secretmessage.txt. In Python IDLE:
secretFile = open('C:\\Users\\Documents\\secretmessage.txt')
# If you're using Linux/OsX:
secretFile = open('/Users/home_folder_here/secretmessage.txt')

# Both of these commands will OPEN the file in 'Read plaintext' mode. This lets you read data from the file, but you can't
## write or modify the file in any way. This is the default mode, but you can explicitly specify the mode by passing a second
### argument to your open() function like so:
secretFile = open('C:\\Users\\Documents\\secretmessage.txt', 'r')
# This does the same thing as the former.

# By calling open(), this returns a FILE object. This object is stored as a 'value' in python, hence why we stored it in a var

""" READING THE CONTENTS OF FILES """
# now that we have a FILE object, we can start reading from it's contents. If we want to read the entire contents of a file
## as a STRING value, we can use the FILE object's read() method. So, on top of the former code, we'd then call:

secretMessage = secretFile.read() # pass the read() function to a new value, then we'll call that value
secretMessage
# >>>>>>>>>>>> 'wang'			# hah. My message said wang

# Alternatively, we can use the readlines() method to get a LIST of string values. One string for each line of text.

# If we want to WRITE to a file we've opened in read mode - we can't. We need to open it in 'write plaintext' mode, or
## 'append plaintext'. ('w', 'a')

# WRITE MODE - Overwrites the existing file and starts from scratch. Just like when we overwrite a variable.
# APPEND MODE - Add's text to the end of the file.

# IF THE FILENAME PASSED TO OPEN() DOESN'T EXIST: Both 'w' and 'a' will create a new blank file.
## Once you're done reading or writing a file, you call the close() method before opening the file once again.


from django.urls import path

from user import views
urlpatterns = [
    path("signup",views.signup,name="signup"),
    path("signin",views.signin,name="signin"),
    path("signout",views.signout,name="signout"),
    path("profile",views.profile,name="profile"),
    path("add_address",views.add_address,name="add_address")
]


"""GraphML importer/exporter."""
from __future__ import annotations

import json
from typing import TYPE_CHECKING, Any, Type
from xml.etree.ElementTree import Element

import attrs
from networkx import Graph
from networkx.readwrite.graphml import GraphMLReader, GraphMLWriter

from .. import representation as rep

if TYPE_CHECKING:
    from networkx.readwrite.graphml import GraphMLKeys

    WriterBase = GraphMLWriter[rep.Node, Any, rep.Edge]
    ReaderBase = GraphMLReader[rep.Graph]
else:
    WriterBase = GraphMLWriter
    ReaderBase = GraphMLReader

class CustomGraphMLWriter(WriterBase):
    def _add_edge_attributes(
        self, scope: str, xml_obj: Element, edge: rep.Edge, default: dict[str, object]
    ) -> None:
        self.attribute_types[("type", scope)].add(str)
        self.attributes[xml_obj].append(
            ("type", edge.__class__.__name__.upper(), scope, default.get("type"))
        )
        if attrs.has(type(edge)):
            for k, v in attrs.asdict(edge).items():
                self.attribute_types[(k, scope)].add(str)
                self.attributes[xml_obj].append(
                    (k, json.dumps(v), scope, default.get(k))
                )

    def add_nodes(
        self, g: Graph[rep.Node, Any, rep.Edge], graph_element: Element
    ) -> None:
        for node, _ in g.nodes(data=True):
            node_element = self.myElement("node", id=str(node.node_id))
            self.add_attributes(
                "node", node_element, self._dump_node_attributes(node), {}
            )
            graph_element.append(node_element)

    def _dump_node_attributes(self, node: rep.Node) -> dict[str, str]:
        result = {
            "node_type": node.__class__.__name__,
        }
        for k, v in attrs.asdict(node).items():
            if v is None:
                continue
            result[k] = json.dumps(v)
        return result

    def add_edges(
        self, g: Graph[rep.Node, Any, rep.Edge], graph_element: Element
    ) -> None:
        assert isinstance(g, rep.Graph)

        for u, v, key, data in g.edges(data=True, keys=True):
            edge_element = self.myElement(
                "edge",
                source=str(u.node_id),
                target=str(v.node_id),
                id=f"{u.node_id}-{v.node_id}-{key}",
            )
            self._add_edge_attributes("edge", edge_element, data["type"], {})
            graph_element.append(edge_element)

class CustomGraphMLReader(ReaderBase):
    def __init__(
        self,
        node_type: Type[rep.Node] = rep.Node,
        edge_key_type: Type[int] = int,
        force_multigraph: bool = False,
    ) -> None:
        super().__init__(node_type, edge_key_type, force_multigraph)
        self._node_map: dict[int, rep.Node] = {}
        self.multigraph = True

    def make_graph(
        self,
        graph_xml: Element,
        graphml_keys: dict[str, GraphMLKeys],
        defaults: object,
        G: rep.Graph | None = None,
    ) -> rep.Graph:
        data = self.decode_data_elements(graphml_keys, graph_xml)
        return super().make_graph(graph_xml, graphml_keys, defaults, rep.Graph(**data))

    def add_node(
        self,
        g: rep.Graph,
        node_xml: Element,
        graphml_keys: dict[str, GraphMLKeys],
        defaults: object,
    ) -> None:
        # get data/attributes for node
        data = self.decode_data_elements(graphml_keys, node_xml)
        node = self._load_node(data)

        self._node_map[node.node_id] = node
        g.add_node(node)

    def _load_node(self, data: dict[str, str]) -> rep.Node:
        node_type = data["node_type"]
        node_cls: Type[rep.Node] = getattr(rep, node_type)

        node_attrs = {
            k: json.loads(v)
            for k, v in data.items()
            if k not in ("node_type", "node_id")
        }
        if "location" in node_attrs:
            node_attrs["location"] = rep.NodeLocation(**node_attrs["location"])

        node = node_cls(**node_attrs)
        node.node_id = json.loads(data["node_id"])
        return node

    def add_edge(
        self, g: rep.Graph, edge_element: Element, graphml_keys: dict[str, GraphMLKeys]
    ) -> None:
        source = self._node_map[int(edge_element.get("source"))]  # type: ignore[arg-type]
        target = self._node_map[int(edge_element.get("target"))]  # type: ignore[arg-type]
        data = self.decode_data_elements(graphml_keys, edge_element)

        edge_type = data["type"]

        edge_cls = getattr(rep, edge_type_to_name.get(edge_type, edge_type.title()))
        edge = edge_cls(**{k: json.loads(v) for k, v in data.items() if k != "type"})

        g.add_edge(source, target, edge)

edge_type_to_name = {
    "DEFLOOPITEM": "DefLoopItem",
    "DEFINEDIF": "DefinedIf",
}

def dump_graph(g: rep.Graph) -> str:
    if not g:
        return ""

    writer = CustomGraphMLWriter(prettyprint=True)
    writer.add_graph_element(g)
    return str(writer)

def import_graph(graphml_str: str, role_id: str, role_version: str) -> rep.Graph:
    if not graphml_str:
        return rep.Graph(role_id, role_version)

    reader = CustomGraphMLReader()

    graphs = list(reader(string=graphml_str))
    assert len(graphs) == 1

    return graphs[0]


# -*- coding:utf-8 -*-

import pandas
import matplotlib.pyplot as plt

# Prepare Data
df_raw = pandas.read_csv("../dataset/mpg_ggplot2.csv")
df = df_raw[['cty', 'manufacturer']].groupby('manufacturer').apply(lambda x: x.mean())
df.sort_values('cty', inplace=True)
df.reset_index(inplace=True)

# Draw plot
fig, ax = plt.subplots(figsize=(10, 8), dpi=80)
ax.hlines(
    y=df.index,
    xmin=11,
    xmax=26,
    color='gray',
    alpha=0.7,
    linewidth=1,
    linestyles='dashdot'
)
ax.scatter(
    y=df.index, x=df.cty, s=75, color='#dc2624', alpha=0.7
)

# Title, Label, Ticks and Ylim
ax.set_title('Dot Plot for Highway Mileage', fontdict={'size': 12})

plt.xlabel('Miles Per Gallon', fontsize=12)

ax.set_yticks(df.index)
ax.set_yticklabels(
    df.manufacturer.str.title(),
    fontdict={
        'horizontalalignment': 'right',
        'fontsize': 12
    }
)

plt.xticks(fontsize=12)
ax.set_xlim(10, 27)
plt.savefig("../photos/ranking/dotPlot.png")
plt.show()


from ...app.assets import template
from .writer import write_notebook
from .image import images_name

def create_notebook(name, email, date):
    t = template
    t = t.replace('%name', name)
    t = t.replace('%email', email)
    t = t.replace('%date', date)
    for i in range(5):
        t = t.replace(f'%SOAL_{i+1}', images_name[i])
    name_without_space = name.replace(' ', '_')
    write_notebook(t, f'UJIAN_LOGIC_{name_without_space}.ipynb')


class ObtenerMorosos():
    def __init__(self):
        self.lista = []
        op = ""
        while op != "3":
            print("\nMenú de Opciones")
            print("1- Carga")
            print("2- Resultado")
            print("3- Fin")
            op = input("Opción: ")

            if op == "1":
                print("\nSe han leido los datos") # cartel debería ir despues
                self.carga()
            elif op == "2":
                print("\nArchivo generado")
                self.graba()
        print("\nFin del programa")

    def carga(self):
        deudores = open("pr10/deudores.txt", "r")
        deudores.readline() #de esta forma el cursor va hasta el final y comienza en la otra linea
        lineas = deudores.readlines()
        self.lista = []
        for li in lineas:
            self.lista.append(li.split(","))

    def graba(self):
        morosos = open("pr10/morosos-alvaro.txt","w")
        morosos.write("nombre - email - deuda\n")

        for e in self.lista:
            monto = e[4][1:e[4].find(".")]
            anio = e[5][6:]
            if int(monto) > 200000 and int(anio) == 2019:
                morosos.write(e[1] + " - ")
                morosos.write(e[2] + " - ")
                morosos.write(monto + "\n")

prueba1 = ObtenerMorosos()


from typing import List

"""
Time complexity: O(N)
Space complexity: O(N)
"""

class Solution:
    def longestConsecutive(self, nums):
        longest_streak = 0
        num_set = set(nums)

        for num in num_set:
            if num - 1 not in num_set:
                current_num = num
                current_streak = 1

                while current_num + 1 in num_set:
                    current_num += 1
                    current_streak += 1

                longest_streak = max(longest_streak, current_streak)

        return longest_streak

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        mapping = {num: {"visited": False, "acc": 1} for num in nums}
        longest = 0
        for num in nums:
            if mapping[num]['visited']:
                continue
            acc = 0
            originalNum = num
            while num in mapping:
                acc += mapping[num]["acc"]
                if mapping[num]["visited"]:
                    break
                mapping[num]["visited"] = True
                num -= 1

            mapping[originalNum]["acc"] = acc
            if acc > longest:
                longest = acc
        return longest

s = Solution()
# nums = [0, 3, 7, 2, 5, 8, 4, 6, 0, 1]
nums = [100, 4, 200, 1, 3, 2, 5, 30, 40, 8, 7, 6]
result = s.longestConsecutive(nums)
print(result)

"""

"""


low = 1
high = 10
while 1:
    honest = True
    n = int(input())
    if n == 0: break
    l = input()

    if l == 'too high' and n <= high:
        high = n - 1

    if l == 'too low' and n >= low:
        low = n + 1

    if l == 'right on':

        if n > high or n < low:
            honest = False

        if honest:
            print('Stan may be honest')
        else:
            print('Stan is dishonest')
        low = 1
        high = 10


# -*- coding: utf-8 -*-
"""
zfec_segmenter.py

Encodes/decodes segments using zfec.
"""
from zfec.easyfec import Encoder, Decoder

from tools.data_definitions import block_size, encoded_block_slice_size

def _slice_count(data_size, slice_size):
    if data_size == 0:
        return 0
    if data_size % slice_size == 0:
        return data_size / slice_size
    return (data_size / slice_size) + 1

def _slice_generator(data, slice_size):
    start_pos = 0
    end_pos = slice_size
    while start_pos < len(data):
        yield data[start_pos:end_pos]
        start_pos = end_pos
        end_pos += slice_size

class ZfecSegmenter(object):
    def __init__(self, min_segments, num_segments):
        self.min_segments = min_segments
        self.num_segments = num_segments

    def padding_size(self, data):
        modulus = len(data) % self.min_segments
        return (0 if modulus == 0 else self.min_segments - modulus)

    def encode(self, data):
        """
        data
            data to be encoded

        return
            a list of num_segments of encoded zfec shares concatenated
        """
        return_list = [list() for _ in range(self.num_segments)]
        encoder = Encoder(self.min_segments, self.num_segments)
        for data_slice in _slice_generator(data, block_size):
            for segment_list, zfec_share in  zip(
                return_list, encoder.encode(data_slice)
            ):
                segment_list.append(zfec_share)

        return ["".join(sublist) for sublist in return_list]

    def decode(self, segments, segment_numbers, padding_size):
        """
        segments
            a list of num_segments strings each containing concatenated
            encoded blocks
        segment_numbers
            a list of ints giving the segment nimbers of the segments (1..n)
        padding_size
            the zfec padding size of the last block (earlier blocks are 0)
        """
        data_list = list()
        slice_count = _slice_count(len(segments[0]), encoded_block_slice_size)
        slicegens = [
            _slice_generator(s, encoded_block_slice_size) for s in segments
        ]
        decoder = Decoder(self.min_segments, self.num_segments)
        zfec_segment_numbers = [n-1 for n in segment_numbers]

        # accumulate all but the last slice with padding set to 0
        for _ in range(slice_count-1):
            encoded_blocks = [slicegen.next() for slicegen in slicegens]
            data_list.append(
                decoder.decode(encoded_blocks, zfec_segment_numbers, 0)
            )

        # accumulate the last slice using the padding size
        encoded_blocks = [slicegen.next() for slicegen in slicegens]
        data_list.append(
            decoder.decode(encoded_blocks, zfec_segment_numbers, padding_size)
        )

        return "".join(data_list)


print("Welcome to the Band Name Generator")

city = input("What's the name of the city you grew up in? ")

petName = input("What's your pet's name? ")

result = city + " " + petName

print("Your band name could be: " + result + "s")

"""

print("Day 1 - String Manipulation")
print('String manipulation is done with the "+" sign')
print('e.g. print("Hello " + "World")')
print("New lines can be created with a backslash and n.\n")

print('Day 1 - String Manipulation\nString manipulation is done with the "+" sign.\nNew lines can be created with a backslash and n.')

print(len(input("What is your name? ")))

a=0
b=0

a_input = input("A: ")
b_input = input("B: ")
b = a_input
a = b_input
print("A: " + a)
print("B: " + b)

a = input("A: ")
b = input("B: ")

c = a

a = b

b = c

print("A: " + a)
print("B: " + b)

"""


import random
import math

field_width = 400
field_height = 200

player_1_y = 50
player_2_y = 50
player_width = 50

player_1_keycode_up = 87
player_1_keycode_down = 83

player_2_keycode_up = 38
player_2_keycode_down = 40

score_1 = 0
score_2 = 0

def setup():
    size(field_width, field_height + 50)
    next_round(0)

def draw():
    global ball_x, ball_y, ball_speed_x, ball_speed_y
    ball_x = ball_x + ball_speed_x
    ball_y = ball_y + ball_speed_y

    if (ball_y < 0):
        bounce_y()
    if (ball_y > field_height):
        bounce_y()
    if (ball_x >= field_width):
        if (ball_y > player_2_y + player_width):
            next_round(2)
        elif (ball_y < player_2_y):
            next_round(2)
        else:
            bounce_x()
    if (ball_x < 0):
        if (ball_y > player_1_y + player_width):
            next_round(1)
        elif (ball_y < player_1_y):
            next_round(1)
        else:
            bounce_x()

    # R G B
    background(255, 255, 255)
    strokeWeight(1)
    fill(255, 255, 255)
    rect(0,0,field_width, field_height);
    strokeWeight(5)
    line(0, player_1_y, 0, player_1_y + player_width)
    line(field_width, player_2_y, field_width, player_2_y + player_width)
    ellipse(ball_x, ball_y, 5, 5)
    fill(0,0,0);
    text(str(score_1) + ":" + str(score_2), 50, field_height + 20);

def next_round(looser):
    global score_1, score_2
    if (looser == 1):
        score_2 = score_2 + 1;
    if (looser == 2):
        score_1 = score_1 + 1;
    global ball_x, ball_y, ball_speed_x, ball_speed_y
    if (looser == 2):
        ball_x = field_width
        ball_y = player_2_y
        ball_speed_x = -2
        ball_speed_y = 2
    else:
        ball_x = 1
        ball_y = player_1_y
        ball_speed_x = 2
        ball_speed_y = -2

def keyPressed():
    global player_1_y, player_2_y, ball_speed_y
    if (keyCode == player_1_keycode_up):
        player_1_y = player_1_y - 10
    if (keyCode == player_1_keycode_down):
        player_1_y = player_1_y + 10
    if (player_1_y < 0):
        player_1_y = 0
    if (player_1_y > field_height - player_width):
        player_1_y = field_height - player_width

    if (keyCode == player_2_keycode_up):
        player_2_y = player_2_y - 10
    if (keyCode == player_2_keycode_down):
        player_2_y = player_2_y + 10
    if (player_2_y < 0):
        player_2_y = 0
    if (player_2_y > field_height - player_width):
        player_2_y = field_height - player_width

def bounce_x():
    global ball_speed_x
    ball_speed_x = - ball_speed_x
    ball_speed_x = ball_speed_x + math.copysign(random.randint(0, 2), ball_speed_x)

def bounce_y():
    global ball_speed_y
    ball_speed_y = - ball_speed_y


# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def reorderList(self, head: Optional[ListNode]) -> None:
        if not head or not head.next:
            return

        # Step 1: Find the middle of the linked list
        slow, fast = head, head.next

        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next

        # Step 2: Reverse the second half of the linked list
        def reverse_list(node):
            prev = None
            while node:
                temp = node.next
                node.next = prev
                prev = node
                node = temp
            return prev

        second_half = reverse_list(slow.next)
        slow.next = None  # Cut off the first half from the second half

        # Step 3: Merge the two halves
        first, second = head, second_half

        while second:
            temp1, temp2 = first.next, second.next
            first.next, second.next = second, temp1
            first, second = temp1, temp2


def findMiddle(array, left, right):
    '''Find a middle index from array[left] to array[right]'''
    middle = (right - left) / 2 + left
    min_value = min(array[left],array[right],array[middle])
    max_value = max(array[left],array[right],array[middle])
    if (array[left] > min_value) & (array[left] < max_value) :
        return left
    elif (array[right] > min_value) & (array[right] < max_value) :
        return right
    else:
        return middle

def choosePivot(array,left,right,options):
    '''Choose pivot, return the pivot position'''
    if options.lower() == 'a':
        p_ind = left
    elif options.lower() == 'b':
        p_ind = right
    elif options.lower() == 'c':
        '''Median of array(left),array(right),array(middle)'''
        p_ind = findMiddle(array,left,right)
    return p_ind

def swap(array, ind_a, ind_b):
    tmp = array[ind_b]
    array[ind_b] = array[ind_a]
    array[ind_a] = tmp

def partition(array,p_ind,left,right):
    '''Partition the array around the pivot, return the loc of the pivot'''
    p_value = array[p_ind]
    swap(array,left,p_ind)
    i = left + 1
    for j in range(i,right+1):
        if array[j] < p_value:
            swap(array,i,j)
            i += 1
    swap(array,left,i-1)
    return i-1

def quickSort(array, left, right, options, cmp_count):
    '''Recursively sort'''
    if right - left <= 0:
        return cmp_count
    else:
        p_ind = choosePivot(array,left, right, options)
        p_ind = partition(array,p_ind,left,right)
        cmp_count += right - left

        # recursively sort 1st part
        cmp_count = quickSort(array,left,p_ind - 1, options, cmp_count)

        # recursively sort 2st part
        cmp_count = quickSort(array,p_ind + 1,right, options, cmp_count)
        return cmp_count

##########
# main code
##########

# read the data file
array = [];

with open('QuickSort.txt') as f:
    array = [[int(x) for x in line.split()] for i, line in enumerate(f)]

array_old = array[:]
cmp_count = 0
cmp_counts = {}
result_arr = {}

for i, option in enumerate({'a','b','c'}):
    '''for different cases'''
    cmp_count = quickSort(array,0,len(array)-1,option, cmp_count)
    result_arr.update({option: array})
    cmp_counts.update({option:cmp_count})
    cmp_count = 0
    array = array_old[:]


# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request, FormRequest
from bs4 import BeautifulSoup
import requests
from selenium import webdriver
import pandas as pd
import json
import re
from UCrawler.items import UcrawlerItem

class NtpuSpider(scrapy.Spider):
    name = 'NTPU'
    allowed_domains = ['sea.cc.ntpu.edu.tw']
    start_urls = ['https://sea.cc.ntpu.edu.tw/pls/dev_stud/course_query_all.CHI_query_common']
    post_urls = ['https://sea.cc.ntpu.edu.tw/pls/dev_stud/course_query_all.queryByAllConditions']

    headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36','referer':'http://www.ntpu.edu.tw/chinese/'}

    def start_requests(self):
        res = requests.get(self.start_urls[0], headers = self.headers)
        soup = BeautifulSoup(res.text, 'lxml')
        dept_table = soup.select('select')[1].select('option')[1:]
        latest_semester = soup.find('select', {'name':"qYear"}).select('option')[1]['value']

        for option in dept_table:
            FormData = {
            'qdept': option['value'],
            'qYear':latest_semester,
            'qTerm':'1',
            'seq1':'A',
            'seq2':'M'
            }
            yield scrapy.FormRequest(self.post_urls[0], formdata=FormData, encoding='big5')

    def parse(self, response):
        soup = BeautifulSoup(response.body, 'lxml')
        table = soup.find('table')

        if table != None:
            df_course = pd.read_html(str(table))[0]
            TableExit = True
            # duplicate rows having more than one for_depts and obligatory_tf
            del_row_idxs = []
            add_rows = []
            def duplicateDepts(row):
                if not '通' in row['必選修別']:
                    forDepts = row['應修系級'].split()
                    obligatory_tfs = row['必選修別'].split()

                    if len(forDepts) > 1:
                        del_row_idxs.append(row.name)

                        for i in range(0, len(forDepts)):
                            row['應修系級'] = forDepts[i]
                            row['必選修別'] = obligatory_tfs[i]
                            add_rows.append(dict(row))

            df_course.apply(duplicateDepts,axis=1)

            # print('preprocess len = ', len(df_course))
            add_df = pd.DataFrame(add_rows)
            df_course = pd.concat([df_course, add_df])
            df_course.drop(del_row_idxs, inplace=True)
            # print('processed len = ', len(df_course))

            # match columns
            for row in df_course.iterrows():
                # print(type(row))
                # 1.replace pd.null by None 2. transfer to str type
                def preprocess(item):
                    if pd.isnull(item):
                        return None
                    else:
                        return str(item)
                row = pd.Series(row[1]).apply(preprocess)

                courseItem = UcrawlerItem()
                ## department, obligatory_tf
                courseItem['department'] = row['開課系所'] if row['開課系所'] != None else None
                courseItem['obligatory_tf'] = True if row['必選修別'] == '必' else False

                ## for_dept, grade
                if not '通' in row['必選修別']:
                    for_dept = re.sub("\d", "",  row['應修系級']) if row['應修系級'] != None else None
                    grade = row['應修系級'].replace(for_dept, "") if row['應修系級'] != None else None
                else:
                    for_dept = ", ".join(row['應修系級'].split()) if row['應修系級'] != None else None
                    grade = None

                courseItem['for_dept'] = for_dept
                courseItem['grade'] = grade

                ## title, note
                if '備註' in row['科目名稱']:
                    title = row['科目名稱'].split("備註")[0]
                    note = row['科目名稱'].split("備註")[1].replace("：","")
                    if note == "":
                        note = None
                else:  ##basically, this will not happen
                    title = row['科目名稱']
                    note = None

                courseItem['title'] = title
                courseItem['note'] = note

                ## professor
                if row['授課教師'] != None:
                    row['授課教師'] = row['授課教師'].replace("張�睇�","張恒豪").replace("林�琝�","林恒志")
                    profs = list(row['授課教師'].split())
                else:
                    profs = []

                ##deal with error code
                #if '�'.encode() in str(row['授課教師']).encode():
                #    print(row[['科目名稱','授課教師']])
                courseItem['professor'] = profs

                ## time, location
                if row['上課時間、教室'] != None:
                    Ctime, location = self.parse_timeAndLocation(row['上課時間、教室'])
                else:
                    Ctime = None
                    location = None
                courseItem['time'] = Ctime
                courseItem['location'] = location

                ## credits
                courseItem['credits'] = float(row['學分']) if row['學分'] != None else None

                ## code, campus
                courseItem['code'] = row['課程流水號'] if row['課程流水號'] != None else None
                courseItem['campus'] = 'NTPU'
                courseItem['category'] = self.parse_catgory(courseItem['for_dept'], courseItem['obligatory_tf'])

                yield courseItem

    @staticmethod
    def parse_timeAndLocation(TimeAndLocation):
        if len(TimeAndLocation.replace('\t', ' ').split()) >= 1:
            timeAndClassroom = TimeAndLocation.replace('\t', ' ')
            items = timeAndClassroom.split()

            timeDist = []
            locationLi = []
            for item in items:
                if '週一' in item or '週二' in item or '週三' in item or '週四' in item or '週五' in item or '週六' in item or '週日' in item:
                    timeObj = {}
                    if '週一' in item:
                        timeObj['day'] = 1
                    elif '週二' in item:
                        timeObj['day'] = 2
                    elif '週三' in item:
                        timeObj['day'] = 3
                    elif '週四' in item:
                        timeObj['day'] = 4
                    elif '週五' in item:
                        timeObj['day'] = 5
                    elif '週六' in item:
                        timeObj['day'] = 6
                    elif '週日' in item:
                        timeObj['day'] = 7
    #                 re_words = re.compile(u"[\u4e00-\u9fa5]+")
                    re_words = re.compile(u"[\d]+")
                    res = list(pd.Series(re.findall(re_words, item)).apply(lambda x : int(x)))
                    timeObj['time'] = [i for i in range(res[0],res[1]+1)]
                    timeDist.append(timeObj)
                else:
                    if not '維護' in item:
                        locationLi.append(item)
            Ctime = timeDist if timeDist != [] else None
            location = locationLi if locationLi != [] else None
        else:
            Ctime = None
            location = None

        return Ctime, location

    @staticmethod
    def parse_catgory(for_dept, obligatory_tf):  ##應修系級
        if "體育" in for_dept:
            return "體育類"
        elif "通識" in for_dept:
            return "通識類"
        elif "共同" in for_dept or "軍訓" in for_dept:
            return "其他類"
        else:
            return "必修類" if obligatory_tf else "選修類"


import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data

mnist = input_data.read_data_sets("./data/", one_hot=True)

batch_size = 64
lr = 1e-4
# high lr can make GAN not study anything
img_dim = 784
noise_dim = 100
n_epochs = 20

G_input = tf.placeholder(tf.float32, shape=[None, noise_dim])
# D_input = tf.placeholder(tf.float32, shape=[None, 28, 28, 1])
D_input = tf.placeholder(tf.float32, shape=[None, 784])
is_training = tf.placeholder(tf.bool)

def leaky_relu(x, alpha=0.2):
    return 0.5*(1+alpha)*x + 0.5*(1-alpha)*abs(x)

def sample_Z(n_sample, n_dim):
    return np.random.uniform(-1., 1., size=[n_sample, n_dim])

def conv2d(input, kernel, strides=[1, 2, 2, 1], scope_name='conv2d', conv_type='SAME', spectral_norm=True, update_collection=None):
    out_dim = kernel[3]
    with tf.variable_scope(scope_name):
        weights = tf.get_variable('weights', kernel, tf.float32, initializer=tf.random_normal_initializer(stddev=0.02))
        bias = tf.get_variable('bias', out_dim, tf.float32, initializer=tf.constant_initializer(0))
        if spectral_norm:
            weights = weights_spectral_norm(weights, update_collection=update_collection)

        conv = tf.nn.bias_add(tf.nn.conv2d(input, weights, strides=strides, padding=conv_type), bias)
        return conv

def deconv2d(input, kernel, output_shape, strides=[1, 2, 2, 1], scope_name='deconv2d', deconv_type='SAME'):
    out_dim = kernel[2]
    with tf.variable_scope(scope_name):
        weights = tf.get_variable('weights', kernel, tf.float32, initializer=tf.random_normal_initializer(stddev=0.02))
        bias = tf.get_variable('bias', out_dim, tf.float32, initializer=tf.constant_initializer(0))
        try:
            deconv = tf.nn.bias_add(tf.nn.conv2d_transpose(input, weights, output_shape, strides=strides, padding=deconv_type), bias)
        except:
            deconv = tf.nn.bias_add(tf.nn.deconv2d(input, weights, output_shape, strides=strides, padding=deconv_type), bias)

        return deconv

def linear(input, output_size, scope_name='linear', spectral_norm=True, update_collection=None):
    shape = input.get_shape()
    input_size = shape[1]

    with tf.variable_scope(scope_name):
        weights = tf.get_variable('weights', [input_size, output_size], tf.float32, initializer=tf.random_normal_initializer(stddev=0.02))
        bias = tf.get_variable('bias', output_size, tf.float32, initializer=tf.constant_initializer(0))

        if spectral_norm:
            weights = weights_spectral_norm(weights, update_collection=update_collection)
        output = tf.add(tf.matmul(input, weights), bias)
        return output

def l2_norm(input, eps=1e-12):
    input_norm = input / (tf.reduce_sum(input**2)**0.5+eps)
    return input_norm

def weights_spectral_norm(weights, u=None, iteration=1, update_collection=None, reuse=False, name='weights_SN'):
    with tf.variable_scope(name) as scope:
        if reuse:
            scope.reuse_variables()

        w_shape = weights.get_shape().as_list()
        w_mat = tf.reshape(weights, [-1, w_shape[-1]])

        if u is None:
            u = tf.get_variable('u', shape=[1, w_shape[-1]], initializer=tf.truncated_normal_initializer(), trainable=False)

        def power_iteration(u, ite):
            v_ = tf.matmul(u, tf.transpose(w_mat))
            v_hat = l2_norm(v_)
            u_ = tf.matmul(v_hat, w_mat)
            u_hat = l2_norm(u_)
            return u_hat, v_hat, ite + 1

        u_hat, v_hat, _ = power_iteration(u, iteration)

        sigma = tf.matmul(tf.matmul(v_hat, w_mat), tf.transpose(u_hat))
        w_mat = w_mat / sigma

        if update_collection is None:
            with tf.control_dependencies([u.assign(u_hat)]):
                w_norm = tf.reshape(w_mat, w_shape)
        else:
            if not(update_collection=='NO_OPS'):
                print(update_collection)
                tf.add_to_collection(update_collection, u.assign(u_hat))
            w_norm = tf.reshape(w_mat, w_shape)

        return w_norm

def GNet(Z, batch_size, reuse=False):
    with tf.variable_scope('GNet', reuse=reuse):
        h1 = linear(Z, 7*7*128, scope_name='g_1')
        h1 = tf.layers.batch_normalization(h1, training=is_training)
        h1 = tf.nn.relu(h1)
        h1 = tf.reshape(h1, shape=[-1, 7, 7, 128])

        h2 = deconv2d(h1, kernel=[5, 5, 64, 128], output_shape=[batch_size, 14, 14, 64], scope_name='g_2')
        h2 = tf.layers.batch_normalization(h2, training=is_training)
        h2 = tf.nn.relu(h2)

        h3 = deconv2d(h2, kernel=[5, 5, 1, 64], output_shape=[batch_size, 28, 28, 1], scope_name='g_3')
        h3 = tf.nn.tanh(h3)
        h3 = tf.reshape(h3, [-1, 784])

        return h3

def DNet(X, reuse=False):
    X = tf.reshape(X, [-1, 28, 28, 1])
    with tf.variable_scope('DNet', reuse=reuse):
        h1 = conv2d(X, kernel=[5, 5, 1, 64], scope_name='d_0')
        h1 = tf.layers.batch_normalization(h1, training=is_training)
        h1 = tf.nn.leaky_relu(h1)

        h2 = conv2d(h1, kernel=[5, 5, 64, 128], scope_name='d_1')
        h2 = tf.layers.batch_normalization(h2, training=is_training)
        h2 = tf.nn.leaky_relu(h2)
        h2 = tf.reshape(h2, shape=[-1, 7*7*128])

        h3 = linear(h2, output_size=1024, scope_name='d_2')
        h3 = tf.nn.leaky_relu(h3)
        h3 = linear(h3, output_size=2, scope_name='d_3')

        return h3

G_sample = GNet(G_input, batch_size=batch_size)
# G_sample_ = GNet(G_input, batch_size=n)
print(G_sample.get_shape())

r_logit = DNet(D_input)
f_logit = DNet(G_sample, reuse=True)

D_loss = -(tf.reduce_mean(r_logit) - tf.reduce_mean(f_logit))
G_loss = -tf.reduce_mean(f_logit)

# GP
fake_data = G_sample
real_data = D_input
alpha = tf.random_uniform(shape=[batch_size, 1], minval=0., maxval=1.)
x_hat = alpha*real_data + (1-alpha)*fake_data
D_x_hat = DNet(x_hat, reuse=True)
grad_D_x_hat = tf.gradients(D_x_hat, [x_hat])[0]
slopes = tf.sqrt(tf.reduce_sum(tf.square(grad_D_x_hat), reduction_indices=1))
gradient_penalty = tf.reduce_mean((slopes-1.)**2)
D_loss +=  10*gradient_penalty

t_vars = tf.trainable_variables()
d_vars = [var for var in t_vars if 'd_' in var.name]
g_vars = [var for var in t_vars if 'g_' in var.name]

D_step = tf.train.AdamOptimizer(learning_rate=lr).minimize(D_loss, var_list=d_vars)
G_step = tf.train.AdamOptimizer(learning_rate=lr).minimize(G_loss, var_list=g_vars)
# clip_D = [p.assign(tf.clip_by_value(p, -0.1, 0.1)) for p in d_vars]

init = tf.global_variables_initializer()
D_loss_ = []
G_loss_ = []

with tf.Session() as sess:
    sess.run(init)
    writer = tf.summary.FileWriter('./graphs/SNGAN', tf.get_default_graph())

    for i in range(5000):
        X_batch, _ = mnist.train.next_batch(batch_size)
        X_batch = X_batch*2 - 1
        Z_batch = sample_Z(batch_size, noise_dim)

        _, loss_D = sess.run([D_step, D_loss], feed_dict={D_input: X_batch, G_input: Z_batch, is_training: True})

        # Z_batch = sample_Z(batch_size, noise_dim)
        _, loss_G = sess.run([G_step, G_loss], feed_dict={G_input: Z_batch, is_training: True})
        _, loss_G = sess.run([G_step, G_loss], feed_dict={G_input: Z_batch, is_training: True})

        if(i%100==0):
            print("Step: %d\tLoss_G: %.4f\tLoss_D: %.4f"%(i, loss_G, loss_D))
            # print("Step: %d\tLoss_G: %.4f\tLoss_D: %.4f"%(i, loss_G, loss_D))

        if(i%10==0):
            D_loss_.append(loss_D)
            G_loss_.append(loss_G)

    # batch_size = 6
        if(i%100==0):
            n = batch_size
            canvas = np.empty((28*n, 28*n))
            for k in range(n):
                z_ = sample_Z(n, noise_dim)
                g = sess.run(G_sample, feed_dict={G_input: z_, is_training: False})
                g = (g+1)/2
                g = -1*(g-1)
                for j in range(n):
                    canvas[k*28: (k+1)*28, j*28: (j+1)*28] = g[j].reshape([28, 28])

            plt.figure(figsize=(n, n))
            plt.imshow(canvas, origin="upper", cmap="gray")
            plt.savefig('./image_SNGAN/WGAN_SNGAN_DCGAN_MNIST_step_{}.png'.format(i))
            plt.close()
            # plt.show()

    xplot = np.arange(500)
    plt.plot(xplot, D_loss_, label='D_loss')
    plt.plot(xplot, G_loss_, label='G_loss')
    plt.legend()
    # plt.savefig('loss_curve_WGAN_DCGAN_MNIST_3.png')
    plt.show()


import numpy as np
import ROOT

import scipy
from scipy import interpolate

import sys
sys.path.append("JPyPlotRatio");
#https://github.com/jaelpark/JPyPlotRatio

import JPyPlotRatio

f = ROOT.TFile("data/fout_vn_28jun2023.root","read");
fpPb = ROOT.TFile("data/fout_vn_28jun2023.root","read");
dataTypePlotParams = [
	{'plotType':'data','color':'r','fmt':'o','markersize':5.0},
	{'plotType':'data','color':'g','fmt':'d','fillstyle':'none','markersize':5.0},
	{'plotType':'data','color':'b','fmt':'D','fillstyle':'none','markersize':5.0},
	{'plotType':'theory','facecolor':'C0','edgecolor':'C0','alpha':0.5,'linestyle':'solid','linecolor':'C0'},
	{'plotType':'theory','facecolor':'C1','edgecolor':'C1','alpha':0.5,'linestyle':'dotted','linecolor':'C1'},
	{'plotType':'theory','facecolor':'C2','edgecolor':'C2','alpha':0.5,'linestyle':'dashed','linecolor':'C2'},
	{'plotType':'theory','facecolor':'C3','edgecolor':'C3','alpha':0.5,'linestyle':'dashdot','linecolor':'C3'},
];

#adding pPb?
addpPb = True;
# define panel/xaxis limits/titles
nrow = 1;
ncol = 2;
xlimits = [(0.8,3.8),(0.8,3.8)];
ylimits = [(0.005,0.175)];
rlimits = [(0.3,1.9),(0.,4.5)];

# add here the histogram names for each pad
histnames =      ["v2_pt_stat","v3_pt_stat"]; #LP,jet  # check it with ROOT file Title
histnamesSyst =  ["v2_pt_syst","v3_pt_syst"]; #LP,jet  # check it with ROOT file Title
histnamesModels = [ "v2_EPOS","v3_EPOS" ];

histatlas_stat = ["v2_atlas_stat","v3_atlas_stat"];
histatlas_syst = ["v2_atlas_syst","v3_atlas_syst"];

histpPb_stat = ["v2_v0a_stat_pt","v3_v0a_stat_pt"];
histpPb_syst = ["v2_v0a_syst_pt","v3_v0a_syst_pt"];
# add labels for each pad
#plables = [ "Leading Particle $|\\eta|<0.9$","Jet, anti-$k_\mathrm{T}$ $R=0.4$ $|\\eta|<0.4$" ];
plables = [ "$n = 2$", "$n = 3$" ];
# model names : for histonames in ROOT file
modelStr = ["EPOS LHC"]; # "PYTHIA8 Monash2013"];#for legend
#modelStr = ["PYTHIA8 String Shoving $g$ = 3","EPOS LHC"];
#xtitle = ["$p^{\\mathrm{LP}}_\\mathrm{T,min}\\,(\\mathrm{GeV}/c)$","$p^{\\mathrm{Jet}}_\\mathrm{T,min}\\,(\\mathrm{GeV}/c)$"];
#xtitle = ["$p_\\mathrm{T,trig(assoc)}\\,(\\mathrm{GeV}/c)$","$p_\\mathrm{T,trig(assoc)}\\,(\\mathrm{GeV}/c)$"];
xtitle = ["$p_\\mathrm{T,trig}\\,(\\mathrm{GeV}/c)$","$p_\\mathrm{T,trig}\\,(\\mathrm{GeV}/c)$"];
ytitle = ["$v_{n}$"];

# Following two must be added
toptitle = "pp $\\sqrt{s}$ = 13 TeV \n $0--0.1\%$"; # need to add on the top
dataDetail = ["$1.6<|\\Delta\\eta|<1.8$ \n 1 $ < p_\\mathrm{T,assoc} < 4 \\,\\mathrm{GeV}/c $"];

plot = JPyPlotRatio.JPyPlotRatio(panels=(nrow,ncol),
	panelsize=(4,5),
	rowBounds=ylimits,  # for nrow
	colBounds=xlimits,  # for ncol
	panelLabel=plables,  # nrowxncol
	ratioBounds=rlimits,# for nrow
#	ratioSystPlot=True,
	disableRatio=[0],
	panelLabelLoc=(0.71,0.89),panelLabelSize=13,panelLabelAlign="left",
	axisLabelSize=13,tickLabelSize=13,legendPanel=0,
	legendLoc=(0.47,0.20),legendSize=12,xlabel={0:xtitle[0],1:xtitle[1]},ylabel=ytitle[0]);

plot.EnableLatex(True); # for publication need fonts via texlive

plotMatrix = np.empty((nrow,ncol),dtype=int);

#plot.GetAxes(0).set_xticks([1.5,2.5,3.5]);

for i in range(0,nrow):
	for j in range(0,ncol):
		index = i*ncol+j; # for each panel
		#plot.GetAxes(index).set_xticks([1.5,2.5,3.5]);
		plot.GetAxes(index).xaxis.set_ticks_position('both');
		plot.GetAxes(index).yaxis.set_ticks_position('both');
		gr = f.Get("{}".format(histnames[j]));
		datapp = plot.Add(index,gr,**dataTypePlotParams[0],label="pp $\\sqrt{s} = 13$ TeV 0--0.1\%");
		    #, $1.6<|\\Delta\\eta|<1.8$ \n 1 $ < p_\\mathrm{T,assoc} < 4 \\,\\mathrm{GeV}/c $"
		grsyst = f.Get("{}".format(histnamesSyst[j]));
		_,_,_,syst = JPyPlotRatio.TGraphErrorsToNumpy(ROOT.TGraphErrors(grsyst));
		plot.AddSyst(datapp,syst);
		#Loading ATLAS
		grATLAS = f.Get("{}".format(histatlas_stat[j]));
		#data = plot.Add(index,grATLAS,**dataTypePlotParams[1],label="ATLAS, $2.0<|\\Delta\\eta|<5.0$ \n 0.5 $ < p_\\mathrm{T,assoc} < 5 \\,\\mathrm{GeV}/c $");
		#grsystATLAS = f.Get("{}".format(histatlas_syst[j]));
		#_,_,_,syst = JPyPlotRatio.TGraphErrorsToNumpy(ROOT.TGraphErrors(grsystATLAS));
		#plot.AddSyst(data,syst);
		#End of ATLAS
		#Loading pPb
		if(addpPb):
			grpPb = fpPb.Get("{}".format(histpPb_stat[j]));
			grpPb.Print();
			dataPb = plot.Add(index,grpPb,**dataTypePlotParams[2],label="p--Pb $\\sqrt{s_{\\rm NN}} = 5.02$ TeV 0--20\%");
			grsystpPb = fpPb.Get("{}".format(histpPb_syst[j]));
			_,_,_,syst = JPyPlotRatio.TGraphErrorsToNumpy(ROOT.TGraphErrors(grsystpPb));
			plot.AddSyst(dataPb,syst);
			plot.Ratio(dataPb,datapp);
		#End of pPb
		#gr = f.Get("{}".format(histnames[j]));
		#gr.Print();
		#data = plot.Add(index,gr,**dataTypePlotParams[0],label="ALICE, $1.6<|\\Delta\\eta|<1.8$ \n 1 $ < p_\\mathrm{T,assoc} < 4 \\,\\mathrm{GeV}/c $");
		#grsyst = f.Get("{}".format(histnamesSyst[j]));
		#_,_,_,syst = JPyPlotRatio.TGraphErrorsToNumpy(ROOT.TGraphErrors(grsyst));
		#plot.AddSyst(data,syst);

f.Close();

plot.GetPlot().text(0.15,0.80,"ALICE",fontsize=13);
#plot.GetPlot().text(0.15,0.77,"ALICE",fontsize=12);
plot.GetPlot().text(0.54,0.65,dataDetail[0],fontsize=13);
#plot.GetPlot().text(0.16,0.17,dataDetail[1],fontsize=10);

# this is need because of the input histo label setting..
#plot.GetAxes(0).set(xticks=[0.5,1.5,2.5,3.5,4.5,5.5,6.5], xticklabels=["0","3","5","7","9","13","20"]);
#plot.GetAxes(1).set(xticks=[0.5,1.5,2.5,3.5,4.5,5.5], xticklabels=["0","10","20","30","40","50"]);
plot.Plot();
if(addpPb):
	plot.Save("figures/FIG4_vn_pppPb.pdf");
	plot.Save("figs/FIG4_vn_pppPb.png");
else:
    plot.Save("figures/FIG4_vn.pdf")
    plot.Save("figures/FIG4_vn.png");

plot.Show();