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

class Solution:
  def deleteNode(self, head: ListNode, val: int) -> ListNode:
    if head.val == val:
      return head.next
    cur = head
    while cur and cur.next:
      if cur.next.val == val:
        cur.next = cur.next.next
        break
      cur = cur.next
    return head


# - TickTock (2022) - interview python code example by BeardyMike, designed to be easily expandable
import time
number = 1
while number < 101:
    output =""                                  # this makes sure output is empty before we begin
    if number % 3 == 0:                         # is number a tick?
        output +="tick"                         # add tick to the output variable
    if number % 5 == 0:                         # is number a tock?
        output +="tock"                         # add tock to the output variable
    if number == 69:                            # is number 69?
        output =""                              # empties output
        output +="Nice!"                        # add Nice! to the output variable
    if output !="":                             # check if output is empty
        output += "(" + str(number) + ")"       # add the actual value of number in (brackets)
    if output =="":                             # if output is empty, then output equals number
        output = number
    print(output)                               # print the result for all to see
    number += 1                                 # increment i by 1
    time.sleep(0.01)                            # add a teeny tiny pause so you can see the magic happening


#!/usr/bin/env python

# call this file from within the FreeCAD git repo
# this script creates a file with the important version information
import os
import sys
import subprocess

sys.path.append(f"{os.getcwd()}/src/Tools")
import SubWCRev

gitInfo = SubWCRev.GitControl()
gitInfo.extractInfo("","")
i = open("src/Build/Version.h.cmake")
content = []
for line in i.readlines():
    line = line.replace("${PACKAGE_WCREF}",gitInfo.rev)
    line = line.replace("${PACKAGE_WCDATE}",gitInfo.date)
    line = line.replace("${PACKAGE_WCURL}",gitInfo.url)
    content.append(line)

with open("src/Build/Version.h.cmake", "w") as o:
	content.append('// Git relevant stuff\n')
	content.append('#define FCRepositoryHash   "%s"\n' % (gitInfo.hash))
	content.append('#define FCRepositoryBranch "%s"\n' % (gitInfo.branch))
	o.writelines(content)

with open(os.sys.argv[1], "w") as f:
	f.write(f"rev_number: {gitInfo.rev}\n")
	f.write(f"branch_name: {gitInfo.branch}\n")
	f.write(f"commit_date: {gitInfo.date}\n")
	f.write(f"commit_hash: {gitInfo.hash}\n")
	f.write(f"remote_url: {gitInfo.url}\n")

p = subprocess.Popen(["git", "-c", "user.name='ghaction'", "-c", "user.email='gh@action.org'",
		       "commit", "-a", "-m", "add git information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)

out, err = p.communicate()

print(out.decode())
print(err.decode())


# 16. Split a file into N pieces
# Receive a natural number $N$ from a command-line argument, and split the input file into $N$ pieces at line boundaries. Confirm the result by using split command.
from sys import argv

if (len(argv) > 1 and argv[1].isdigit()):
    n = int(argv[1])
    with open('popular-names.txt') as f:
        lines = f.readlines()
        lineNum = len(lines)
        outLinesNum = lineNum / n
        i = 0
        for j in range(n):
            with open(f'split{j}.txt', 'w') as wf:
                for k in range(int(outLinesNum)):
                    wf.write(lines[i])
                    i = i + 1
                wf.close()
        f.close()

else:
    print("type appropriate number")


from print_center import *
import curses

def print_best_score(stdscr):
    while 1:
        stdscr.clear()
        h, w = stdscr.getmaxyx()
        print_center(stdscr, "Your best score is")
        f = open("best_score.txt", "r")
        score = f.read()
        stdscr.addstr(h//2 + 2, w//2 - len(score)//2, score)
        f.close()
        stdscr.refresh()
        t = stdscr.getch()
        if t == 27:
            break


from glob import glob
import os
from setuptools import setup

package_name = 'cpmr_ch2'

setup(
    name=package_name,
    version='3.1.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages', ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.py')),
        (os.path.join('share', package_name), glob('maps/*.json')),
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='jenkin',
    maintainer_email='jenkin@yorku.ca',
    description='Code associated with Chapter 2 of CPMR 3rd Edition',
    license='MIT License',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'drive_to_goal = cpmr_ch2.drive_to_goal:main',
            'add_obstacle = cpmr_ch2.add_obstacle:main',
            'build_map = cpmr_ch2.build_map:main',
            'destroy_map = cpmr_ch2.destroy_map:main',
        ],
    },
)


"""
Created on Tuesday 20 February 2018
Last update: Sunday 11 March 2018

@author: Michiel Stock
michielfmstock@gmail.com

Solution for the lecture optimal transport
"""

from itertools import permutations
import numpy as np
from sklearn.metrics.pairwise import pairwise_distances
from random import shuffle

blue = '#264653'
green = '#2a9d8f'
yellow = '#e9c46a'
orange = '#f4a261'
red = '#e76f51'
black = '#50514F'

def shuffle_rows(X):
    """
    Randomly shuffles rows of a numpy matrix
    """
    n, _ = X.shape
    indices = list(range(n))
    shuffle(indices)
    X[:] = X[indices]

def monge_brute_force(C):
    """
    Solves the Monge assignment problem using
    brute force.

    Inputs:
        - C: cost matrix (square, size n x n)

    Outputs:
        - best_perm: optimal assigments (list of n indices matching the rows
                to the columns)
        - best_cost: optimal cost corresponding to the best permutation

    DO NOT USE FOR PROBLEMS OF A SIZE LARGER THAN 12!!!
    """
    n, m = C.shape
    assert n==m  # C should be square
    best_perm = None
    best_cost = np.inf
    for perm in permutations(range(n)):
        cost = 0
        for i, p_i in enumerate(perm):
            cost += C[i, p_i]
        if cost < best_cost:
            best_cost = cost
            best_perm = perm
    return best_perm, best_cost

def compute_optimal_transport(C, a, b, lam, epsilon=1e-8,
                verbose=False, return_iterations=False):
    """
    Computes the optimal transport matrix and Slinkhorn distance using the
    Sinkhorn-Knopp algorithm

    Inputs:
        - C : cost matrix (n x m)
        - a : vector of marginals (n, )
        - b : vector of marginals (m, )
        - lam : strength of the entropic regularization
        - epsilon : convergence parameter
        - verbose : report number of steps while running
        - return_iterations : report number of iterations till convergence,
                default False

    Output:
        - P : optimal transport matrix (n x m)
        - dist : Sinkhorn distance
        - n_iterations : number of iterations, if `return_iterations` is set to
                        True
    """
    n, m = C.shape
    P = np.exp(- lam * C)
    iteration = 0
    while True:
        iteration += 1
        u = P.sum(1)  # marginals of rows
        max_deviation = np.max(np.abs(u - a))
        if verbose: print('Iteration {}: max deviation={}'.format(
                            iteration, max_deviation
                        ))
        if max_deviation < epsilon:
            break
        # scale rows
        P *= (a / u).reshape((-1, 1))
        # scale columns
        P *= (b / P.sum(0)).reshape((1, -1))
    if return_iterations:
        return P, np.sum(P * C), iteration
    else:
        return P, np.sum(P * C)

def barrycenters_entropic(B, C, weights, lam=1, L=100):
    """
    Finds a barrycenter of Sinkhorn distances for a set of
    proabiltiy vectors (of same dimension) with a cost matrix
    and a set of weigths.

    Inputs:
        - B: matrix containing the probability vectors (n x s), positive
            and normalized
        - C: cost matrix (n x n), same everywhere
        - weigths: s weights for the relative importance
        - lam: strength of the entropic regularization
        - L: number of Sinkhorn updates

    Output:
        - a: barrycenter (probability vector)
    """
    n, s = B.shape
    K = np.exp(- lam * C)
    U = np.ones_like(B) / n
    V = np.zeros_like(B)
    a = np.zeros((n, 1))
    for step in range(L):
        # update V
        V[:] = B / (K.T  @ U)
        # update a
        a[:] = np.exp(np.sum(np.log(K @ V) * weights.reshape((1, s)), axis=1, keepdims=True))
        U[:] = a / (K @ V)
    return a

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    B = np.zeros((10, 2))
    B[:5,0] = 0.2
    B[5:,1] = 0.2

    C = pairwise_distances(np.arange(10).reshape((-1,1)),
                metric='sqeuclidean')

    A = np.zeros((10, 11))
    for i in range(11):
        A[:,i] = barrycenters_entropic(B, C, np.array((1-i/10, 0+i/10)))[:,0]

    plt.imshow(A, interpolation='nearest')
    plt.savefig('Figures/simpleinterpol.png')

    # images
    dim = 50
    B = np.zeros((dim**2, 4))

    # square
    square = np.zeros((dim, dim))
    square[dim//4:-dim//4,:][:,dim//4:-dim//4] = 1
    B[:,0] = square.flatten()

    # circle
    circle = np.array([[(i-dim/2)**2+(j-dim/2)**2 < dim**2 / 4**2 for i in range(dim)]
        for j in range(dim)], dtype=float)
    circle -= np.array([[(i-dim/2)**2+(j-dim/2)**2 < dim**2 / 6**2 for i in range(dim)]
        for j in range(dim)], dtype=float)
    B[:,1] = circle.flatten()

    # diamond
    diamond = np.array([[np.abs(i-dim/2)+np.abs(j-dim/2) < dim/4 for i in range(dim)]
        for j in range(dim)], dtype=float)
    B[:,2] = diamond.flatten()

    """
    # cross
    cross = np.zeros((dim, dim))
    cross[dim//3:-dim//3,:] = 1
    cross[:, dim//3:-dim//3] = 1
    B[:,3] = cross.flatten()
    """

    # two blobs

    two_blobs = np.zeros((dim, dim))
    two_blobs[:] += np.array([[(i-dim/4)**2+(j-dim/4)**2 < (dim/10)**2 for i in range(dim)]
        for j in range(dim)], dtype=float)
    two_blobs[:] += np.array([[(i-dim/4*3)**2+(j-dim/4*3)**2 < (dim/10)**2 for i in range(dim)]
        for j in range(dim)], dtype=float)
    B[:,3] = two_blobs.flatten()

    B /= B.sum(0)

    C = pairwise_distances([[i/dim, j/dim] for i in range(dim)
    for j in range(dim)], metric="sqeuclidean")

    A = np.zeros((dim**2, 25))

    image_nr = 0
    for di in np.linspace(0, 1, 5):
        for dj in np.linspace(0, 1, 5):
            weights = np.array([
                    (1-di) * (1-dj),
                    di * (1-dj),
                    (1-di) * dj,
                    di * dj
            ])
            A[:,image_nr] = barrycenters_entropic(B, C, weights,
                            lam=500, L=50)[:,0]
            image_nr += 1

    fig, axes = plt.subplots(nrows=5, ncols=5)

    for i in range(5):
        for j in range(5):
            ax = axes[i, j]
            ax.imshow(A[:,i+5*j].reshape((dim,dim)) > 1e-5, cmap='Greys',
                            interpolation='nearest')
            ax.set_yticks([])
            ax.set_xticks([])
    plt.savefig('Figures/barrycenters.png')


import argparse
import time
from labeller.labeller import get_labeller
import numpy as np
from sklearn.metrics import accuracy_score
from calibrator.lm_ensemble import EnsembleCalibrator
from utils import evaluate_end_model, save_results, get_sampler, get_label_model, update_results, get_filter_probs, \
    preprocess_data, evaluate_label_quality, get_reviser, select_thresholds, estimate_cov_acc_tradeoff,\
    plot_performance, calibrate_lm_threshold

def run_trilabel(train_data, valid_data, test_data, args, seed):
    start = time.process_time()
    if args.evaluate:
        # evaluate label quality
        results = {
            "n_labeled": [],  # number of expert labeled data
            "n_sampled": [],  # number of expert labeled data + pseudo-labeled data
            "frac_labeled": [],  # fraction of expert labeled data
            "sampled_acc": [],  # accuracy of sampled data
            "label_acc": [],  # label accuracy
            "label_nll": [],  # label NLL score
            "label_brier": [],  # label brier score
            "label_coverage": [],  # covered fraction of training data
            "dp_coverage": [],  # fraction of training data covered by DP
            "al_coverage": [],  # fraction of training data covered by AL
            "al_acc": [],  # accuracy of AL model
            "test_acc": [],  # end model's test accuracy
            "test_f1": [],  # end model's test f1
            "golden_test_acc": np.nan,  # end model's test accuracy using golden labels
            "golden_test_f1": np.nan  # end model's test f1 using golden labels
        }
    else:
        results = {}

    if args.record_runtime:
        last_timestamp = start
        lm_time = 0   # time used for training label model
        al_time = 0   # time used for sampling and training AL model
        thres_time = 0  # time used for threshold selection
        perf_time = 0  # time used for estimating coverage-accuracy tradeoff

    train_act_probs, train_act_indices = None, None
    M = train_data.n_lf
    covered_train_data = train_data.get_covered_subset()
    if args.calibration is None:
        label_model = get_label_model(args.label_model, seed=seed)
        label_model.fit(dataset_train=covered_train_data,
                        dataset_valid=valid_data)
    elif args.calibration == "EN":
        label_model = EnsembleCalibrator(train_data, valid_data, args.label_model,
                                         ensemble_size=10, feature_size=M, bootstrap=False, seed=seed)
    elif args.calibration == "EN+FS":
        feature_size = np.floor(M * 0.8).astype(int)
        label_model = EnsembleCalibrator(train_data, valid_data, args.label_model,
                                         ensemble_size=10, feature_size=feature_size,bootstrap=False, seed=seed)
    else:
        feature_size = np.floor(M * 0.8).astype(int)
        label_model = EnsembleCalibrator(train_data, valid_data, args.label_model,
                                         ensemble_size=10, feature_size=feature_size, bootstrap=True, seed=seed)

    if args.record_runtime:
        cur_timestamp = time.process_time()
        lm_time += cur_timestamp - last_timestamp
        last_timestamp = cur_timestamp

    if args.evaluate:
        lm_train_probs = label_model.predict_proba(covered_train_data)
        lm_train_preds = np.argmax(lm_train_probs, axis=1)
        if args.use_soft_labels:
            pred_train_labels = lm_train_probs
        else:
            pred_train_labels = lm_train_preds
        label_acc, label_nll, label_brier = evaluate_label_quality(covered_train_data.labels, lm_train_probs)
        label_coverage = len(covered_train_data) / len(train_data)
        perf, _ = evaluate_end_model(covered_train_data, pred_train_labels, valid_data, test_data, args, seed)
        update_results(results, n_labeled=0, frac_labeled=0.0,n_sampled=0, sampled_acc=np.nan,
                       label_acc=label_acc, label_nll=label_nll, label_brier=label_brier,
                       label_coverage=label_coverage,dp_coverage=label_coverage, al_coverage=0.0,
                       al_acc=np.nan, test_acc=perf["test_acc"], test_f1=perf["test_f1"])
        golden_perf, _ = evaluate_end_model(train_data, train_data.labels, valid_data, test_data, args, seed)
        results["golden_test_acc"] = golden_perf["test_acc"]
        results["golden_test_f1"] = golden_perf["test_f1"]

    labeller = get_labeller(args.labeller)
    encoder = None
    reviser = get_reviser(args.revision_model, train_data, valid_data, encoder, args.device, seed)

    if args.sampler in ["tri-pl", "tri-random", "tri-pl+random"]:
        lm_threshold = calibrate_lm_threshold(valid_data, label_model)
        valid_class_dist = np.bincount(valid_data.labels) / len(train_data)
        init_method = args.sampler[4:]
        sampler = get_sampler(args.sampler, train_data, labeller, label_model=label_model, revision_model=reviser,
                              valid_class_dist=valid_class_dist, init_method=init_method, lm_threshold=lm_threshold)
    else:
        sampler = get_sampler(args.sampler, train_data, labeller, label_model=label_model, revision_model=reviser)
    n_labeled = 0
    while n_labeled < args.sample_budget:
        n_to_sample = min(args.sample_budget - sampler.get_n_sampled(), args.sample_per_iter)
        sampler.sample_distinct(n_to_sample)
        indices, labels = sampler.get_sampled_points()
        n_labeled = sampler.get_n_sampled()
        frac_labeled = n_labeled / len(train_data)
        reviser.train_revision_model(indices, labels)
        sampler.update_stats()
        if args.record_runtime:
            cur_timestamp = time.process_time()
            al_time += cur_timestamp - last_timestamp
            last_timestamp = cur_timestamp

        cov_acc_dict = estimate_cov_acc_tradeoff(train_data, valid_data, label_model, reviser)
        candidate_theta_list = []
        cov_list = []
        acc_list = []
        epsilon_list = []
        for i in range(len(cov_acc_dict["lm_theta"])):
            for j in range(len(cov_acc_dict["rm_theta"])):

                acc = cov_acc_dict["acc"][i,j]
                epsilon = cov_acc_dict["epsilon"][i,j]
                cov = cov_acc_dict["cov"][i,j]
                if args.desired_label_acc is not None and acc < args.desired_label_acc:
                    continue
                if args.desired_label_cov is not None and cov < args.desired_label_cov:
                    continue

                idx = 0
                while idx < len(cov_list) and cov_list[idx] <= cov:
                    idx += 1

                if idx < len(cov_list) and acc_list[idx] >= acc:
                    # there exist a combination that have higher/same accuracy and higher coverage
                    continue

                if idx > 0 and cov_list[idx-1] == cov and acc_list[idx-1] >= acc:
                    # there exist a combination that have higher/same accuracy and same coverage
                    continue

                cov_list.insert(idx, cov)
                acc_list.insert(idx, acc)
                epsilon_list.insert(idx, epsilon)
                candidate_theta_list.insert(idx, (cov_acc_dict["lm_theta"][i], cov_acc_dict["rm_theta"][j]))
                while idx > 0 and acc_list[idx-1] <= acc:
                    cov_list.pop(idx-1)
                    acc_list.pop(idx-1)
                    candidate_theta_list.pop(idx-1)
                    idx -= 1

        if args.record_runtime:
            cur_timestamp = time.process_time()
            perf_time += cur_timestamp - last_timestamp
            last_timestamp = cur_timestamp

        if len(candidate_theta_list) == 0:
            # No configuration can satisfy user provided constraints at current label budget
            print(f"Constraints cannot be satisfied at label budget {n_labeled}. Increasing sample size.")
            if args.evaluate:
                indices, labels = sampler.get_sampled_points()
                sampled_acc = accuracy_score(np.array(train_data.labels)[indices], labels)
                update_results(results, n_labeled=n_labeled, frac_labeled=frac_labeled, n_sampled=len(indices),
                               sampled_acc=sampled_acc, label_acc=np.nan, label_nll=np.nan, label_brier=np.nan,
                               label_coverage=np.nan, dp_coverage=np.nan, al_coverage=np.nan,
                               al_acc=np.nan, test_acc=np.nan, test_f1=np.nan)
            continue

        if args.optimize_target == "f1":
            selected_theta, valid_f1, test_f1 = select_thresholds(train_data, valid_data, test_data,
                                                                   candidate_theta_list,
                                                                   label_model, reviser, args, seed)

            if args.plot_performance:
                theta_indices = np.nonzero(valid_f1 > 0)[0]
                evaluated_valid_f1 = valid_f1[theta_indices]
                evaluated_test_f1 = test_f1[theta_indices]
                evaluated_cov = np.array(cov_list)[theta_indices]
                evaluated_acc = np.array(acc_list)[theta_indices]
                evaluated_epsilon = np.array(epsilon_list)[theta_indices]
                gt_acc = np.zeros_like(evaluated_acc)
                for i in range(len(theta_indices)):
                    lm_theta, rm_theta = candidate_theta_list[theta_indices[i]]
                    train_probs, train_act_indices = get_filter_probs(train_data, label_model, reviser, lm_theta, rm_theta)
                    train_preds = np.argmax(train_probs, axis=1)
                    train_labels = np.array(train_data.labels)[train_act_indices]
                    gt_acc[i] = accuracy_score(train_labels, train_preds)

                plot_performance(args.dataset, evaluated_cov, evaluated_acc, evaluated_epsilon, gt_acc,
                                 evaluated_valid_f1, evaluated_test_f1, n_labeled, args.output_path)
        elif args.optimize_target == "accuracy":
            selected_theta = candidate_theta_list[0]  # select the one with highest label accuracy
        else:
            selected_theta = candidate_theta_list[-1]  # select the one with highest label coverage

        train_act_probs, train_act_indices = get_filter_probs(train_data, label_model, reviser,
                                                              selected_theta[0], selected_theta[1])
        if args.record_runtime:
            cur_timestamp = time.process_time()
            thres_time += cur_timestamp - last_timestamp
            last_timestamp = cur_timestamp

        if args.evaluate:
            indices, labels = sampler.get_sampled_points()
            sampled_acc = accuracy_score(np.array(train_data.labels)[indices], labels)
            lm_train_conf = np.max(lm_train_probs, axis=1)
            dp_coverage = np.sum(lm_train_conf >= selected_theta[0]) / len(train_data)
            al_coverage = len(train_act_indices) / len(train_data) - dp_coverage
            al_preds = reviser.predict(train_data)
            al_acc = accuracy_score(train_data.labels, al_preds)
            pred_train_data = train_data.create_subset(train_act_indices)
            label_acc, label_nll, label_brier = evaluate_label_quality(pred_train_data.labels, train_act_probs)
            label_coverage = len(pred_train_data) / len(train_data)
            if args.use_soft_labels:
                pred_train_labels = train_act_probs
            else:
                pred_train_labels = train_act_probs.argmax(axis=1)
            perf, _ = evaluate_end_model(pred_train_data, pred_train_labels, valid_data, test_data, args, seed)
            update_results(results, n_labeled=n_labeled, frac_labeled=frac_labeled,n_sampled=len(indices),
                           sampled_acc=sampled_acc, label_acc=label_acc, label_nll=label_nll, label_brier=label_brier,
                           label_coverage=label_coverage, dp_coverage=dp_coverage, al_coverage=al_coverage,
                           al_acc=al_acc, test_acc=perf["test_acc"], test_f1=perf["test_f1"])

    end = time.process_time()
    results["time"] = end - start
    if args.record_runtime:
        results["lm_time"] = lm_time
        results["al_time"] = al_time
        results["perf_time"] = perf_time
        results["thres_time"] = thres_time

    return train_act_probs, train_act_indices, results

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    # device info
    parser.add_argument("--device", type=str, default="cuda:0")
    # dataset
    parser.add_argument("--dataset", type=str, default="youtube")
    parser.add_argument("--dataset_path", type=str, default="../datasets/")
    parser.add_argument("--extract_fn", type=str, default="bert")  # method used to extract features
    parser.add_argument("--max_dim", type=int, default=300)  # dimension reduction using PCA
    # User provided constraints
    parser.add_argument("--sample_budget", type=float, default=300)  # Total sample budget
    parser.add_argument("--desired_label_acc", type=float, default=None)  # Desired Label Accuracy
    parser.add_argument("--desired_label_cov", type=float, default=None)  # Desired Label Coverage
    parser.add_argument("--optimize_target", type=str, choices=["accuracy", "coverage", "f1"], default="f1")
    # active learning sample strategy and model
    parser.add_argument("--sampler", type=str, default="uncertain-rm")
    parser.add_argument("--sample_per_iter", type=float, default=100)  # Sample per iteration (batch size)
    parser.add_argument("--revision_model", type=str, default="mlp")
    # label model and end models
    parser.add_argument("--label_model", type=str, default="metal")
    parser.add_argument("--end_model", type=str, default="mlp")
    parser.add_argument("--em_epochs", type=int, default=100)
    parser.add_argument("--use_soft_labels", action="store_true")
    # other settings
    parser.add_argument("--calibration", type=str, choices=["EN", "EN+FS", "EN+FS+BS"], default=None)
    parser.add_argument("--evaluate", action="store_true")  # evaluate label quality and performance using golden labels
    parser.add_argument("--record_runtime", action="store_true")  # record runtime of every component
    parser.add_argument("--plot_performance", action="store_true")  # draw performance plot
    parser.add_argument("--theta_explore_strategy", type=str, default="step")
    parser.add_argument("--theta_explore_num", type=int, default=10)  # maximum number of evaluated theta thresholds

    parser.add_argument("--labeller", type=str, default="oracle")
    parser.add_argument("--metric", type=str, default="f1_macro")
    parser.add_argument("--repeats", type=int, default=10)
    parser.add_argument("--seed", type=int, default=42)
    parser.add_argument("--output_path", type=str, default="output/")
    parser.add_argument("--verbose", action="store_true")
    parser.add_argument("--tag", type=str, default="test")
    args = parser.parse_args()
    if args.record_runtime:
        args.evaluate = False  # No evaluation of label quality when recording pipeline runtime

    train_data, valid_data, test_data = preprocess_data(args)

    if args.sample_budget <= 1:
        args.sample_budget = np.ceil(args.sample_budget * len(train_data)).astype(int)
        args.sample_per_iter = np.ceil(args.sample_per_iter * len(train_data)).astype(int)
    else:
        args.sample_budget = int(args.sample_budget)
        args.sample_per_iter = int(args.sample_per_iter)

    np.random.seed(args.seed)
    run_seeds = np.random.randint(1, 100000, args.repeats)
    results_list = []
    for i in range(args.repeats):
        print(f"Start run {i}")
        _, _, results = run_trilabel(train_data, valid_data, test_data, args, seed=run_seeds[i])
        results_list.append(results)

    id_tag = f"trilabel_{args.label_model}_{args.end_model}_{args.sampler}_{args.tag}"
    save_results(results_list, args.output_path, args.dataset, f"{id_tag}.json")


import logging
from unittest import TestCase

from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Q
from rest_framework.authentication import TokenAuthentication
from rest_framework.parsers import JSONParser
from rest_framework.views import APIView

from api_test.common import code
from api_test.common.api_response import JsonResponse
from api_test.common.common import get_availability_project, record_dynamic, objects_paginator
from api_test.common.config_http import test_api
from api_test.common.parameter_check import project_status_check, parameter_ids_check
from api_test.models import Project, AutomationTestCase, TestCaseGroup, GlobalHost, AutomationCaseApi, \
    AutomationTestResult
from api_test.serializer import TestCaseDeserializer, TestCaseSerializer, AutomationTestResultSerializer

def all_parameter_check(data):
    try:
        if not data['project_id'] or not data['case_name'] or not data['test_case_group_id']:
            return JsonResponse(code=code.CODE_PARAMETER_ERROR)
        if not isinstance(data['project_id'],int) or not isinstance(data['test_case_group_id'],int):
            return JsonResponse(code=code.CODE_PARAMETER_ERROR)
        same_name_case=AutomationTestCase.objects.filter(case_name=data['case_name'],project=data['project_id'])
        if same_name_case:
            return JsonResponse(code=code.CODE_EXIST_SAME_NAME,msg='存在同名用例名称')
    except KeyError:
        return JsonResponse(code=code.CODE_KEY_ERROR)

class AddCase(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def post(self,request):
        """
        添加测试用例
        :param request:
        :return:
        """
        data=JSONParser().parse(request)
        project=get_availability_project(data,request.user)
        result=all_parameter_check(data) if isinstance(project,Project) else project
        if result:
            return result
        with transaction.atomic():
            try:
                serializer = TestCaseDeserializer(data=data)
                if serializer.is_valid():
                    try:
                        group = TestCaseGroup.objects.get(id=data['test_case_group_id'], project=project.id)
                        serializer.save(project=project, test_case_group=group, user=User.objects.get(id=request.user.pk))
                    except KeyError:
                        serializer.save(project=project,user=User.objects.get(id=request.user.pk))
                    record_dynamic(project=project, _type='添加', operation_object='测试用例', user=request.user.pk,
                           data='新增测试用例 %s' % data['case_name'])
                    return JsonResponse(data={'case_id':serializer.data.get('id')},code=code.CODE_SUCCESS)
                return JsonResponse(code=code.CODE_Failed)
            except Exception:
                return JsonResponse(code=code.CODE_Failed)

class CaseList(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def get(self,request):
        """
        获取用例列表
        :param request:
        :return:
        """
        result=project_status_check(request)
        if result:
            return result
        project_id, group_id, name = request.GET.get('project_id'), request.GET.get(
            'test_case_group_id'), request.GET.get('name')
        if group_id:
            if not group_id.isdecimal():
                return JsonResponse(code=code.CODE_PARAMETER_ERROR)
            if name:
                objects=AutomationTestCase.objects.filter(project=project_id,case_name__contains=name,test_case_group=group_id).order_by('id')
            else:
                objects=AutomationTestCase.objects.filter(project=project_id,test_case_group=group_id).order_by('id')
        else:
            if name:
                objects = AutomationTestCase.objects.filter(project=project_id, case_name__contains=name).order_by('id')
            else:
                objects = AutomationTestCase.objects.filter(project=project_id).order_by('id')
        result = objects_paginator(request, objects)
        serialize = TestCaseSerializer(result['obm'], many=True)
        return JsonResponse(data={"data": serialize.data,
                                  "page": result['page'],
                                  "total": result['total']
                                  }, code=code.CODE_SUCCESS)

class DelCase(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def post(self,request):
        """
        删除测试用例
        :param request:
        :return:
        """
        data=JSONParser().parse(request)
        project=get_availability_project(data,request.user)
        result=parameter_ids_check(data) if isinstance(project,Project) else project
        if result:
            return result
        id_list=Q()
        for id in data['ids']:
            id_list=id_list|Q(id=id)
        case_list=AutomationTestCase.objects.filter(id_list,project=project.id)
        name_list=[]
        for i in case_list:
            name_list.append(str(i.case_name))
        with transaction.atomic():
            case_list.delete()
            record_dynamic(project=project.id,_type='删除',operation_object='测试用例',user=request.user.pk,data="删除测试用例：%s" % name_list)
            return JsonResponse(code=code.CODE_SUCCESS)

class UpdateCase(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def post(self,request):
        """
        修改用例
        :param request:
        :return:
        """
        data=JSONParser().parse(request)
        project=get_availability_project(data,request.user)
        result=all_parameter_check(data) if isinstance(project,Project) else project
        if result:
            return result
        try:
            case=AutomationTestCase.objects.get(id=data['id'],project=project.id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg='测试用例不存在')
        try:
            group=TestCaseGroup.objects.get(id=data['test_case_group_id'],project=project.id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg='用例分组不存在')
        serializer=TestCaseDeserializer(data=data)
        if serializer.is_valid():
            serializer.update(instance=case,validated_data=data)
            record_dynamic(project=project.id,_type='更新',operation_object='测试用例',user=request.user.pk,data="修改测试用例：%s" % data['case_name'])
            return JsonResponse(code=code.CODE_SUCCESS)
        return JsonResponse(code=code.CODE_Failed)

class UpdateCaseGroup(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def post(self,request):
        """
        更新用例的分组
        :param request:
        :return:
        """
        data=JSONParser().parse(request)
        project=get_availability_project(data,request.user)
        result=parameter_ids_check(data) if isinstance(project,Project) else project
        if result:
            return result
        try:
            group=TestCaseGroup.objects.get(id=data['test_case_group_id'],project=project.id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg='测试用例分组不存在')

        id_list=Q()
        for i in data['ids']:
            id_list=id_list|Q(id=i)
        case_list=AutomationTestCase.objects.filter(id_list,project=project.id)

        case_name=[]
        for j in case_list:
            case_name.append(j.case_name)

        with transaction.atomic():
            case_list.update(test_case_group=group)
            record_dynamic(project=project, _type='修改', operation_object='测试用例', user=request.user.pk,
                           data='修改测试用例分组 %s' % case_name)
            return JsonResponse(code=code.CODE_SUCCESS)

class Test(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def parameter_check(self,data):
        """
        校验参数
        :param data:
        :return:
        """
        try:
            if not data['project_id'] or not data['case_id'] or not data['id'] or not data['host_id']:
                return JsonResponse(code=code.CODE_PARAMETER_ERROR)
        except KeyError:
            return JsonResponse(code=code.CODE_PARAMETER_ERROR)

    def post(self,request):
        """
        执行
        :param request:
        :return:0
        """
        data=JSONParser().parse(request)
        project=get_availability_project(data,request.user)
        result=self.parameter_check(data) if isinstance(project,Project) else project
        if result:
            return result
        try:
            case=AutomationTestCase.objects.get(id=data['case_id'],project=project.id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg='测试用例不存在')
        try:
            host=GlobalHost.objects.get(id=data['host_id'],project=project.pk)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg="host不存在")
        try:
            case_api=AutomationCaseApi.objects.get(id=data['id'],automation_test_case=case.id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg="接口不存在")
        AutomationTestResult.objects.filter(automation_case_api=case_api.id).delete()
        try:
            result=test_api(host_id=host.id,case_id=case.id,_id=case_api.id,project_id=project.id)
        except Exception as e:
            logging.exception(e)
            return JsonResponse(code=code.CODE_Failed)
        record_dynamic(project=project, _type='测试', operation_object='用例接口', user=request.user.pk,
                       data="测试用例 %s 接口\"%s\""%(case.case_name,case_api.name))
        return JsonResponse(data={
            "result":result
        },code=code.CODE_SUCCESS)

class LookResult(APIView):
    authentication_classes = (TokenAuthentication,)
    permission_classes = ()

    def get(self,request):
        """
        获取测试结果
        :param request:
        :return:
        """
        project_id,case_id,api_id=request.GET.get('project_id'),request.GET.get('case_id'),request.GET.get('api_id')
        result=project_status_check(request)
        if result:
            return result
        try:
            AutomationTestCase.objects.get(id=case_id,project_id=project_id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg="用例不存在")
        try:
            AutomationCaseApi.objects.get(id=api_id,automation_test_case=case_id)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_OBJECT_NOT_EXIST,msg="接口不存在")
        try:
            data=AutomationTestResult.objects.get(automation_case_api=api_id)
            serializer=AutomationTestResultSerializer(data)
            return JsonResponse(data=serializer.data,code=code.CODE_SUCCESS)
        except ObjectDoesNotExist:
            return JsonResponse(code=code.CODE_Failed)


class Node(object):
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

    def get_data(self):
        return self.data

    def set_data(self, new_data):
        self.data = new_data

    def get_next(self):
        return self.next

    def set_next(self, next_node):
        self.next = next_node

class Linked_List(object):
    def __init__(self, head=None):
        self.head = head
        self.size = 0

    # append at the end
    def append(self, node):
        cur = self.head
        while cur.next != None:
            cur = cur.next
        cur.next = node
        self.size += 1

    def size(self):
        return self.size

    def search(self, value):
        cur = self.head
        found = False
        while not found:
            if cur.data == value:
                found = True
                return cur
            elif cur.next:
                cur = cur.next
            else:
                return ValueError("Value not found")

    def delete(self, value):
        cur = self.head
        if cur.data == value:
            self.head = cur.next
        else:
            while cur.data != value:
                cur = cur.next
            if cur.next:
                cur.data = cur.next.data
                cur.next = cur.next.next
            else:
                cur.next = None
                cur.data = None

    def traverse(self):
        cur = self.head
        arr = [cur.data]
        while cur.next:
            cur = cur.next
            arr.append(cur.data)
        return arr

node1 = Node(4)
node2 = Node(2)
node3 = Node(6)
node4 = Node(10)
node5 = Node(5)
ll = Linked_List(node1)
print(f"Current linked list is {ll.traverse()}")
ll.append(node2)
ll.append(node3)
print(f"After adding some nodes, we get {ll.traverse()}")
print(f"Now the size of the linked list is {ll.size}")
print(f"If we search for 10, we should see that it is not in the list: {ll.search(10)}")
ll.append(node4)
ll.append(node5)
print(f"However if we append some nodes, and one of them has value 10 like: {ll.traverse()}")
print(f"We should now get a response for searching for 10: {ll.search(10)}")
ll.delete(2)
print(f"After deleting node 2, we get {ll.traverse()}")


import math
if __name__ == "__main__":
    class Punto:
        def __init__(self,x:int,y:int):
            self.x:int = x
            self.y:int = y

        def mostrar_coordenadas(self):
            return print(f"{self.x},{self.y}")

        def Mover_punto(self,dx:int,dy:int):
            self.x:int = self.x + dx
            self.y: int = self.y + dy

        def distacia_punto(self,punto_diferente):
            termino_1 = punto_diferente.x - self.x
            termino_2 = punto_diferente.y - self.y
            calculo = math.sqrt(termino_1 ** 2) + (termino_2 ** 2)
            return calculo

    punto_1 = Punto(5,5)
    punto_2 = Punto(5,5)
    punto_1.mostrar_coordenadas()
    punto_1.Mover_punto(-5,4)
    punto_1.mostrar_coordenadas()

    punto_1.distacia_punto(punto_2)


import copy
from typing import Any, Dict, List

class Parameters(object):
    __slots__ = ('_parameters', )

    def __init__(self, parameters: Dict[str, Any]):
        self._parameters = parameters

    def __getattr__(self, _name: str):
        if _name in self._parameters:
            return copy.deepcopy(self._parameters[_name])

        raise AttributeError("'Parameters' object has no attribute '{}'".format(_name))

    def __getitem__(self, key: str):
        if key in self._parameters:
            return copy.deepcopy(self._parameters[key])

        raise KeyError

    def __str__(self):
        return str(self._parameters)

    def __repr__(self):
        return self.__str__()

    def __iter__(self):
        for key in self._parameters.keys():
            yield key, self._parameters[key]

    def get(self, key: str, default: Any = None):
        if key in self._parameters:
            return copy.deepcopy(self._parameters[key])

        return default

    def has(self, key: str) -> bool:
        return key in self._parameters

    def all(self) -> dict:
        return copy.deepcopy(self._parameters)

    def extend(self, additional_parameters: dict):
        self._parameters.update(additional_parameters)

    @classmethod
    def from_cli_params(cls, parameters: List[str]) -> "Parameters":
        dict_parameters = {}
        key_name = None
        value = None
        for parameter in parameters:
            if parameter.startswith("-"):
                if key_name is not None:
                    if value is None:
                        value = True
                    dict_parameters[key_name] = value
                    value = None
                key_name = parameter.lstrip("-")
            elif key_name is not None:
                print(parameter)
                if value is None:
                    value = parameter
                elif isinstance(value, list):
                    value.append(parameter)
                else:
                    value = [value, parameter]

        if key_name is not None:
            if value is None:
                value = True
            dict_parameters[key_name] = value

        return cls(dict_parameters)


class Test:

    def __init__(self, limit):
        self._limit = limit

    # Called when iteration is initialized
    def __iter__(self):
        self._x = 10
        return self

    # To move to next element
    def __next__(self):

        # Store current value of x
        x = self._x

        # Stop iteration if limit is reached
        if x > self._limit:
            raise StopIteration

        # Else increment and return old value
        self._x += 1
        return x

if __name__ == "__main__":

    # Prints number from 10 to 50
    for i in Test(50):
        print(i)

    # Prints nothing
    for i in Test(8):
        print(i)


import sys
from pathlib import Path
import click
from pptx import Presentation
import csv

def read_pptx(pptxpath, csvwriter):
    prs = Presentation(pptxpath)
    for i, slide in enumerate(prs.slides):
        for j, shape in enumerate(slide.shapes):
            if hasattr(shape, 'text'):
                csvwriter.writerow([pptxpath, i, j, '-',  shape.text])
            elif shape.has_table:
                for k, cell in enumerate(shape.table.iter_cells()):
                    csvwriter.writerow([pptxpath, i, j, k, cell.text])

@click.command()
@click.argument('src_pptx_path')
@click.argument('out_csv_file', type=click.File(mode='w', encoding='utf_8_sig'))
def extract_text_from_pptxs(src_pptx_path, out_csv_file):
    src_pptx_files = Path(src_pptx_path).glob('**\*.pptx')
    csvwriter = csv.writer(out_csv_file, lineterminator='\n')
    csvwriter.writerow(['filename', 'slide', 'shape', 'cell', 'text'])
    for src_pptx_file in src_pptx_files:
        try:
            read_pptx(src_pptx_file, csvwriter)
        except Exception as e:
            print(str(src_pptx_file) + ' cannot read : ' + str(e), file=sys.stderr)
    return

if __name__ == '__main__':
    extract_text_from_pptxs()


ans = 0
D = dict()
S = 0
for i in range(int(input())):
    a = input()
    if a not in D:
        D[a] = 1
    else:
        if S - D[a] < D[a]:
            ans+=1
        D[a]+=1
    S+=1
print(ans)


#!/usr/bin/python3
"""Base Module"""

import csv
import json
from typing import List, Dict
import turtle

class Base:
    """This module defines a base class."""
    __nb_objects = 0

    def __init__(self, id: int = None) -> None:
        """Constructor method that assigns the public instance attribute 'id'.

        Args:
            id (int): An integer value used to manage IDs in this project.

        Returns:
            None.
        """
        if id is not None:
            self.id = id
        else:
            Base.__nb_objects += 1
            self.id = Base.__nb_objects

    @staticmethod
    def to_json_string(list_dictionaries: List[Dict]) -> str:
        """Return the JSON serialization of a list of dictionaries.

        This method takes a list of dictionaries and returns its JSON representation.

        Args:
            list_dictionaries (list): A list of dictionaries to be serialized.

        Returns:
            str: JSON representation of the input list of dictionaries.
        """
        if list_dictionaries is None or list_dictionaries == []:
            return "[]"
        return json.dumps(list_dictionaries)

    @classmethod
    def save_to_file(cls, list_objs):
        """Serialize a list of objects and save it to a JSON file.

        Args:
            list_objs (list): A list of objects derived from Base class.
        """
        file_name = cls.__name__ + ".json"
        with open(file_name, "w") as jsonfile:
            if list_objs is None:
                json.dump([], jsonfile)
            else:
                list_dicts = [o.to_dictionary() for o in list_objs]
                json.dump(list_dicts, jsonfile)

    @staticmethod
    def from_json_string(json_string):
        """
        Convert a JSON string into a Python list.

        Args:
            json_string (str): The JSON string to be converted, representing a list of dictionaries.

        Returns:
            list: The Python list represented by the input JSON string. If the input string is None or empty,
                  an empty list is returned.
        """
        if json_string is None or json_string == "[]":
            return []

        try:
            return ast.literal_eval(json_string)
        except (ValueError, SyntaxError):
            return []

    @classmethod
    def create(cls, **attributes):
        """Return a class instantiated from a dictionary of attributes.

        Args:
            **attributes (dict): Key/value pairs of attributes to initialize the class.
        """
        instance = cls(1, 1) if cls.__name__ == "Rectangle" else cls(1)
        instance.update(**attributes)
        return instance

    @classmethod
    def load_from_file(cls):
        """
        Return a list of class instances created from a JSON file.

        Reads from a JSON file named '<ClassName>.json', where 'ClassName'
        is the name of the class in snake_case format.

        Returns:
            If the file does not exist - an empty list.
            Otherwise - a list of instantiated classes from the JSON data.
        """
        filename = f"{cls.__name__}.json"
        try:
            with open(filename, "r") as jsonfile:
                list_dicts = json.load(jsonfile)
                return [cls.create(**d) for d in list_dicts]
        except FileNotFoundError:
            return []

    @classmethod
    def save_to_file_csv(cls, list_objs):
        """Serialize a list of objects to a CSV file.

        Args:
            list_objs (list): A list of objects that inherit from the Base class.
            filename (str): The name of the file to write the CSV data to.
        """
        filename = f"{cls.__name__}.csv"
        with open(filename, "w", newline="") as csvfile:
            if not list_objs:
                csvfile.write("[]")
            else:
                fieldnames = ["id", "width", "height", "x", "y"] if cls.__name__ == "Rectangle" else ["id", "size", "x", "y"]
                writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
                writer.writeheader()
                for obj in list_objs:
                    writer.writerow(obj.to_dictionary())
    @classmethod
    def load_from_file_csv(cls):
        """Return a list of class objects instantiated from a CSV file.

        The function reads from '<cls.__name__>.csv' where 'cls' is the class name.

        Returns:
            If the file does not exist - an empty list.
            Otherwise - a list of class objects instantiated from the CSV file.
        """
        filename = f"{cls.__name__}.csv"
        try:
            with open(filename, "r", newline="") as csvfile:
                fieldnames = ["id", "width", "height", "x", "y"] if cls.__name__ == "Rectangle" else ["id", "size", "x", "y"]
                reader = csv.DictReader(csvfile, fieldnames=fieldnames)
                next(reader)  # Skip the header row
                list_dicts = [dict([(k, int(v)) for k, v in d.items()]) for d in reader]
                return [cls.create(**d) for d in list_dicts]
        except FileNotFoundError:
            return []


from multi_agent_Q_Learning.find_moves import find_moves_hexagons

def single_index_to_tuple(index, cols):
    """
    Turns a single index of our hexagonal tesselation (from a graph) into a tuple (i,j) pair.
    ( the harder logic version w/o the missing invisible entires in on my points_to_gps.py file
    :param index: single index
    :return: tuple
    """
    i = index // cols
    j = index % cols
    return i, j

def tuple_to_single_index(tuple, cols):
    """
    Turns a tuple (i,j) pair of our hexagonal tesselation into a single index (from a graph).
    :param tuple:
    :return: single index
    """
    return tuple[0]*cols + tuple[1]

def tuple_to_single_index_grid(tuple, cols):
    """
    Turns a tuple (i,j) pair of our GridWorld into a single index (from a graph).
    :param tuple:
    :return: single index
    """
    return tuple[0]*cols + tuple[1]

def nums_to_ops(nums, rows, cols):
    """
    Turns a path based off a single-index from something like
       1     3      5
    0     2     4
       7     9     11
    6     8    10
       12    13     14
    to a list of ordered pairs that is compatible with my path_to_coords() function
    WARNING!!! THIS IS OLD/ depreacted
    :param nums: numbers
    :param rows: rows in hexagon graph
    :param cols: columns in hexagon graph
    :return: a list of ordered pairs
    """
    op_path = []

    for num in nums:
        i = num//cols
        j = num % cols
        if i == rows - 1:  # if its in the last row
            last_reg = (rows-1)*cols - 1  # last regular ( from the last full row)
            diff = num - last_reg  # finds how much further our num is from the last number
            j = 2*diff - 1  # declares what the new column is ( it does by twos now!.\
        op_path.append([i, j])
    return op_path

# idk if these will work
def optimal_action_q(q_table, state, rows, cols):
    """optimal action for a specified q table"""
    max_value = -10000
    action = ""
    possible_actions = find_moves_hexagons(state, rows, cols)
    for a in possible_actions:
        next_value = q_table[tuple(state)][a]
        if next_value >= max_value:
            action = a
            max_value = next_value
    return action

def show_path(indv_q_table, rows, cols):
    """show path defined for this special double agent"""
    # env = HexagonEnv()
    direction_dict = {'up': 1,
                      'up_right': 2,
                      'down_right': 3,
                      'down': 4,
                      'down_left': 5,
                      'up_left': 6}

    for i in range(rows):
        row_string = ""
        for j in range(cols):
            state = (i, j)
            best_move = optimal_action_q(indv_q_table, state, rows, cols)
            if i != rows - 1 or j % 2 == 1:
                row_string = row_string + " " + str(direction_dict[best_move])
            else:
                row_string = row_string + "  "
        print(row_string)


import nltk
import pandas as pd
from nltk.corpus import wordnet
from nltk.stem import PorterStemmer, SnowballStemmer, LancasterStemmer
from nltk.stem import WordNetLemmatizer

from chester.util import ReportCollector, REPORT_PATH

def get_stemmer(name=None):
    if name == "porter":
        return PorterStemmer()
    elif name == "snowball":
        return SnowballStemmer("english")
    elif name == "lancaster":
        return LancasterStemmer()
    else:
        return PorterStemmer()

def lemmatize(text, lemmatizer=WordNetLemmatizer()):
    lemmas = []
    pos_tags = nltk.pos_tag(text)
    for word, pos_tag in pos_tags:
        pos = get_wordnet_pos(pos_tag)
        lemma = lemmatizer.lemmatize(word, pos)
        lemmas.append(lemma)
    return lemmas

def get_wordnet_pos(treebank_tag):
    if treebank_tag.startswith('J'):
        return wordnet.ADJ
    elif treebank_tag.startswith('V'):
        return wordnet.VERB
    elif treebank_tag.startswith('N'):
        return wordnet.NOUN
    elif treebank_tag.startswith('R'):
        return wordnet.ADV
    else:
        return wordnet.NOUN

def tokenize(text):
    tokens = nltk.word_tokenize(text)
    return tokens

# get port stemmer
port_stemmer = PorterStemmer()
# get word net lemmatizer
world_ner_lemmatizer = WordNetLemmatizer()

def stem(words, stemmer=port_stemmer):
    stemmed_words = [stemmer.stem(word) for word in words]
    return stemmed_words

def preprocess_df_text(text_column, preprocessing_options: dict):
    print_preprocessing_options(**preprocessing_options)
    return text_column.apply(lambda x: preprocess_text(x, **preprocessing_options))

def print_preprocessing_options(stemmer=None, lemmatizer=None, stem_flag=False, lemmatize_flag=False,
                                tokenize_flag=True):
    print("Preprocessing step:")
    if stem_flag:
        print("Stemming text using {} stemmer".format(stemmer.__class__.__name__))
    if lemmatize_flag:
        print("Lemmatizing text using {} lemmatizer".format(lemmatizer.__class__.__name__))
    if tokenize_flag:
        print("Tokenizing text")

def preprocess_text(text,
                    stemmer=None,
                    lemmatizer=None,
                    stem_flag=False,
                    lemmatize_flag=False,
                    tokenize_flag=True):
    if stem_flag and lemmatize_flag:
        raise ValueError("Both stemmer and lemmatizer cannot be applied. Please choose one.")

    if tokenize_flag:
        tokens = tokenize(text)
    else:
        tokens = text

    if stem_flag:
        stemmer = stemmer if stemmer is not None else PorterStemmer()
        stemmed_words = stem(tokens, stemmer=stemmer)
    else:
        stemmed_words = tokens

    if lemmatize_flag:
        lemmatized_words = lemmatize(stemmed_words,
                                     lemmatizer=lemmatizer if lemmatizer is not None else world_ner_lemmatizer)
    else:
        lemmatized_words = stemmed_words

    return ' '.join(lemmatized_words)

class TextPreprocessor:
    def __init__(self,
                 df: pd.DataFrame = None,
                 text_column: str = 'text',
                 stemmer=None,
                 lemmatizer=None,
                 stem_flag=True,
                 lemmatize_flag=False,
                 tokenize_flag=True):
        self.df = df
        self.text_column = text_column
        self.stemmer = stemmer
        self.lemmatizer = lemmatizer
        self.stem_flag = stem_flag
        self.lemmatize_flag = lemmatize_flag
        self.tokenize_flag = tokenize_flag

    def generate_report(self):
        report_str = ""
        if self.stem_flag:
            if self.stemmer:
                if self.stemmer == "snowball":
                    report_str += "Using Snowball stemmer, "
                else:
                    report_str += f"Using {self.stemmer} stemmer, "
            else:
                report_str += "Stemming text, "
        if self.lemmatize_flag:
            if self.lemmatizer:
                report_str += f"Using {self.lemmatizer} lemmatizer, "
            else:
                report_str += "Lemmatizing text, "
        if self.tokenize_flag:
            report_str += "Tokenizing text, "
        if report_str:
            report_str = report_str[:-2]
            title_to_print = f"The following preprocessing steps will be applied to the column '{self.text_column}': {report_str}."
            print(title_to_print)
            rc = ReportCollector(REPORT_PATH)
            rc.save_text(title_to_print)
        else:
            print("No preprocessing steps selected.")

def preprocess_text_df(text_preprocessor: TextPreprocessor):
    df = text_preprocessor.df
    text_column = text_preprocessor.text_column

    df[text_column] = df[text_column].apply(lambda x: preprocess_text(
        x,
        stemmer=text_preprocessor.stemmer,
        lemmatizer=text_preprocessor.lemmatizer,
        stem_flag=text_preprocessor.stem_flag,
        lemmatize_flag=text_preprocessor.lemmatize_flag,
        tokenize_flag=text_preprocessor.tokenize_flag,
    ))
    return df


import math
import numpy as np

# progressive passes (25% closer to goal)
def calculate_progressive_passes(df):
    df['beginning'] = np.sqrt(np.square(120 - df['x']) + np.square(40 - df['y']))
    df['end'] = np.sqrt(np.square(120 - df['end_x']) + np.square(40 - df['end_y']))
    df.reset_index(inplace=True, drop=True)
    # Get progressive passes
    df['progressive'] = [(df['end'][x]) / (df['beginning'][x]) < .75 for x in range(len(df['beginning']))]
    # Filter for progressive passes
    df = df.loc[df['progressive'] == True].reset_index(drop=True)
    return df

# deep completions
def calculate_deep_completions(df):
    df['initialDistancefromgoal'] = np.sqrt(((120 - df['x'])**2) + ((40 - df['y'])**2))
    df['finalDistancefromgoal'] = np.sqrt(((120 - df['end_x'])**2) + ((40 - df['end_y'])**2))

    df['deepCompletion'] = (np.where(((df['finalDistancefromgoal'] <= (21.87)) &
                                      (df['initialDistancefromgoal'] >= (21.87))), True, False))
    df_deep_completion = df[df["deepCompletion"]==True]
    return df_deep_completion

def draw_arrow_with_shrink(ax, x, y, end_x, end_y, lw, line_color, alpha, dist_delta=2.5):
    dist = math.hypot(end_x - x, end_y - y)
    angle = math.atan2(end_y - y, end_x - x)
    upd_end_x = x + (dist - dist_delta) * math.cos(angle)
    upd_end_y = y + (dist - dist_delta) * math.sin(angle)
    upd_x = end_x - (dist - dist_delta * 1.2) * math.cos(angle)
    upd_y = end_y - (dist - dist_delta * 1.2) * math.sin(angle)
    ax.annotate('', xy=(upd_end_x, upd_end_y), xytext=(upd_x, upd_y), zorder=1,
                arrowprops=dict(linewidth=lw, color=line_color, alpha=alpha,
                                headwidth=15, headlength=15))


from picamera import PiCamera
import time
from datetime import datetime

# take foto
def captureImage(fname):
    camera = PiCamera()
    camera.resolution = (1024,768)
    camera.framerate = 30
    camera.vflip = True

    now = datetime.now()
    dt = now.strftime("%Y%m%d_%H:%M:%S")
    print(dt)
    try:
        camera.capture("fotos/"+fname+dt+".jpg") # ,use_video_port=True)
        time.sleep(5)
    finally:
        camera.close() #sleep(10)

def captureVideo():
    camera = PiCamera()
    camera.resolution = (1024,768)
    camera.framerate = 30
    camera.vflip = True

    now = datetime.now()
    dt = now.strftime("%Y%m%d_%H%M%S")
    camera.start_recording("filmpje_"+str(dt)+".h264")
    time.sleep(15)
    camera.stop_recording()
    camera.close()

imagelocation='/home/pi/raspi/fotos/'
while True:
    captureImage(imagelocation+"kweekkas_")
    time.sleep(60*(60-datetime.now().minute) ) # de tweede en volgende foto's worden op het hele uur genomen

''''
while True:
    now = datetime.now()
    dt = now.strftime("%Y%m%d_%H%M%S")
    pir.wait_for_motion()
    print("motion detected")
    camera.start_recording("filmpje_"+str(dt)+".h264")
    time.sleep(10)
    pir.wait_for_no_motion()
    print("no motion")
#    camera.stop_recording()

'''


import time
from turtle import Screen
from player import Player
from car_manager import CarManager
from scoreboard import ScoreBoard
from turtle import Turtle
from random import randint

def net(x, y):
    my_net = Turtle()
    my_net.penup()
    my_net.goto(x=x, y=y)
    for n in range(100):
        my_net.pendown()
        my_net.forward(20)
        my_net.penup()
        my_net.forward(20)

screen = Screen()
screen.setup(width=600, height=600)
screen.tracer(0)
player = Player()
manager = CarManager()
level = ScoreBoard()
i = -250
for n in range(6):
    net(-300, i)
    i += 100

screen.listen()
screen.onkeypress(player.move, "Up")

game_is_on = True
while game_is_on:
    time.sleep(0.1)
    screen.update()
    manager.generate_cars()
    manager.cars_moves()
    for car in manager.car_list:
        if car.distance(player) < 20:
            game_is_on = False
            level.game_over()
    if player.ycor() == 280:
        player.start()
        manager.increase_speed()
        level.add_level()
screen.exitonclick()