"""
DFS, BFS
Dijkstra for single source shortest path
SCC on Digraph
Application: 2-SAT
MST
PageRank (Markov Chains)

Connected components in undirected graph G
    Run DFS and keep track of component#

"""
from collections import defaultdict

def edges_to_adjlist(edges):
    adjlist = defaultdict(list)
    for s,t in edges:
        adjlist[s].append(t)
        adjlist[t].append(s)
    return adjlist

def classic_dfs(graph):
    visited = set()
    start = list(graph.keys())[0]
    def dfs(graph, v):
        if v not in visited:
            visited.add(v)
            for t in graph[v]:
                dfs(graph, t)
    dfs(graph, start)
    return visited

def cc_dfs0(graph):
    # input: G = (V, E) in adj list representation
    # output: vertices labelled by connected components
    cc = 0
    ccnum = {}
    visited = set()
    def explore(z): # should be named dfs
        ccnum[z] = cc
        visited.add(z)
        for t in graph[z]:
            if t not in visited:
                explore(t)
    for v in graph:
        if v not in visited:
            cc += 1
            explore(v)
    return ccnum

def cc_dfs1(graph): # base on classic_dfs
    cc = 0
    ccnum = {}
    visited = set()
    def dfs(v):
        if v not in visited:
            ccnum[v] = cc
            visited.add(v)
            for t in graph[v]:
                dfs(t)
    for v in graph:
        if v not in visited:
            cc += 1
            dfs(v)
    return ccnum

def cc_dfs2(graph): # save path, backtrack
    cc = 0
    ccnum = {}
    visited = set()
    track = {}
    def explore(z): # should be named dfs
        ccnum[z] = cc
        visited.add(z)
        for t in graph[z]:
            if t not in visited:
                track[t] = z
                explore(t)
    for v in graph:
        if v not in visited:
            cc += 1
            explore(v)
    return ccnum, track

def cc_dfs3(graph):
    # prepare for digraph
    # preorder and postorder
    clock = 1
    pre, post = {}, {}
    visited = set()
    track = {}
    def explore(z): # should be named dfs
        nonlocal clock
        pre[z] = clock
        clock += 1
        visited.add(z)
        for t in graph[z]:
            if t not in visited:
                track[t] = z
                explore(t)
        post[z] = clock
        clock += 1
    for v in graph:
        if v not in visited:
            explore(v)
    return pre, post

def test0():
    edges = [(1,2), (2,3), (1,4), (3,4), (5,6)]
    adjlist = edges_to_adjlist(edges)
    print(adjlist)
    print(classic_dfs(adjlist))
    print(cc_dfs0(adjlist))
    print(cc_dfs1(adjlist))
    print(cc_dfs2(adjlist))
    print(cc_dfs3(adjlist))

if __name__ == '__main__':
    test0()


import csv
import cv2
import numpy as np
import matplotlib.pyplot as plt

from keras.models import Sequential
from keras.layers import Flatten, Dense, Cropping2D, Convolution2D, Lambda
from keras.callbacks import EarlyStopping
from keras.callbacks import ModelCheckpoint
from keras.utils.visualize_util import plot

from datetime import datetime

# Helper function to load data from a directory
def load_data(data_dir, X, y):

    with open(data_dir + '\\driving_log.csv') as csv_file:
        reader = csv.reader(csv_file)
        headers = next(reader)

        for line in reader:
            center_image_file_path = data_dir + '\\' + line[headers.index('center')]

            image = cv2.imread(center_image_file_path)
            steer = float(line[headers.index('steering')])

            X.append(image)
            y.append(steer)

            # add flipped image to dataset
            X.append(np.fliplr(image))
            y.append(-steer)

    return

def train(X, y):

    # define nvidia model
    model = Sequential()
    model.add(Lambda(lambda x : (x / 255.0) -0.5, input_shape = (160, 320, 3)))
    model.add(Cropping2D(cropping = ((70, 25), (0, 0)), input_shape = (160, 320, 3)))
    model.add(Convolution2D(24, 5, 5, subsample = (2, 2), activation = 'relu'))
    model.add(Convolution2D(36, 5, 5, subsample = (2, 2), activation = 'relu'))
    model.add(Convolution2D(48, 5, 5, subsample = (2, 2), activation = 'relu'))
    model.add(Convolution2D(64, 3, 3, activation = 'relu'))
    model.add(Convolution2D(64, 3, 3, activation = 'relu'))

    model.add(Flatten())
    model.add(Dense(100))
    model.add(Dense(50))
    model.add(Dense(10))
    model.add(Dense(1))

    model.compile(loss = 'mse', optimizer = 'adam')

    plot(model, to_file='model.png', show_shapes=True)

    # configure early_stopping_callback to prevent overfitting
    checkpoint_callback = ModelCheckpoint('./model_checkpoints/model_{epoch:02d}.h5',  verbose = 1)
    history = model.fit(X, y, validation_split = 0.2, shuffle = True, nb_epoch = 20, callbacks = [checkpoint_callback])

    plt.plot(history.history['loss'])
    plt.plot(history.history['val_loss'])
    plt.title('model loss')
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.legend(['train', 'validation'], loc='upper left')
    plt.savefig('model_loss.png')

    return

if __name__ == '__main__':

    X = []
    y = []

    print(datetime.now())

    # load data from various directories in X, y arrays
    load_data('.\\data\\sample_data', X, y)
    load_data('.\\data\\lap', X, y)
    load_data('.\\data\\specific_training_1', X, y)
    load_data('.\\data\\specific_training_2', X, y)
    load_data('.\\data\\specific_training_3', X, y)

    print(datetime.now())

    train(np.array(X), np.array(y))

    print(datetime.now())


import tensorflow as tf
from common.net.net import Net

class Softmax(Net):

  def __init__(self, **kwargs):
    self.output_size = kwargs.get('output_size', 1)
    return

  def inference(self, data):
    feature_size = data.get_shape()[1].value

    with tf.name_scope('weights'):
      W = tf.Variable(tf.zeros([feature_size, self.output_size]))
    with tf.name_scope('biases'):
      b = tf.Variable(tf.zeros([self.output_size]), name='bias')
    with tf.name_scope('y'):
      y = tf.matmul(data, W) + b
    with tf.name_scope('predictions'):
      predictions = tf.nn.softmax(y)

    return {'logits': y, 'predictions': predictions}

  def loss(self, layers, labels):
    logits = layers['logits']

    with tf.variable_scope('losses'):
      loss = tf.reduce_mean(
          tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=logits))
    return loss

  def metric(self, layers, labels):
    predictions = layers['predictions']
    with tf.variable_scope('metrics'):
      metrics = {
          "accuracy":
              tf.metrics.accuracy(
                  tf.argmax(labels, 1), predictions=tf.argmax(predictions, 1))
      }
    return metrics


# coding: utf-8

"""
    NiFi Rest API

    The Rest API provides programmatic access to command and control a NiFi instance in real time. Start and                                             stop processors, monitor queues, query provenance data, and more. Each endpoint below includes a description,                                             definitions of the expected input and output, potential response codes, and the authorizations required                                             to invoke each service.

    OpenAPI spec version: 1.19.0
    Contact: dev@nifi.apache.org
    Generated by: https://github.com/swagger-api/swagger-codegen.git
"""

from pprint import pformat
from six import iteritems
import re

class RemoteProcessGroupStatusDTO(object):
    """
    NOTE: This class is auto generated by the swagger code generator program.
    Do not edit the class manually.
    """

    """
    Attributes:
      swagger_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    swagger_types = {
        'group_id': 'str',
        'id': 'str',
        'name': 'str',
        'target_uri': 'str',
        'transmission_status': 'str',
        'stats_last_refreshed': 'str',
        'validation_status': 'str',
        'aggregate_snapshot': 'RemoteProcessGroupStatusSnapshotDTO',
        'node_snapshots': 'list[NodeRemoteProcessGroupStatusSnapshotDTO]'
    }

    attribute_map = {
        'group_id': 'groupId',
        'id': 'id',
        'name': 'name',
        'target_uri': 'targetUri',
        'transmission_status': 'transmissionStatus',
        'stats_last_refreshed': 'statsLastRefreshed',
        'validation_status': 'validationStatus',
        'aggregate_snapshot': 'aggregateSnapshot',
        'node_snapshots': 'nodeSnapshots'
    }

    def __init__(self, group_id=None, id=None, name=None, target_uri=None, transmission_status=None, stats_last_refreshed=None, validation_status=None, aggregate_snapshot=None, node_snapshots=None):
        """
        RemoteProcessGroupStatusDTO - a model defined in Swagger
        """

        self._group_id = None
        self._id = None
        self._name = None
        self._target_uri = None
        self._transmission_status = None
        self._stats_last_refreshed = None
        self._validation_status = None
        self._aggregate_snapshot = None
        self._node_snapshots = None

        if group_id is not None:
          self.group_id = group_id
        if id is not None:
          self.id = id
        if name is not None:
          self.name = name
        if target_uri is not None:
          self.target_uri = target_uri
        if transmission_status is not None:
          self.transmission_status = transmission_status
        if stats_last_refreshed is not None:
          self.stats_last_refreshed = stats_last_refreshed
        if validation_status is not None:
          self.validation_status = validation_status
        if aggregate_snapshot is not None:
          self.aggregate_snapshot = aggregate_snapshot
        if node_snapshots is not None:
          self.node_snapshots = node_snapshots

    @property
    def group_id(self):
        """
        Gets the group_id of this RemoteProcessGroupStatusDTO.
        The unique ID of the process group that the Processor belongs to

        :return: The group_id of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._group_id

    @group_id.setter
    def group_id(self, group_id):
        """
        Sets the group_id of this RemoteProcessGroupStatusDTO.
        The unique ID of the process group that the Processor belongs to

        :param group_id: The group_id of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._group_id = group_id

    @property
    def id(self):
        """
        Gets the id of this RemoteProcessGroupStatusDTO.
        The unique ID of the Processor

        :return: The id of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._id

    @id.setter
    def id(self, id):
        """
        Sets the id of this RemoteProcessGroupStatusDTO.
        The unique ID of the Processor

        :param id: The id of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._id = id

    @property
    def name(self):
        """
        Gets the name of this RemoteProcessGroupStatusDTO.
        The name of the remote process group.

        :return: The name of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._name

    @name.setter
    def name(self, name):
        """
        Sets the name of this RemoteProcessGroupStatusDTO.
        The name of the remote process group.

        :param name: The name of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._name = name

    @property
    def target_uri(self):
        """
        Gets the target_uri of this RemoteProcessGroupStatusDTO.
        The URI of the target system.

        :return: The target_uri of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._target_uri

    @target_uri.setter
    def target_uri(self, target_uri):
        """
        Sets the target_uri of this RemoteProcessGroupStatusDTO.
        The URI of the target system.

        :param target_uri: The target_uri of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._target_uri = target_uri

    @property
    def transmission_status(self):
        """
        Gets the transmission_status of this RemoteProcessGroupStatusDTO.
        The transmission status of the remote process group.

        :return: The transmission_status of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._transmission_status

    @transmission_status.setter
    def transmission_status(self, transmission_status):
        """
        Sets the transmission_status of this RemoteProcessGroupStatusDTO.
        The transmission status of the remote process group.

        :param transmission_status: The transmission_status of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._transmission_status = transmission_status

    @property
    def stats_last_refreshed(self):
        """
        Gets the stats_last_refreshed of this RemoteProcessGroupStatusDTO.
        The time the status for the process group was last refreshed.

        :return: The stats_last_refreshed of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._stats_last_refreshed

    @stats_last_refreshed.setter
    def stats_last_refreshed(self, stats_last_refreshed):
        """
        Sets the stats_last_refreshed of this RemoteProcessGroupStatusDTO.
        The time the status for the process group was last refreshed.

        :param stats_last_refreshed: The stats_last_refreshed of this RemoteProcessGroupStatusDTO.
        :type: str
        """

        self._stats_last_refreshed = stats_last_refreshed

    @property
    def validation_status(self):
        """
        Gets the validation_status of this RemoteProcessGroupStatusDTO.
        Indicates whether the component is valid, invalid, or still in the process of validating (i.e., it is unknown whether or not the component is valid)

        :return: The validation_status of this RemoteProcessGroupStatusDTO.
        :rtype: str
        """
        return self._validation_status

    @validation_status.setter
    def validation_status(self, validation_status):
        """
        Sets the validation_status of this RemoteProcessGroupStatusDTO.
        Indicates whether the component is valid, invalid, or still in the process of validating (i.e., it is unknown whether or not the component is valid)

        :param validation_status: The validation_status of this RemoteProcessGroupStatusDTO.
        :type: str
        """
        allowed_values = ["VALID", "INVALID", "VALIDATING"]
        if validation_status not in allowed_values:
            raise ValueError(
                "Invalid value for `validation_status` ({0}), must be one of {1}"
                .format(validation_status, allowed_values)
            )

        self._validation_status = validation_status

    @property
    def aggregate_snapshot(self):
        """
        Gets the aggregate_snapshot of this RemoteProcessGroupStatusDTO.
        A status snapshot that represents the aggregate stats of all nodes in the cluster. If the NiFi instance is a standalone instance, rather than a cluster, this represents the stats of the single instance.

        :return: The aggregate_snapshot of this RemoteProcessGroupStatusDTO.
        :rtype: RemoteProcessGroupStatusSnapshotDTO
        """
        return self._aggregate_snapshot

    @aggregate_snapshot.setter
    def aggregate_snapshot(self, aggregate_snapshot):
        """
        Sets the aggregate_snapshot of this RemoteProcessGroupStatusDTO.
        A status snapshot that represents the aggregate stats of all nodes in the cluster. If the NiFi instance is a standalone instance, rather than a cluster, this represents the stats of the single instance.

        :param aggregate_snapshot: The aggregate_snapshot of this RemoteProcessGroupStatusDTO.
        :type: RemoteProcessGroupStatusSnapshotDTO
        """

        self._aggregate_snapshot = aggregate_snapshot

    @property
    def node_snapshots(self):
        """
        Gets the node_snapshots of this RemoteProcessGroupStatusDTO.
        A status snapshot for each node in the cluster. If the NiFi instance is a standalone instance, rather than a cluster, this may be null.

        :return: The node_snapshots of this RemoteProcessGroupStatusDTO.
        :rtype: list[NodeRemoteProcessGroupStatusSnapshotDTO]
        """
        return self._node_snapshots

    @node_snapshots.setter
    def node_snapshots(self, node_snapshots):
        """
        Sets the node_snapshots of this RemoteProcessGroupStatusDTO.
        A status snapshot for each node in the cluster. If the NiFi instance is a standalone instance, rather than a cluster, this may be null.

        :param node_snapshots: The node_snapshots of this RemoteProcessGroupStatusDTO.
        :type: list[NodeRemoteProcessGroupStatusSnapshotDTO]
        """

        self._node_snapshots = node_snapshots

    def to_dict(self):
        """
        Returns the model properties as a dict
        """
        result = {}

        for attr, _ in iteritems(self.swagger_types):
            value = getattr(self, attr)
            if isinstance(value, list):
                result[attr] = list(map(
                    lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
                    value
                ))
            elif hasattr(value, "to_dict"):
                result[attr] = value.to_dict()
            elif isinstance(value, dict):
                result[attr] = dict(map(
                    lambda item: (item[0], item[1].to_dict())
                    if hasattr(item[1], "to_dict") else item,
                    value.items()
                ))
            else:
                result[attr] = value

        return result

    def to_str(self):
        """
        Returns the string representation of the model
        """
        return pformat(self.to_dict())

    def __repr__(self):
        """
        For `print` and `pprint`
        """
        return self.to_str()

    def __eq__(self, other):
        """
        Returns true if both objects are equal
        """
        if not isinstance(other, RemoteProcessGroupStatusDTO):
            return False

        return self.__dict__ == other.__dict__

    def __ne__(self, other):
        """
        Returns true if both objects are not equal
        """
        return not self == other


from event import Event, EventType
from station import Station

# Iniettatore di Clienti.

class ClientGenerator(Station):
    distribution = None

    def __init__(self, next_station, observer, simulator, distribution):
        super().__init__("gen", observer, simulator)
        self.next_station = next_station
        self.distribution = distribution
        time = distribution.next_sample()
        evt = Event(time, self, EventType.DEPARTURE)
        simulator.schedule_event(evt)

    def client_departure(self, event):
        evt = Event(0,  self.next_station, EventType.ARRIVAL)
        self.simulator.schedule_event(evt)
        time = self.distribution.next_sample()
        evt = Event(time,  self, EventType.DEPARTURE)
        self.simulator.schedule_event(evt)

    def reset(self):
        time = self.distribution.next_sample()
        evt = Event(time, self, EventType.DEPARTURE)
        self.simulator.schedule_event(evt)


import torch
from torch import nn
import torch.nn.functional as F
import math
import os
from knnbox.common_utils import read_config, write_config
from knnbox.combiner.utils import calculate_combined_prob, calculate_knn_prob

class RobustCombiner(nn.Module):
    r""" Robust knn-mt Combiner """
    def __init__(self,
                max_k,
                probability_dim,
                midsize = 32,
                midsize_dc = 4,
                topk_wp = 8,
                **kwargs
                ):
        super().__init__()

        self.max_k = max_k
        self.probability_dim = probability_dim
        self.midsize = midsize
        self.midsize_dc = midsize_dc
        self.topk_wp = topk_wp
        self.kwargs = kwargs
        self.mask_for_distance = None

        self.meta_k_network = MetaKNetwork(
            max_k=self.max_k,
            midsize=self.midsize,
            midsize_dc=self.midsize_dc,
            topk_wp=self.topk_wp,
            **kwargs
        )

    def set_num_updates(self, num_updates):
        """State from trainer to pass along to model at every update."""

        def _apply(m):
            if hasattr(m, "set_num_updates") and m != self:
                m.set_num_updates(num_updates)

        self.apply(_apply)

    def get_knn_prob(
        self,
        tgt_index: torch.Tensor,
        knn_dists: torch.Tensor,
        knn_key_feature: torch.Tensor,
        network_probs: torch.Tensor,
        network_select_probs: torch.Tensor,
        device="cuda:0"
    ):
        metak_outputs = self.meta_k_network(
            tgt_index=tgt_index,
            knn_dists=knn_dists,
            knn_key_feature=knn_key_feature,
            network_probs=network_probs,
            network_select_probs=network_select_probs
        )

        self.lambda_ = metak_outputs["knn_lambda"]

        knn_prob = torch.zeros(*network_probs.shape, device=device)
        knn_prob.scatter_add_(dim=-1, index=tgt_index, src=metak_outputs["probs"])

        return knn_prob

    def get_combined_prob(self, knn_prob, neural_model_logit, log_probs=False):
        r""" get combined probs of knn_prob and neural_model_prob """
        return calculate_combined_prob(knn_prob, neural_model_logit, self.lambda_, log_probs)

    def dump(self, path):
        r""" dump the robust knn-mt to disk """
        # step 1. write config
        config = {}
        config["max_k"] = self.max_k
        config["probability_dim"] = self.probability_dim
        config["midsize"] = self.midsize
        config["midsize_dc"] = self.midsize_dc
        for k, v in self.kwargs.items():
            config[k] = v
        write_config(path, config)
        # step 2. save model
        torch.save(self.state_dict(), os.path.join(path, "robust_combiner.pt"))

    @classmethod
    def load(cls, path):
        r""" load the robust knn-mt from disk """
        config = read_config(path)
        robust_combiner = cls(**config)

        robust_combiner.load_state_dict(torch.load(os.path.join(path, "robust_combiner.pt")))
        return robust_combiner

    @staticmethod
    def _generate_mask_for_distance(max_k, device):
        k_mask = torch.empty((max_k, max_k)).fill_(999.)
        k_mask = torch.triu(k_mask, diagonal=1) + 1
        power_index = torch.tensor([pow(2, i) - 1 for i in range(0, int(math.log(max_k, 2)) + 1)])
        k_mask = k_mask[power_index]
        k_mask.requires_grad = False
        k_mask = k_mask.to(device)
        return k_mask

class MetaKNetwork(nn.Module):
    r""" meta k network of robust knn-mt """
    def __init__(
        self,
        max_k = 32,
        midsize = 32,
        midsize_dc = 4,
        topk_wp = 8,
        k_trainable = True,
        lambda_trainable = True,
        temperature_trainable = True,
        relative_label_count = False,
        device = "cuda:0",
        **kwargs,
    ):
        super().__init__()
        self.max_k = max_k
        self.k_trainable = k_trainable
        self.lambda_trainable = lambda_trainable
        self.temperature_trainable = temperature_trainable
        self.relative_label_count = relative_label_count
        self.device = device
        self.mask_for_label_count = None
        self.midsize = midsize
        self.midsize_dc = midsize_dc
        self.topk_wp = topk_wp

        # Robust kNN-MT always uses the same configuration
        # ? WP network: S_{NMT}
        self.distance_func = nn.Sequential(
            nn.Linear(self.max_k * 2 + self.topk_wp, 1), # ? W_6
        )
        # ? DC network: c_k
        self.distance_fc1 = nn.Sequential(
            nn.Linear(2, self.midsize_dc), # ? W_4
            nn.Tanh(),
            nn.Linear(self.midsize_dc, 1), # ? W_3
        )
        # ? WP network: S_{kNN}  &  WP network: T
        self.distance_fc2 = nn.Sequential(
            nn.Linear(self.max_k * 2, self.midsize), # ? W_2
            nn.Tanh(),
            nn.Linear(self.midsize, 2), # ? [W_1, W_5]
        )

    def forward(
        self,
        tgt_index: torch.Tensor,
        knn_dists: torch.Tensor,
        knn_key_feature: torch.Tensor,
        network_probs: torch.Tensor,
        network_select_probs: torch.Tensor,
    ):
        B, S, K = knn_dists.size()
        label_counts = self._get_label_count_segment(tgt_index, self.relative_label_count)
        all_key_feature = torch.cat([knn_key_feature.log().unsqueeze(-1), network_select_probs.log().unsqueeze(-1)], -1)
        top_prob, top_idx = torch.topk(network_probs, self.topk_wp)
        knn_feat = torch.cat([knn_dists, label_counts.float()], -1)
        # ? c_k
        noise_logit = self.distance_fc1(all_key_feature).squeeze(-1)
        # ? S_{NMT}
        sim_lambda = self.distance_func(torch.cat([top_prob.log(), knn_key_feature.log(), network_select_probs.log()], -1))
        # ? [S_{kNN},T]
        lambda_logit = self.distance_fc2(knn_feat.view(B, S, -1))
        # ? \lambda_t
        knn_lambda = torch.softmax(torch.cat([lambda_logit[:, :, :1], sim_lambda], -1), -1)[:, :, :1]
        # ? T
        tempe = torch.sigmoid(lambda_logit[:, :, 1:2])
        probs = torch.softmax(-knn_dists * tempe + noise_logit, -1)

        return {
            "probs": probs,
            "knn_lambda": knn_lambda,
        }

    def _get_label_count_segment(self, vals, relative=False):
        r""" this function return the label counts for different range of k nearest neighbor
            [[0:0], [0:1], [0:2], ..., ]
        """

        # caculate `label_count_mask` only once
        if self.mask_for_label_count is None:
            mask_for_label_count = torch.empty((self.max_k, self.max_k)).fill_(1)
            mask_for_label_count = torch.triu(mask_for_label_count, diagonal=1).bool()
            mask_for_label_count.requires_grad = False
            # [0,1,1]
            # [0,0,1]
            # [0,0,0]
            self.mask_for_label_count = mask_for_label_count.to(vals.device)

        ## TODO: The feature below may be unreasonable
        B, S, K = vals.size()
        expand_vals = vals.unsqueeze(-2).expand(B,S,K,K)
        expand_vals = expand_vals.masked_fill(self.mask_for_label_count, value=-1)

        labels_sorted, _ = expand_vals.sort(dim=-1) # [B, S, K, K]
        labels_sorted[:, :, :, 1:] *= ((labels_sorted[:, :, :, 1:] - labels_sorted[:, :, : , :-1]) != 0).long()
        retrieve_label_counts = labels_sorted.ne(0).sum(-1)
        retrieve_label_counts[:, :, :-1] -= 1

        if relative:
            retrieve_label_counts[:, :, 1:] = retrieve_label_counts[:, :, 1:] - retrieve_label_counts[:, :, :-1]

        return retrieve_label_counts


import fins.udp
import os

# fins_instance.memory_area_write(fins.FinsPLCMemoryAreas().DATA_MEMORY_WORD,address,bytes,1)
# mem_area[0] = fins_instance.memory_area_read(fins.FinsPLCMemoryAreas().DATA_MEMORY_WORD,address)

# get PLCIP from user
PLCIP = '192.168.251.1'

# these next few lines is just an algorithm for windows to get the ethernet ip of your computer so it can get the node from it
#os.system('cmd /c "ipconfig > C:/Users/Public/Documents/ipconfig.txt"')
#ipconfig = open("C:/Users/Public/Documents/ipconfig.txt", "r")
word = str()
charcnt = 0
COMPIP = "192.168.251.10"
checkip = False

# break the IP's apart to get the nodes from them
PLCNODE = int(PLCIP.split(".")[3])
COMPNODE = int(COMPIP.split(".")[3])

# init fins connection
fins_instance = fins.udp.UDPFinsConnection()
# plc ip to connect to
fins_instance.connect(PLCIP)
# last number in the ip of each device
fins_instance.dest_node_add = PLCNODE
fins_instance.srce_node_add = COMPNODE

# init spl with gas variable
spl = 2
# clear the message that may or may not be used
message = [0, 0, 0, 0]
while spl != 3:
    # prompt user for decision
    spl = input("0=Read, 1=Write, 2=Clear, 3=Leave: ")
    spl = int(spl)
    # cases
    # if user wants to read from registers
    if spl == 0:
        # prompt amount to read
        length = input("How many registers would you like to read? ")
        # for the amount to read
        for i in range(int(length)):
            # calculate address starting at 0
            address = bytes(1) + bytes([i]) + bytes(1)
            # read from memory
            i = fins_instance.memory_area_read(fins.FinsPLCMemoryAreas().DATA_MEMORY_WORD, address)
            # print it
            print(i[14] * 256 + i[15], end=' ')
        print('\n')

    # if user wants to write to memory
    if spl == 1:
        # prompt for classification
        boo = input("Classification= ")
        # convert to word (high byte, low byte) and add to message
        message[3] = bytes(1) + bytes([int(boo)])
        print("Integer inputs should less than or equal to 65535")
        # prompt user for value
        # area = input("Area= ")
        area = 500
        # convert to high and low byte
        area = [int(int(area) / 256), int(int(area) % 256)]
        # add to message
        message[0] = bytes([area[0]]) + bytes([area[1]])
        # repeat process for data needed
        x = input("X= ")
        x = [int(int(x) / 256), int(int(x) % 256)]
        message[1] = bytes([x[0]]) + bytes([x[1]])
        y = input("Y= ")
        y = [int(int(y) / 256), int(int(y) % 256)]
        message[2] = bytes([y[0]]) + bytes([y[1]])
        # for message
        for i in range(len(message)):
            # calculate address
            address = bytes(1) + bytes([i]) + bytes(1)
            # write to memory
            fins_instance.memory_area_write(fins.FinsPLCMemoryAreas().DATA_MEMORY_WORD, address, message[i], 1)
    # if user wants to clear the memory
    if spl == 2:
        # for the 45 chosen registers
        for i in range(0, 45):
            # get address
            address = bytes(1) + bytes([i]) + bytes(1)
            # write 0 to address
            fins_instance.memory_area_write(fins.FinsPLCMemoryAreas().DATA_MEMORY_WORD, address, b'\x00\x00', 1)
print("Goodbye")
# end of program


from real_inputs.day_6 import real_input
from example_inputs.day_6 import example_input_1, example_input_2, example_input_3, example_input_4, example_input_5

def check_chars_unique(chars: str) -> bool:
    return len(chars) == len(set(chars))

def main(signal: str, length_of_unique_chars: int):
    iterator = 0
    while not check_chars_unique(signal[iterator:iterator+length_of_unique_chars]):
        iterator += 1

    print(iterator+length_of_unique_chars)

print("should be 7")
main(example_input_1, 4)
print("should be 5")
main(example_input_2, 4)
print("should be 6")
main(example_input_3, 4)
print("should be 10")
main(example_input_4, 4)
print("should be 11")
main(example_input_5, 4)

print("real input")
main(real_input, 14)

print("should be 19")
main("mjqjpqmgbljsphdztnvjfqwrcgsmlb", 14)
print("should be 23")
main("bvwbjplbgvbhsrlpgdmjqwftvncz", 14)
print("should be 23")
main("nppdvjthqldpwncqszvftbrmjlhg", 14)
print("should be 29")
main("nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg", 14)
print("should be 26")
main("zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw", 14)


from collections import deque
def solution(priorities, location):
    answer = 0
    pp = sorted(priorities)
    s = deque()

    for i in range(len(priorities)):
        s.append((priorities[i],i))

    while True:
        p, l = s.popleft()
        if p == pp[-1]:
            pp.pop()
            answer += 1
            if l == location:
                return answer
        else:
            s.append((p,l))

print(solution([2, 1, 3, 2],	2))


"""
Given n non-negative integers representing an elevation map where the width of each bar is 1,
compute how much water it is able to trap after raining.
Example:
    Input: [0,1,0,2,1,0,1,3,2,1,2,1]
    Output: 6
"""
from typing import List

def max_water(elevation: List[int]) -> int:
    size = len(elevation)

    left_largest = [0] * size
    right_largest = [0] * size

    for index in range(1, size):
        left_largest[index] = max(left_largest[index - 1], elevation[index - 1])

    for index in range(size - 2, -1, -1):
        right_largest[index] = max(right_largest[index + 1], elevation[index + 1])

    water = 0
    for index in range(1, size - 1):  # water at index
        water += max(
            0, min(left_largest[index], right_largest[index]) - elevation[index]
        )

    return water

if __name__ == "__main__":
    assert max_water([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]) == 6


import dash_core_components as dcc
import dash_html_components as html

import dash_bootstrap_components as dbc

import pathlib
import page_builder as pb
from settings import *
from charts import empty_chart, figure_4_7

chapter_num = '4.5'
bannerImgSrc = IMAGES_PATH+'TerrestrialSections/Albedo_Malahide-BarryDuggan.jpg'
bannerImgCredit = 'Barry Duggan'

introText = """
    Radiation from the sun is essential for all biological processes on Earth.
    The proportion of this radiation that is reflected by the Earth’s surface is known as
    albedo. Albedo is key to understanding energy exchanges between the atmosphere
    and the surface. The average Earth albedo is 0.3 on a scale from 0.0 to 1.0.
    However, this proportion changes seasonally and regionally.
        """
bulletPoint1 = """
    High albedo values are observed from surfaces covered by snow, while dense forest
    and water bodies, which absorb most of the incident radiation, have low albedo.
        """
bulletPoint2 = """
    An anomalously high value of albedo was observed during the last week of 2010,
    when most of Ireland was covered in snow.
        """
bulletPoints = [bulletPoint1, bulletPoint2]
trendChartTitle = 'Mean 10-Day Albedo - Ireland'
trendChart = figure_4_7()

trendCaption = """

        """

infrastructureText = """
Ground-based observations of albedo require measurement of incoming and reflected solar radiation. These measurements tend to be made at local scales and for research studies. There is no long-term in situ albedo monitoring programme in Ireland.
Regional albedo measurements are generally made by satellite sensors, which have the ability to measure the total radiation reflected by the Earth’s surface on a regular basis. There is very good continuity of satellite observations since the 1980s from which albedo estimates can be inferred. The Copernicus Global Land Service (CGLS) generates a surface albedo dataset as part of a suite of energy monitoring products. This dataset is derived from the SPOT VEGETATION and PROBA-V satellite programmes.

        """
infrastructureMap = empty_chart()

infoLinks = [
    {'text': 'Albedo ESSENTIAL CLIMATE VARIABLE (ECV). GCOS FACTSHEETS',
     'url': 'https://gcos.wmo.int/en/essential-climate-variables/albedo/'},
         {'text': 'The Copernicus Global Land Service (CGLS) data',
     'url': 'https://land.copernicus.eu/global/products/sa'},
         {'text': 'Current global satellite albedo products',
     'url': 'https://lpvs.gsfc.nasa.gov/producers2.php?topic=SurfRad'},
         {'text': 'EUMETSAT Surface Albedo Validation Sites',
     'url': 'http://savs.eumetsat.int/'},
         {'text': 'Wang, Z., et al. (2019). Global Surface Albedo Product Validation Best Practices Protocol. Practice for Satellite Derived Land Product Validation (p. 45): Land Product Validation Subgroup (WGCV/CEOS). doi:10.5067/DOC/CEOSWGCV/LPV/ALBEDO.001 ',
     'url': 'https://www.researchgate.net/publication/332106030_Global_Surface_Albedo_Product_Validation_Best_Practices'},

]

########################################################################################################################
chapter_dict = next(
    (item for item in CHAPTERS if item['chapter-num'] == chapter_num), None)

trendCaption1 = """
Time series of 10-Day periods of average albedo over Ireland derived from the Copernicus Global
 Land Service (CGLS) datasets for the period 1999 to 2018.
 A seasonal trend is observed, in which the highest values occur during winter, particularly in
 late December and early January, while the lowest values are observed during the summer months,
 when vegetation cover is at its maximum.
        """

trendCaption2 = """
Albedo during four 10-day periods in 2018 derived from the Copernicus Global Land Service (CGLS)
datasets. © European Union, Copernicus Land Monitoring Service 2020, European Environment Agency (EEA).
The highest values are observed during the autumn and winter, when the presence of vegetation is reduced.
        """

custom_trend = dbc.Container(
        className='sr-trends',
        style={'borderColor': chapter_dict['domain-color']},
        id='trends',
        children=[
            html.H3(
                className='sr-section-heading',
                style={'color': chapter_dict['domain-color']},
                children='Trends',
            ),
            dbc.Row(
                children=[
                    dbc.Col(className="col-md-10 offset-md-1",
                            children=[
                                html.H4(
                                    className='sr-chart-title',
                                    children=trendChartTitle),
                                dcc.Graph(
                                    figure=trendChart,
                                    id='chart'+chapter_dict['href'],
                                    config={'displayModeBar': False})]
                            )
                ]
            ),
            dbc.Row(
                children=[
                    dbc.Col(className="col-md-10 offset-md-1",
                            children=[
                                html.P(
                                    className='sr-chart-caption',
                                    children=[
                                        html.I(className="fas fa-play _up",
                                               style={"color": chapter_dict['domain-color']}),
                                        trendCaption1]
                                )]
                            )
                ]
            ),
                        dbc.Row(
                children=[
                    dbc.Col(className="col-md-10 offset-md-1",
                            children=[
                                html.H4(
                                    className='sr-chart-title',
                                    children='Albedo Over Four 10-Day Periods in 2018'),
                                html.Img(src=IMAGES_PATH+'TerrestrialSections/Albedo_Seasonal_Maps.png')]
                            )
                ]
            ),
            dbc.Row(
                children=[
                    dbc.Col(className="col-md-10 offset-md-1",
                            children=[
                                html.P(
                                    className='sr-chart-caption',
                                    children=[
                                        html.I(className="fas fa-play _up",
                                               style={"color": chapter_dict['domain-color']}),
                                        trendCaption2]
                                )]
                            )
                ]
            ),
        ])

custom_infrastructure = dbc.Container(
        className='sr-infrastructure',
        style={'borderColor': chapter_dict['domain-color']},
        id='infrastructure',
        children=[
            html.H3(
                className='sr-section-heading',
                children='Infrastructure',
                style={'color': chapter_dict['domain-color']},
            ),
            dbc.Row(
                children=[
                    dbc.Col(className="col-12 my-auto",
                            children=[
                                html.P(infrastructureText)]
                            ),

                ])
        ])
def create_layout(app):
    return html.Div(
        children=[
            pb.build_banner(bannerImgSrc,
                            bannerImgCredit,
                            chapter_dict
                            ),
            pb.build_breadcrumb(chapter_dict),
            pb.build_nav(chapter_dict),
            pb.build_intro(introText,
                           bulletPoints,
                           chapter_dict
                           ),
            custom_trend,
            custom_infrastructure,
            pb.build_info(infoLinks,
                          chapter_dict),

            pb.build_nav_carousel(chapter_dict)
        ])


import random

# Food values
food = random.randint(0,2)
CANDY = 0
STEAK = 1
POTION = 2
CANDY_RESTORE = 10
STEAK_RESTORE = 15
POTION_RESTORE = 35

# Creature values
BUNNY = 0
DWARF = 1
TROLL = 2
WYVERN = 3
GAMBLER = 4
BAT = 6
TROGG = 7
BUNNY_DAMAGE = 10
DWARF_DAMAGE = 15
BAT_DAMAGE = 25
TROLL_DAMAGE = 30
WYVERN_DAMAGE = 50
GAMBLER_DAMAGE = 40
TROGG_DAMAGE = 60

# Misc values
TUITION = 50
BEER = 50
invalid_direction_msg = ('You fumble around in the darkness...your hands meet familiar structures as you feel your way around. You realize you\'re back where you started before you moved.')
cardinal_directions = ['N', 'E', 'S', 'W']

# Dictionary for player values -- stored in global scope.
player = {
    'name': '',
    'gold': 0,
    'health': 125,
    'room_num': 1
}

# Rooms dictionary -- the purpose will be to store the contents of each room.
Rooms_list = [
    # Room 1
    {
        'room_num': 1,
        'message':
        '''
    ----------
    | Room 1 |
    |----------------------------------------------------------------
    | You stumble into a hole in the ground. When you               |
    | shake off the dirt and leaves you realize you are in          |
    | the entrance to a cave that looks man made. As you            |
    | take a look around, you decide it might be fun to explore.    |
    -----------------------------------------------------------------
        ''',
        'valid_directions': ['E', 'S'],
        'north_destination': lambda: print(invalid_direction_msg),
        'east_destination': lambda: update_player_room(4),
        'south_destination': lambda: update_player_room(2),
        'west_destination': lambda: print(invalid_direction_msg)
    },

    # Room 2
    {
        'room_num': 2,
        'message':
        '''
    ----------
    | Room 2 |
    |------------------------------------------------------------
    | You have entered the throne room. In the middle           |
    | of the room there is a giant wooden throne with           |
    | intricate carvings. As you take a closer look at the      |
    | carvings, you see that they show trolls chasing humans.   |
    | Hmmm, maybe this is not a great place to stop for a rest. |
    -------------------------------------------------------------
        ''',
        'valid_directions': ['N', 'E'],
        'north_destination': lambda: update_player_room(1),
        'east_destination': lambda: update_player_room(3),
        'south_destination': lambda: print(invalid_direction_msg),
        'west_destination': lambda: print(invalid_direction_msg)
    },

    # Room 3
    {
        'room_num': 3,
        'message':
        '''
    ----------
    | Room 3 |
    |------------------------------------------------------------
    | You have entered an abandoned pub. There are piles        |
    | of dirty dishes and empty beer mugs all over the place.   |
    | You hear someone coming and duck behind a table to hide.  |
    -------------------------------------------------------------
        ''',
        'valid_directions': ['S', 'E'],
        'north_destination': lambda: print(invalid_direction_msg),
        'east_destination': lambda: update_player_room(5),
        'south_destination': lambda: update_player_room(2),
        'west_destination': lambda: print(invalid_direction_msg)
    },

    # Room 4
    {
        'room_num': 4,
        'message':
        '''
    ----------
    | Room 4 |
    |----------------------------------------------------------------
    | You have entered a huge storage room filled with empty boxes. |
    | Looking at the side of one box, you see ACME Wyvern food      |
    | You better get out of here before you end up on the menu.     |
    -----------------------------------------------------------------
        ''',
        'valid_directions': ['E', 'S', 'W'],
        'north_destination': lambda: print(invalid_direction_msg),
        'east_destination': lambda: update_player_room(6),
        'south_destination': lambda: update_player_room(5),
        'west_destination': lambda: update_player_room(1)
    },

    # Room 5
    {
        'room_num': 5,
        'message':
        '''
    ----------
    | Room 5 |
    |----------------------------------------------------------------------------------------
    | You have no idea where you are -- you know for sure you\'ve come across another room   |
    | Stumbling in the darkness, the only stimulus you have is a cool, damp breeze blowing  |
    | towards you, through what must be multiple exits. This will be a hard decision...     |
    -----------------------------------------------------------------------------------------
        ''',
        'valid_directions': ['N', 'E', 'W'],
        'north_destination': lambda: update_player_room(4),
        'east_destination': lambda: update_player_room(6),
        'south_destination': lambda: print(invalid_direction_msg),
        'west_destination': lambda: update_player_room(3)
    },

    # Room 6
    {
        'room_num': 6,
        'message':
        '''
    ----------
    | Room 6 |
    |------------------------------------------------------------------------
    | You hurriedly escape into what appears to be a panic room.            |
    | Looking at your surroundings, it\'s obvious someone left in a hurry.   |
    | This seems to be near the end of the cave...it\'s clear there\'s a      |
    | door at the end of the corridor.                                      |
    -------------------------------------------------------------------------
        ''',
        'valid_directions': ['E', 'W'],
        'north_destination': lambda: print(invalid_direction_msg),
        'east_destination': lambda: update_player_room(4),
        'south_destination': lambda: print(invalid_direction_msg),
        'west_destination': lambda: update_player_room(5)
    }
]

def update_player_gold(gold: int):
    player['gold'] = gold

def update_player_health(health: int):
    player['health'] = health

def update_player_room(room_num: int):
    player['room_num'] = room_num

def add_player_gold(gold: int):
    update_player_gold(player['gold'] + gold)

def damage_player(damage: int):
    new_health = player['health'] - damage
    new_health = 0 if new_health < 0 else new_health
    update_player_health(new_health)
    is_game_over()

def print_status():
    '''
    This function print's the players current status.

    Params
    ------
    player: dict
        A dictionary for the player that stores the player's current attributes
    '''
    gold = player['gold']
    health = player['health']

    if gold > 2 * health:
        print('\nYou are rich with ' + str(gold) + ' gold, but your health is only ' + str(health) + '\n')
    elif health > 2 * gold:
        print('\nYou are strong with ' + str(health) + ' health, but you only have ' + str(gold) + ' gold\n')
    else:
        print('\nYour health is ' + str(health) + ' and you have ' + str(gold) + ' gold\n')

def find_treasure(max_gold: int) -> int:
    '''
    This function calculates how much treasure is found by generating a random gold amount between 1 and the max number of gold coins.

    Params
    ------
    max_gold : int
        The maximum amount of gold possible to find.

    Returns
    -------
    int
        The actual amount of gold the user found.
    '''
    gold = random.randint(1, max_gold)
    add_player_gold(gold)

    if gold < max_gold / 2:
        print('\nYou find ' + str(gold) + ' gold pieces on the floor.\n')
    else:
        print('\nYou find a huge mound of ' + str(gold) + ' gold pieces!\n')

def search_for_treasure() -> bool:
    valid_responses = ['yes', 'no', 'y', 'n']

    user_choice = input('You see something shiny, would you like to search for treasure? (Y/N)\n').lower()

    while user_choice not in valid_responses:
        user_choice = input('Sorry that\'s not a valid response, please type either (Y/N):\n')

    return user_choice == 'yes' or user_choice == 'y'

def eat_food(food):
    '''
    This function allows the player to eat one of the optional food items which will update their health and print a message.

    Params
    ------
    food : int
        The food item being chosen. This number corresponds to the global variables we've created, however, there is a base case if the user attempts to eat
        nothing.
    health : int
        The player's current health prior to eating.
    '''
    health = player['health']
    # Check which food the user is eating then update their health and print a message about what they ate
    if food == CANDY:
        health += CANDY_RESTORE
        print('\nYou find a half eaten energy bar on the floor which restores your health by ' + str(CANDY_RESTORE) + '\n')

    elif food == STEAK:
        health += STEAK_RESTORE
        print('\nYou find a warm and juicy steak on the table which restores your health by ' + str(STEAK_RESTORE) + '\n')

    elif food == POTION:
        health += POTION_RESTORE
        print('\nYou find a green glowing potion on a shelf which restores your health by ' + str(POTION_RESTORE) + '\n')

    else:
        print('\nYour stomach is rumbling, but there is nothing to eat\n')

    if health > 100:
        health = 100

    update_player_health(health)

def fight_battle(creature: int):
    '''
    This function simulates battle with one of the optional creatures. A damage value will be generated based on each creatures max damage and a message will
    be given to the user regarding their battle.

    Params
    ------
    creature : int
        The creature that you are fighting. This number corresponds to the global variables we've created, however, there is a base case if the user attempts
        to fight nothing.

    Returns
    -------
    int
        The damage dealt to the player during the battle.
    '''

    damage = 0
    if creature == BUNNY:
        damage = random.randint(1, BUNNY_DAMAGE)
        print('\nYou trip over a cute bunny, dealing ' + str(damage) + ' damage to your health.\n')

    elif creature == DWARF:
        damage = random.randint(1, DWARF_DAMAGE)
        print('\nA drunken dwarf hits you with a beer mug and does ' + str(damage) + ' damage to your health.\n')

    elif creature == TROLL:
        damage = random.randint(1, TROLL_DAMAGE)
        print('\nAn angry troll takes a swing at you and does ' + str(damage) + ' damage to your health.\n')
    elif creature == WYVERN:
        damage = random.randint(1, WYVERN_DAMAGE)
        print('Chills run down your spine as you notice a polymorphic creature standing in your way --')
        print('it has wings like a bat and a tail like a scorpion -- the head is that of a lion.')
        print('\nThe Wyvern pierces you with their stinger, dealing ' + str(damage) + ' damage to your health.\n')
    elif creature == BAT:
        damage = random.randint(1, BAT_DAMAGE)
        print('A high-pitched shriek indicates the presence of a bat! This creature seems unsure of itself...')
        print('\nThe Bat dives blindly towards you, dealing ' + str(damage) + ' damage to your health.\n')
    elif creature == GAMBLER:
        damage = random.randint(0, GAMBLER_DAMAGE)
        print('A dwarf-like creature with beady eyes smiles at you -- you hear him snicker menacingly as he begins to shake a pair of magical dice.')
        print('\nThe Gambler rolls his dice -- throwing them at you to deal ' + str(damage) + ' damage to your health.\n')
    elif creature == TROGG:
        damage = random.randint(20, TROGG_DAMAGE)
        print('A nasty, short, hairy little monster with razor sharp incisors hisses at you from across the room')
        print('\nIt lunges at you -- powerful claws tear into your skin, dealing ' + str(damage) + ' damage to your health.\n')
    else:
        print('\nIt is ghostly quiet here, you must be alone\n')

    # Damage the player
    if damage > 0:
        damage_player(damage)
    elif damage == 0:
        print('Well...gambling doesn\'t always pay off, now does it?')

def get_direction() -> str:
    '''
    This function gets what direction the user would like to travel.

    Returns
    -------
    str
        User choice of direction between one of the following values (N, S, E, W)
    '''
    optional_directions = ['N', 'S', 'E', 'W']
    direction = input('\nWhat direction would you like to go? (N, S, E, W): ').upper()

    while direction not in optional_directions:
        print('Sorry, you can not go that way...\n')
        direction = input('What direction would you like to go? (N, S, E, W): ').upper()

    return direction

def death_event():
    print('You feel the life leaving your body as your health reaches ' + str(player['health']) + ' -- this is the end of ' + (player['name']))
    exit()

def win_event():
    print('You empty your pockets and discover ' + str(player['gold']) + ' gold coins!')
    print('This will pay for your tuition, and your beer! w00t!')
    print('You crawl out of the cave and blink your eyes to')
    print('adjust to the bright sunshine. Congratulations!')
    print('Try not to spend it all in one place!')
    exit()

def is_game_over():
    gold = player['gold']
    health = player['health']

    if gold >= 100:
        game_over = True
        did_player_win = True

    elif health == 0:
        game_over = True
        did_player_win = False

    else:
        game_over = False
        did_player_win = False

    return game_over, did_player_win

def room_1():
    '''
    This function visits the first room in the game and starts off the story.
    '''

    fight_battle(0)

    if player['health'] > 0:
        if search_for_treasure():
            find_treasure(10)

    print_status()

def room_2():
    '''
    This function visits the second room in the game.
    '''
    fight_battle(TROLL)

    if player['health'] > 0:
        if search_for_treasure():
            fight_battle(GAMBLER)
        else:
            print('Instead of treasure.....\n')
            eat_food(CANDY)

    print_status()

def room_3():
    '''
    This function visits the third room in the game.
    '''
    fight_battle(DWARF)

    if player['health'] > 0:
        if search_for_treasure():
            find_treasure(15)

    print_status()

def room_4():
    '''
    This function visits the fourth room in the game.
    '''

    rand = random.randint(1,100)
    if rand >= 50:
        fight_battle(WYVERN)
    else:
        eat_food(STEAK)

    if player['health'] > 0:

        if search_for_treasure():
            find_treasure(20)
        else:
            fight_battle(GAMBLER)

    print_status()

def room_5():
    '''
    This function visits the fifth room in the game.
    '''
    rand = random.randint(1, 100)
    if rand >= 50:
        fight_battle(BAT)
    else:
        eat_food(food)

    if player['health'] > 0:
        if search_for_treasure():
            find_treasure(30)

    print_status()

def room_6():
    '''
    This function visits the sixth room in the game.
    '''

    door_value = random.randint(0,10)

    # ONLY IF 0
    if door_value == 0:
        print('...you are extremely lucky and find that it opens, behind it is everything you were searching for!\n')
        add_player_gold(TUITION)
        win_event()

    # Range [1, 3]
    elif door_value <= 3:
        print('...there appears to be something shiny and heavy blocking it...\n')
        find_treasure(30)

    # Range [4, 6]
    elif door_value <= 6:
        print('Your stomach growls...somehow the door understands this and inexplicably spits out some food.')
        eat_food(food)

    # Everything else [7, 10]
    else:
        print('...it\'s guarded by a trogg!')
        fight_battle(TROGG)

def get_current_room(room_num: int):
    for room in Rooms_list:
        if room['room_num'] == room_num:
            return room
    return None

def validate_direction(direction: str, valid_directions_list) -> bool:
    if direction in valid_directions_list:
        return True
    else:
        return False

def move_to_destination(direction: str, room):
    if direction == 'N':
        room['north_destination']()
    elif direction == 'E':
        room['east_destination']()
    elif direction == 'S':
        room['south_destination']()
    elif direction == 'W':
        room['west_destination']()

room_map = {
    1: lambda : room_1(),
    2: lambda : room_2(),
    3: lambda : room_3(),
    4: lambda : room_4(),
    5: lambda : room_5(),
    6: lambda : room_6(),
}

if __name__ == '__main__':

    player['name'] = input('Before we begin...what is your name, traveler?\n')

    # Main game loop
    game_over = False
    did_player_win = False
    while not game_over:

        # Get the current room
        current_room = get_current_room(player['room_num'])

        # Print room message
        print(current_room['message'])

        # run the event for that room
        room_map[current_room['room_num']]()

        # Check if game is over
        game_over, did_player_win = is_game_over()

        if not game_over:

            is_valid_direction = False
            direction = ''

            while not is_valid_direction:

                # Get player's choice for the next room
                direction = get_direction()

                # Validate the direction
                is_valid_direction = validate_direction(direction, current_room['valid_directions'])

                if not is_valid_direction:
                    if direction in cardinal_directions:
                        move_to_destination(direction, current_room)
                    else:
                        print('Try again, except this time choose a cardinal direction...(N, E, S, W).\n')

            # Move the player to the next room
            move_to_destination(direction, current_room)

    # End the game
    if is_game_over():
        win_event()
    else:
        death_event()


from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import *

import numpy as np
import random

from globals import *

from sklearn import preprocessing

def average(x: list[float]) -> float:
    return sum(x) / len(x) if len(x) else 0.0

class Category(Enum):
    INCREASED = 0
    DECREASED = 1
    EXTINCT = 2

@dataclass
class InitialMassHandler:
    avg_unit_pis: float
    avg_unit_hva: float

    def get_initial_masses(self, read_ratio: str) -> tuple[float, float]:
        hva_units, pis_units = tuple([int(term)
                                      for term in read_ratio.split(':')])
        return self.avg_unit_pis * pis_units, self.avg_unit_hva * hva_units

@dataclass
class RawData:
    pis_initial: float
    hva_initial: float
    nutrient_ratio: float
    shade: int

    pis_final: float
    hva_final: float

    def transform_features(self, transform_functions: TransformFunctions) -> np.array:
        new_features: list = []

        new_features.extend(transform_functions.transform(self.pis_initial))
        new_features.extend(transform_functions.transform(self.hva_initial))
        new_features.extend(transform_functions.transform(self.nutrient_ratio))
        new_features.extend(transform_functions.transform(self.shade))

        np_new_features: np.ndarray = preprocessing.PolynomialFeatures(
            degree=transform_functions.poly_degree).fit_transform(np.array([new_features]))

        np_new_features = np_new_features.flatten()

        return np_new_features

    def categorize(self):
        self.pis_category: Category = None
        self.hva_category: Category = None

        if self.pis_final <= EXTINCTION_THRESHOLD:
            self.pis_category = Category.EXTINCT
        elif self.pis_final >= self.pis_initial:
            self.pis_category = Category.INCREASED
        else:
            self.pis_category = Category.DECREASED

        if self.hva_final <= EXTINCTION_THRESHOLD:
            self.hva_category = Category.EXTINCT
        elif self.hva_final >= self.hva_initial:
            self.hva_category = Category.INCREASED
        else:
            self.hva_category = Category.DECREASED

        if self.hva_category == Category.EXTINCT:
            self.hva_category = Category.DECREASED

@dataclass
class DataContainer:
    data_list: list[RawData]
    psi_bar: np.ndarray = None
    y: np.ndarray = None
    scaler: preprocessing.MinMaxScaler = None
    classification: str = None

    def transform_features(self, transform_functions: TransformFunctions) -> np.ndarray:
        if not self.classification:
            self.y = np.array([[point.pis_final, point.hva_final]
                               for point in self.data_list])
        else:
            self.drop_zeroes()
            self.categorize_y()
        self.psi_bar = np.array([point.transform_features(
            transform_functions) for point in self.data_list])

    def categorize_y(self):
        for point in self.data_list:
            point.categorize()
        self.y = np.array([[point.pis_category.value, point.hva_category.value]
                           for point in self.data_list])

    def get_features_and_classes(self, transform_functions: TransformFunctions, normalize: bool = False, shuffle: bool = False) -> tuple[np.ndarray, np.ndarray]:
        # if shuffle and not self.classification:
        #     random.Random(SEED).shuffle(self.data_list)
        self.transform_features(transform_functions)

        if shuffle:
            combined = np.hstack((self.psi_bar, self.y))
            random.Random(SEED).shuffle(combined)
            self.psi_bar = combined[:, :-2]
            self.y = combined[:, -2:]
            # shuffler = np.random.permutation(len(self.y))
            # self.psi_bar = self.psi_bar[shuffler]
            # self.y = self.y[shuffler]

        if normalize:
            self.scaler = preprocessing.MinMaxScaler()
            self.psi_bar = self.scaler.fit_transform(self.psi_bar)

        return self.psi_bar, self.y

    def drop_zeroes(self):
        col = self.classification
        if col == 'PIS':
            new_data_list: list[RawData] = []
            for point in self.data_list:
                if point.pis_initial != 0:
                    new_data_list.append(point)
            self.data_list = new_data_list
        elif col == 'HVA':
            new_data_list: list[RawData] = []
            for point in self.data_list:
                if point.hva_initial != 0:
                    new_data_list.append(point)
            self.data_list = new_data_list
        else:
            print('No changes made.')
            return

@dataclass
class TransformFunctions:
    poly_degree: int
    functions: list[Callable]

    def __post_init__(self):
        self.functions.append(lambda x: x)

    def transform(self, feature: float) -> list[float]:
        return [func(feature) for func in self.functions]


# -*- coding: utf-8 -*-
from metadata import tvshow_meta, season_episodes_meta, all_episodes_meta
from apis.trakt_api import trakt_fetch_collection_watchlist, trakt_get_hidden_items, trakt_get_my_calendar
from modules import kodi_utils, settings, watched_status as ws
from modules.utils import jsondate_to_datetime, adjust_premiered_date, make_day, get_datetime, title_key, date_difference, make_thread_list_enumerate
# logger = kodi_utils.logger

remove_meta_keys, set_view_mode, focus_index, external_browse = kodi_utils.remove_meta_keys, kodi_utils.set_view_mode, kodi_utils.focus_index, kodi_utils.external_browse
add_items, set_content, set_sort_method, end_directory = kodi_utils.add_items, kodi_utils.set_content, kodi_utils.set_sort_method, kodi_utils.end_directory
ls, make_listitem, build_url, dict_removals, sys = kodi_utils.local_string, kodi_utils.make_listitem, kodi_utils.build_url, kodi_utils.episode_dict_removals, kodi_utils.sys
get_art_provider, show_specials, calendar_sort_order, ignore_articles = settings.get_art_provider, settings.show_specials, settings.calendar_sort_order, settings.ignore_articles
nextep_content_settings, nextep_display_settings, calendar_focus_today = settings.nextep_content_settings, settings.nextep_display_settings, settings.calendar_focus_today
metadata_user_info, watched_indicators_info, show_unaired_info = settings.metadata_user_info, settings.watched_indicators, settings.show_unaired
date_offset_info, default_all_episodes = settings.date_offset, settings.default_all_episodes
single_ep_display_title, single_ep_format = settings.single_ep_display_title, settings.single_ep_format
tv_meta_function, season_meta_function, all_episodes_meta_function = tvshow_meta, season_episodes_meta, all_episodes_meta
adjust_premiered_date_function, jsondate_to_datetime_function = adjust_premiered_date, jsondate_to_datetime
date_difference_function, make_day_function, title_key_function, get_datetime_function = date_difference, make_day, title_key, get_datetime
get_progress_percent, get_watched_status, get_watched_info, get_bookmarks = ws.get_progress_percent, ws.get_watched_status_episode, ws.get_watched_info_tv, ws.get_bookmarks
get_in_progress_episodes, get_next_episodes, get_recently_watched = ws.get_in_progress_episodes, ws.get_next_episodes, ws.get_recently_watched
string, david_str, trakt_str, watched_str, unwatched_str, extras_str, options_str, clearprog_str = str, ls(32036), ls(32037), ls(32642), ls(32643), ls(32645), ls(32646), ls(32651)
upper = string.upper
poster_empty, fanart_empty = kodi_utils.empty_poster, kodi_utils.addon_fanart
run_plugin, unaired_label, tmdb_poster = 'RunPlugin(%s)', '[COLOR red][I]%s[/I][/COLOR]', 'https://image.tmdb.org/t/p/'

def build_episode_list(params):
	def _process():
		for item in episodes_data:
			try:
				cm = []
				listitem = make_listitem()
				set_properties = listitem.setProperties
				cm_append = cm.append
				item_get = item.get
				season, episode, ep_name = item_get('season'), item_get('episode'), item_get('title')
				episode_date, premiered = adjust_premiered_date_function(item_get('premiered'), adjust_hours)
				playcount, overlay = get_watched_status(watched_info, string(tmdb_id), season, episode)
				thumb = item_get('thumb', None) or fanart
				item.update({'trailer': trailer, 'tvshowtitle': title, 'premiered': premiered, 'genre': genre, 'duration': duration, 'mpaa': mpaa, 'studio': studio,
							'playcount': playcount, 'overlay': overlay})
				options_params = build_url({'mode': 'options_menu_choice', 'content': 'episode', 'tmdb_id': tmdb_id, 'season': season, 'episode': episode,
											'poster': show_poster, 'is_widget': is_widget})
				extras_params = build_url({'mode': 'extras_menu_choice', 'tmdb_id': tmdb_id, 'media_type': 'tvshow', 'is_widget': is_widget})
				url_params = build_url({'mode': 'play_media', 'media_type': 'episode', 'tmdb_id': tmdb_id, 'season': season, 'episode': episode})
				if not episode_date or current_date < episode_date:
					if not show_unaired: continue
					if season != 0:
						display, unaired = unaired_label % ep_name, True
						item['title'] = display
				else: display, unaired = ep_name, False
				cm_append((extras_str, run_plugin % extras_params))
				cm_append((options_str, run_plugin % options_params))
				clearprog_params, unwatched_params, watched_params = '', '', ''
				if not unaired:
					if playcount:
						if hide_watched: continue
						unwatched_params = build_url({'mode': 'mark_as_watched_unwatched_episode', 'action': 'mark_as_unwatched', 'tmdb_id': tmdb_id,
													'tvdb_id': tvdb_id, 'season': season, 'episode': episode,  'title': title, 'year': year})
						cm_append((unwatched_str % watched_title, run_plugin % unwatched_params))
					else:
						watched_params = build_url({'mode': 'mark_as_watched_unwatched_episode', 'action': 'mark_as_watched', 'tmdb_id': tmdb_id,
													'tvdb_id': tvdb_id, 'season': season, 'episode': episode,  'title': title, 'year': year})
						cm_append((watched_str % watched_title, run_plugin % watched_params))
					progress = get_progress_percent(bookmarks, tmdb_id, season, episode)
					if progress:
						clearprog_params = build_url({'mode': 'watched_unwatched_erase_bookmark', 'media_type': 'episode', 'tmdb_id': tmdb_id,
													'season': season, 'episode': episode, 'refresh': 'true'})
						cm_append((clearprog_str, run_plugin % clearprog_params))
						set_properties({'watchedprogress': progress, 'resumetime': progress, 'david_in_progress': 'true'})
				listitem.setLabel(display)
				listitem.addContextMenuItems(cm)
				listitem.setArt({'poster': show_poster, 'fanart': fanart, 'thumb': thumb, 'icon':thumb, 'banner': banner, 'clearart': clearart, 'clearlogo': clearlogo,
								'landscape': thumb, 'season.poster': season_poster, 'tvshow.poster': show_poster, 'tvshow.clearart': clearart, 'tvshow.clearlogo': clearlogo})
				listitem.setCast(cast + item_get('guest_stars', []))
				listitem.setUniqueIDs({'imdb': imdb_id, 'tmdb': string(tmdb_id), 'tvdb': string(tvdb_id)})
				listitem.setInfo('video', remove_meta_keys(item, dict_removals))
				if is_widget:
					set_properties({'david_widget': 'true', 'david_playcount': string(playcount), 'david_extras_params': extras_params, 'david_options_params': options_params,
									'david_unwatched_params': unwatched_params, 'david_watched_params': watched_params, 'david_clearprog_params': clearprog_params})
				else: set_properties({'david_widget': 'false'})
				yield (url_params, listitem, False)
			except: pass
	handle = int(sys.argv[1])
	try:
		item_list = []
		append = item_list.append
		meta_user_info, watched_indicators, watched_info, show_unaired, is_widget, fanart_enabled, hide_watched, current_date, adjust_hours, bookmarks = get_episode_info()
		poster_main, poster_backup, fanart_main, fanart_backup, clearlogo_main, clearlogo_backup = get_art_provider()
		all_episodes = True if params.get('season') == 'all' else False
		meta = tv_meta_function('tmdb_id', params.get('tmdb_id'), meta_user_info, current_date)
		meta_get = meta.get
		tmdb_id, tvdb_id, imdb_id = meta_get('tmdb_id'), meta_get('tvdb_id'), meta_get('imdb_id')
		title, year, rootname = meta_get('title'), meta_get('year'), meta_get('rootname')
		show_poster = meta_get(poster_main) or meta_get(poster_backup) or poster_empty
		fanart = meta_get(fanart_main) or meta_get(fanart_backup) or fanart_empty
		clearlogo = meta_get(clearlogo_main) or meta_get(clearlogo_backup) or ''
		if fanart_enabled: banner, clearart, landscape = meta_get('banner'), meta_get('clearart'), meta_get('landscape')
		else: banner, clearart, landscape = '', '', ''
		try:
			poster_path = [i['poster_path'] for i in meta_get('season_data') if i['season_number'] == int(params['season'])][0]
			season_poster =  ''.join([tmdb_poster, meta_user_info['image_resolution']['poster'], poster_path]) if poster_path is not None else show_poster
		except: season_poster = show_poster
		cast, mpaa, duration = meta_get('cast', []), meta_get('mpaa'), meta_get('duration')
		trailer, genre, studio = string(meta_get('trailer')), meta_get('genre'), meta_get('studio')
		tvshow_plot = meta_get('plot')
		watched_title = trakt_str if watched_indicators == 1 else david_str
		if all_episodes:
			episodes_data = all_episodes_meta_function(meta, meta_user_info)
			if not show_specials(): episodes_data = [i for i in episodes_data if not i['season'] == 0]
		else: episodes_data = season_meta_function(params['season'], meta, meta_user_info)
		add_items(handle, list(_process()))
	except: pass
	set_content(handle, 'episodes')
	set_sort_method(handle, 'episodes')
	end_directory(handle, False if is_widget else None)
	if not is_widget: set_view_mode('view.episodes', 'episodes')

def build_single_episode(list_type, data, handle):
	def _sort_results(items):
		if list_type_starts_with('next_episode'):
			def func(function):
				if sort_key == 'david_name': return title_key_function(function, ignore_articles_setting)
				elif sort_key == 'david_last_played': return jsondate_to_datetime_function(function, resformat)
				else: return function
			sort_key = nextep_settings['sort_key']
			sort_direction = nextep_settings['sort_direction']
			if nextep_settings['sort_airing_today_to_top']:
				airing_today = [i for i in items
								if date_difference_function(current_date, jsondate_to_datetime_function(i[1].getProperty('david_first_aired'), '%Y-%m-%d').date(), 0)]
				airing_today = sorted(airing_today, key=lambda i: i[1].getProperty('david_first_aired'))
				remainder = [i for i in items if not i in airing_today]
				remainder = sorted(remainder, key=lambda i: func(i[1].getProperty(sort_key)), reverse=sort_direction)
				unaired = [i for i in remainder if i[1].getProperty('david_unaired') == 'true']
				aired = [i for i in remainder if not i in unaired]
				remainder = aired + unaired
				items = airing_today + remainder
			else:
				items = sorted(items, key=lambda i: func(i[1].getProperty(sort_key)), reverse=sort_direction)
				unaired = [i for i in items if i[1].getProperty('david_unaired') == 'true']
				aired = [i for i in items if not i in unaired]
				items = aired + unaired
		else:
			items.sort(key=lambda k: int(k[1].getProperty('david_sort_order')))
			if list_type in ('trakt_calendar', 'trakt_recently_aired'):
				if list_type == 'trakt_calendar': reverse = calendar_sort_order() == 0
				else: reverse = True
				items.sort(key=lambda k: int(k[1].getProperty('david_sort_order')))
				items = sorted(items, key=lambda i: i[1].getProperty('david_first_aired'), reverse=reverse)
		return items
	def _process(item_position, ep_data):
		try:
			cm = []
			listitem = make_listitem()
			set_properties = listitem.setProperties
			cm_append = cm.append
			ep_data_get = ep_data.get
			meta = tv_meta_function('trakt_dict', ep_data_get('media_ids'), meta_user_info, current_date)
			if not meta: return
			meta_get = meta.get
			tmdb_id, tvdb_id, imdb_id = meta_get('tmdb_id'), meta_get('tvdb_id'), meta_get('imdb_id')
			title, year, rootname = meta_get('title'), meta_get('year'), meta_get('rootname')
			orig_season, orig_episode = ep_data_get('season'), ep_data_get('episode')
			show_poster = meta_get(poster_main) or meta_get(poster_backup) or poster_empty
			fanart = meta_get(fanart_main) or meta_get(fanart_backup) or fanart_empty
			clearlogo = meta_get(clearlogo_main) or meta_get(clearlogo_backup) or ''
			if fanart_enabled: banner, clearart, landscape = meta_get('banner'), meta_get('clearart'), meta_get('landscape')
			else: banner, clearart, landscape = '', '', ''
			try:
				poster_path = [i['poster_path'] for i in meta_get('season_data') if i['season_number'] == orig_season][0]
				season_poster =  ''.join([tmdb_poster, meta_user_info['image_resolution']['poster'], poster_path]) if poster_path is not None else show_poster
			except: season_poster = show_poster
			cast, mpaa, duration, tvshow_plot = meta_get('cast', []), meta_get('mpaa'), meta_get('duration'), meta_get('plot')
			trailer, genre, studio = string(meta_get('trailer')), meta_get('genre'), meta_get('studio')
			if list_type_starts_with('next_episode'):
				season_data = meta_get('season_data')
				curr_season_data = [i for i in season_data if i['season_number'] == orig_season][0]
				if orig_episode >= curr_season_data['episode_count']: orig_season, orig_episode, new_season = orig_season + 1, 1, True
				else: orig_episode, new_season = orig_episode + 1, False
			episodes_data = season_meta_function(orig_season, meta, meta_user_info)
			try: item = [i for i in episodes_data if i['episode'] == orig_episode][0]
			except: return
			item_get = item.get
			season, episode, ep_name = item_get('season'), item_get('episode'), item_get('title')
			str_season_zfill2, str_episode_zfill2 = string(season).zfill(2), string(episode).zfill(2)
			episode_date, premiered = adjust_premiered_date_function(item_get('premiered'), adjust_hours)
			if not episode_date or current_date < episode_date:
				if list_type_starts_with('next_episode'):
					if not nextep_include_unaired: return
					if episode_date and new_season and not date_difference_function(current_date, episode_date, 7): return
				elif not show_unaired: return
				unaired = True
				set_properties({'david_unaired': 'true'})
			else:
				unaired = False
				set_properties({'david_unaired': 'false'})
			playcount, overlay = get_watched_status(watched_info, string(tmdb_id), season, episode)
			if display_title == 0: title_string = ''.join([title, ': '])
			else: title_string = ''
			if display_title in (0,1): seas_ep = ''.join([str_season_zfill2, 'x', str_episode_zfill2, ' - '])
			else: seas_ep = ''
			if list_type_starts_with('next_episode'):
				unwatched = ep_data_get('unwatched', False)
				if episode_date: display_premiered = make_day_function(current_date, episode_date, date_format)
				else: display_premiered == 'UNKNOWN'
				airdate = ''.join(['[[COLOR magenta]', display_premiered, '[/COLOR]] ']) if nextep_include_airdate else ''
				highlight_color = nextep_unwatched_color if unwatched else nextep_unaired_color if unaired else ''
				italics_open, italics_close = ('[I]', '[/I]') if highlight_color else ('', '')
				if highlight_color: episode_info = ''.join([italics_open, '[COLOR', highlight_color, ']', seas_ep, ep_name, '[/COLOR]', italics_close])
				else: episode_info = ''.join([italics_open, seas_ep, ep_name, italics_close])
				display = ''.join([airdate, upper(title_string), episode_info])
			elif list_type == 'trakt_calendar':
				if episode_date: display_premiered = make_day_function(current_date, episode_date, date_format)
				else: display_premiered == 'UNKNOWN'
				display = ''.join(['[', display_premiered, '] ', upper(title_string), seas_ep, ep_name])
				if unaired:
					displays = display.split(']')
					display = ''.join(['[COLOR red]', displays[0], '][/COLOR]', displays[1]])
			else:
				color_tags = ('[COLOR red]', '[/COLOR]') if unaired else ('', '')
				display = ''.join([upper(title_string), color_tags[0], seas_ep, ep_name, color_tags[1]])
			thumb = item_get('thumb', None) or fanart
			item.update({'trailer': trailer, 'tvshowtitle': title, 'premiered': premiered, 'genre': genre, 'duration': duration,
						'mpaa': mpaa, 'studio': studio, 'playcount': playcount, 'overlay': overlay, 'title': display})
			options_params = build_url({'mode': 'options_menu_choice', 'content': 'episode_single', 'tmdb_id': tmdb_id, 'season': season, 'episode': episode,
										'poster': show_poster, 'is_widget': is_widget})
			extras_params = build_url({'mode': 'extras_menu_choice', 'tmdb_id': tmdb_id, 'media_type': 'tvshow', 'is_widget': is_widget})
			url_params = build_url({'mode': 'play_media', 'media_type': 'episode', 'tmdb_id': tmdb_id, 'season': season, 'episode': episode})
			cm_append((extras_str, run_plugin % extras_params))
			cm_append((options_str, run_plugin % options_params))
			clearprog_params, unwatched_params, watched_params = '', '', ''
			if not unaired:
				if playcount:
					if hide_watched: return
					unwatched_params = build_url({'mode': 'mark_as_watched_unwatched_episode', 'action': 'mark_as_unwatched', 'tmdb_id': tmdb_id,
												'tvdb_id': tvdb_id, 'season': season, 'episode': episode,  'title': title, 'year': year})
					cm_append((unwatched_str % watched_title, run_plugin % unwatched_params))
				else:
					watched_params = build_url({'mode': 'mark_as_watched_unwatched_episode', 'action': 'mark_as_watched', 'tmdb_id': tmdb_id,
												'tvdb_id': tvdb_id, 'season': season, 'episode': episode,  'title': title, 'year': year})
					cm_append((watched_str % watched_title, run_plugin % watched_params))
				progress = get_progress_percent(bookmarks, tmdb_id, season, episode)
				if progress:
					clearprog_params = build_url({'mode': 'watched_unwatched_erase_bookmark', 'media_type': 'episode', 'tmdb_id': tmdb_id,
												'season': season, 'episode': episode, 'refresh': 'true'})
					cm_append((clearprog_str, run_plugin % clearprog_params))
					set_properties({'WatchedProgress': progress, 'resumetime': progress, 'david_in_progress': 'true'})
			listitem.setLabel(display)
			listitem.addContextMenuItems(cm)
			listitem.setArt({'poster': show_poster, 'fanart': fanart, 'thumb': thumb, 'icon':thumb, 'banner': banner, 'clearart': clearart, 'clearlogo': clearlogo,
				'landscape': thumb, 'season.poster': season_poster, 'tvshow.clearart': clearart, 'tvshow.clearlogo': clearlogo, 'tvshow.landscape': thumb, 'tvshow.banner': banner})
			listitem.setCast(cast + item_get('guest_stars', []))
			listitem.setUniqueIDs({'imdb': imdb_id, 'tmdb': string(tmdb_id), 'tvdb': string(tvdb_id)})
			listitem.setInfo('video', remove_meta_keys(item, dict_removals))
			set_properties({'david_widget': 'false', 'david_first_aired': premiered, 'david_name': '%s - %sx%s' % (title, str_season_zfill2, str_episode_zfill2)})
			if list_type_starts_with('next_episode'):
				last_played = ep_data_get('last_played', resinsert)
				set_properties({'david_last_played': last_played})
			else: set_properties({'david_sort_order': string(item_position)})
			if is_widget:
				set_properties({'david_widget': 'true', 'david_playcount': string(playcount), 'david_extras_params': extras_params, 'david_options_params': options_params,
								'david_unwatched_params': unwatched_params, 'david_watched_params': watched_params, 'david_clearprog_params': clearprog_params})
			append((url_params, listitem, False))
		except: pass
	try:
		item_list = []
		append = item_list.append
		list_type_starts_with = list_type.startswith
		meta_user_info, watched_indicators, watched_info, show_unaired, is_widget, fanart_enabled, hide_watched, current_date, adjust_hours, bookmarks = get_episode_info()
		display_title, date_format, art_keys, all_episodes = get_single_episode_info()
		show_all_episodes = all_episodes in (1, 2)
		poster_main, poster_backup, fanart_main, fanart_backup, clearlogo_main, clearlogo_backup = art_keys
		ignore_articles_setting = ignore_articles()
		watched_title = trakt_str if watched_indicators == 1 else david_str
		if list_type_starts_with('next_episode'):
			nextep_settings, nextep_disp_settings = nextep_content_settings(), nextep_display_settings()
			nextep_unaired_color, nextep_unwatched_color = nextep_disp_settings['unaired_color'], nextep_disp_settings['unwatched_color']
			nextep_include_airdate, nextep_include_unaired = nextep_disp_settings['include_airdate'], nextep_settings['include_unaired']
			if watched_indicators == 1: resformat, resinsert = '%Y-%m-%dT%H:%M:%S.%fZ', '2000-01-01T00:00:00.000Z'
			else: resformat, resinsert = '%Y-%m-%d %H:%M:%S', '2000-01-01 00:00:00'
		threads = list(make_thread_list_enumerate(_process, data))
		[i.join() for i in threads]
		item_list = _sort_results(item_list)
		add_items(handle, item_list)
	except: pass
	set_content(handle, 'episodes')
	end_directory(handle, cacheToDisc=False)
	if not is_widget: set_view_mode('view.episodes', 'episodes')
	if list_type == 'trakt_calendar' and calendar_focus_today():
		try:
			today = upper('[%s]' % ls(32849))
			index = max([i for i, x in enumerate([i[1].getLabel() for i in item_list]) if today in x])
			focus_index(index)
		except: pass

def get_episode_info():
	meta_user_info = metadata_user_info()
	watched_indicators = watched_indicators_info()
	watched_info = get_watched_info(watched_indicators)
	show_unaired = show_unaired_info()
	is_widget = external_browse()
	fanart_enabled = meta_user_info['extra_fanart_enabled']
	hide_watched = is_widget and meta_user_info['widget_hide_watched']
	current_date = get_datetime_function()
	adjust_hours = date_offset_info()
	bookmarks = get_bookmarks(watched_indicators, 'episode')
	return meta_user_info, watched_indicators, watched_info, show_unaired, is_widget, fanart_enabled, hide_watched, current_date, adjust_hours, bookmarks

def get_single_episode_info():
	display_title = single_ep_display_title()
	date_format = single_ep_format()
	art_keys = get_art_provider()
	all_episodes = default_all_episodes()
	return display_title, date_format, art_keys, all_episodes

def build_in_progress_episode():
	data = get_in_progress_episodes()
	build_single_episode('progress', data, int(sys.argv[1]))

def build_recently_watched_episode():
	data = get_recently_watched('episode')[0]
	build_single_episode('recently_watched', data, int(sys.argv[1]))

def build_next_episode():
	nextep_settings = nextep_content_settings()
	include_unwatched = nextep_settings['include_unwatched']
	indicators = watched_indicators_info()
	watched_info = get_watched_info(indicators)
	data = get_next_episodes(watched_info)
	if indicators == 1:
		list_type = 'next_episode_trakt'
		try:
			hidden_data = trakt_get_hidden_items('progress_watched')
			data = [i for i in data if not i['media_ids']['tmdb'] in hidden_data]
		except: pass
	else: list_type = 'next_episode_david'
	if include_unwatched:
		try: unwatched = [{'media_ids': i['media_ids'], 'season': 1, 'episode': 0, 'unwatched': True} for i in trakt_fetch_collection_watchlist('watchlist', 'tvshow')]
		except: unwatched = []
		data += unwatched
	build_single_episode(list_type, data, int(sys.argv[1]))

def build_my_calendar(params):
	recently_aired = params.get('recently_aired', None)
	data = trakt_get_my_calendar(recently_aired, get_datetime())
	list_type = 'trakt_recently_aired' if recently_aired else 'trakt_calendar'
	data = sorted(data, key=lambda k: (k['sort_title'], k['first_aired']), reverse=True)
	build_single_episode(list_type, data, int(sys.argv[1]))


class Programms:
    Company = "Mecrosoft"
    def SetData(self, name, phone,Company):
        self.name = name
        self.phone = phone
        self.Company = Company
    def getData(self):
        print(f"Compnay is :{self.Company}")
        print(f"Name is :{self.name}")
        print(f"phone NO is :{self.phone}")
a = Programms()
a.SetData("Muneeb",304630299,"Mecrosoft")
a.getData()


from __future__ import annotations

from typing import Optional

from range_typed_integers import u16

from skytemple_files.common.util import AutoString

class SsaHeader(AutoString):
    """
    The header of a read SSA file.
    Only contains information used during read-in of the data. Not used for saving.
    """

    layer_count: u16
    layer_list_pointer: Optional[int]
    trigger_pointer: Optional[int]
    actor_pointer: Optional[int]
    object_pointer: Optional[int]
    performer_pointer: Optional[int]
    events_pointer: Optional[int]
    position_marker_pointer: Optional[int]
    unk10_pointer: Optional[int]

    def __init__(
        self,
        layer_count: u16,
        layer_list_pointer: int,
        trigger_pointer: int,
        actor_pointer: int,
        object_pointer: int,
        performer_pointer: int,
        events_pointer: int,
        position_marker_pointer: int,
        unk10_pointer: int,
    ):
        # If the pointer of the following entry is the same, then this layer doesn't actually exist.
        self.layer_count = layer_count
        self.layer_list_pointer = (
            layer_list_pointer if layer_list_pointer != trigger_pointer else None
        )

        self.trigger_pointer = (
            trigger_pointer if trigger_pointer != actor_pointer else None
        )
        if self.layer_list_pointer is not None:
            self.trigger_end_pointer = actor_pointer

        self.actor_pointer = actor_pointer if actor_pointer != object_pointer else None
        if self.actor_pointer is not None:
            self.actor_end_pointer = object_pointer

        self.object_pointer = (
            object_pointer if object_pointer != performer_pointer else None
        )
        if self.object_pointer is not None:
            self.object_end_pointer = performer_pointer

        self.performer_pointer = (
            performer_pointer if performer_pointer != events_pointer else None
        )
        if self.performer_pointer is not None:
            self.performer_end_pointer = events_pointer

        self.events_pointer = (
            events_pointer if events_pointer != position_marker_pointer else None
        )
        if self.events_pointer is not None:
            self.events_end_pointer = position_marker_pointer

        self.position_marker_pointer = (
            position_marker_pointer
            if position_marker_pointer != unk10_pointer
            else None
        )
        if self.position_marker_pointer is not None:
            self.position_marker_end_pointer = unk10_pointer

        self.unk10_pointer = (
            unk10_pointer if unk10_pointer != layer_list_pointer else None
        )
        if self.unk10_pointer is not None:
            self.unk10_end_pointer = layer_list_pointer


import json
import os
import time
from multiprocessing import Process
import pickle
from config import DIC_AGENTS, DIC_ENVS
import sys

def check_all_workers_working(list_cur_p):
    for i in range(len(list_cur_p)):
        if not list_cur_p[i].is_alive():
            return i

    return -1

def downsample(path_to_log):
    path_to_pkl = os.path.join(path_to_log, "inter_0.pkl")
    with open(path_to_pkl, "rb") as f_logging_data:
        logging_data = pickle.load(f_logging_data)
    subset_data = logging_data[::10]
    os.remove(path_to_pkl)
    with open(path_to_pkl, "wb") as f_subset:
        pickle.dump(subset_data, f_subset)

def run_wrapper(dir, one_round, run_cnt, if_gui):
    model_dir = "model/" + dir
    records_dir = "records/" + dir
    model_round = one_round
    dic_path = {}
    dic_path["PATH_TO_MODEL"] = model_dir
    dic_path["PATH_TO_WORK_DIRECTORY"] = records_dir

    with open(os.path.join(records_dir, "agent.conf"), "r") as f:
        dic_agent_conf = json.load(f)
    with open(os.path.join(records_dir, "exp.conf"), "r") as f:
        dic_exp_conf = json.load(f)
    with open(os.path.join(records_dir, "traffic_env.conf"), "r") as f:
        dic_traffic_env_conf = json.load(f)

    dic_exp_conf["RUN_COUNTS"] = run_cnt
    dic_traffic_env_conf["IF_GUI"] = if_gui

    # dump dic_exp_conf
    with open(os.path.join(records_dir, "test_exp.conf"), "w") as f:
        json.dump(dic_exp_conf, f)

    if dic_exp_conf["MODEL_NAME"] in dic_exp_conf["LIST_MODEL_NEED_TO_UPDATE"]:
        dic_agent_conf["EPSILON"] = 0  # dic_agent_conf["EPSILON"]  # + 0.1*cnt_gen
        dic_agent_conf["MIN_EPSILON"] = 0

    agent_name = dic_exp_conf["MODEL_NAME"]
    # TODO with intersection id
    agent = DIC_AGENTS[agent_name](
        dic_agent_conf=dic_agent_conf,
        dic_traffic_env_conf=dic_traffic_env_conf,
        dic_path=dic_path,
        cnt_round=0,  # useless
        # intersection_id=str(i)
    )
    if 1:
        agent.load_network(model_round)

        path_to_log = os.path.join(dic_path["PATH_TO_WORK_DIRECTORY"], "test_round", model_round)
        if not os.path.exists(path_to_log):
            os.makedirs(path_to_log)
        env = DIC_ENVS[dic_traffic_env_conf["SIMULATOR_TYPE"]](path_to_log=path_to_log,
                                                               path_to_work_directory=dic_path[
                                                                   "PATH_TO_WORK_DIRECTORY"],
                                                               dic_traffic_env_conf=dic_traffic_env_conf)

        done = False
        state = env.reset()
        step_num = 0

        while not done and step_num < int(dic_exp_conf["RUN_COUNTS"] / dic_traffic_env_conf["MIN_ACTION_TIME"]):
            action_list = []
            for one_state in state:
                action = agent.choose_action(step_num, one_state)

                action_list.append(action)

            next_state, reward, done, _ = env.step(action_list)

            state = next_state
            step_num += 1
        env.bulk_log()
        env.end_sumo()
        if not __debug__:
            path_to_log = os.path.join(dic_path["PATH_TO_WORK_DIRECTORY"], "test_round",
                                       model_round)
            # print("downsample", path_to_log)
            downsample(path_to_log)
            # print("end down")

    # except:
    #    pass
    # import sys
    # sys.stderr.write("fail to test model_%"%model_round)
    # raise SystemExit(1)

    return

def run_inference(dir, model_file_list, RUN_COUNT):
    # dir = 'cityflow'
    model_dir = "model/" + dir
    records_dir = "records/" + dir
    dic_path = {}
    dic_path["PATH_TO_MODEL"] = model_dir
    dic_path["PATH_TO_WORK_DIRECTORY"] = records_dir

    with open(os.path.join(records_dir, "agent.conf"), "r") as f:
        dic_agent_conf = json.load(f)
    with open(os.path.join(records_dir, "exp.conf"), "r") as f:
        dic_exp_conf = json.load(f)
    with open(os.path.join(records_dir, "traffic_env.conf"), "r") as f:
        dic_traffic_env_conf = json.load(f)

    # dump dic_exp_conf
    with open(os.path.join(records_dir, "test_exp.conf"), "w") as f:
        json.dump(dic_exp_conf, f)

    if dic_exp_conf["MODEL_NAME"] in dic_exp_conf["LIST_MODEL_NEED_TO_UPDATE"]:
        dic_agent_conf["EPSILON"] = 0  # dic_agent_conf["EPSILON"]  # + 0.1*cnt_gen
        dic_agent_conf["MIN_EPSILON"] = 0

    path_to_log = os.path.join(dic_path["PATH_TO_WORK_DIRECTORY"], "test_round", 'log')
    if not os.path.exists(path_to_log):
        os.makedirs(path_to_log)

    dic_traffic_env_conf["SAVEREPLAY"] = True

    env = DIC_ENVS[dic_traffic_env_conf["SIMULATOR_TYPE"]](path_to_log=path_to_log,
                                                           path_to_work_directory=dic_path["PATH_TO_WORK_DIRECTORY"],
                                                           dic_traffic_env_conf=dic_traffic_env_conf)

    state = env.reset()
    agents = [None] * dic_traffic_env_conf['NUM_AGENTS']

    for i in range(dic_traffic_env_conf['NUM_AGENTS']):
        agent_name = dic_exp_conf["MODEL_NAME"]
        agent = DIC_AGENTS[agent_name](
            dic_agent_conf=dic_agent_conf,
            dic_traffic_env_conf=dic_traffic_env_conf,
            dic_path=dic_path,
            cnt_round=0,
            best_round=None,
            # intersection_id=str(i)
        )
        agent.load_network_(model_file_list[i])
        agents[i] = agent

    step_num = 0
    while step_num < RUN_COUNT:
        action_list = []
        for i in range(dic_traffic_env_conf["NUM_AGENTS"]):
            for one_state in state:
                one_state['cur_phase'][0] = str(one_state['cur_phase'][0])
                action = agents[i].choose_action(step_num, one_state)
                action_list.append(action)

        next_state, reward, done, _ = env.step(action_list, False)

        state = next_state
        step_num += 1

def main(memo=None):
    # run name
    if not memo:
        memo = "New_sumo/pipeline_template/"

    # test run_count
    run_cnt = 3600

    # add the specific rounds in the given_round_list, like [150, 160]
    # if none, test all the round
    given_round_list = [63]

    given_traffic_list = [
        # "cross.2phases_rou01_equal_650.xml",
        # "cross.2phases_rou01_equal_600.xml",
        # "cross.2phases_rou01_equal_550.xml",
        # "cross.2phases_rou01_equal_500.xml",
        # "cross.2phases_rou01_equal_450.xml",
        # "cross.2phases_rou01_equal_400.xml",
        # "cross.2phases_rou01_equal_350.xml",
        # "cross.2phases_rou01_equal_300.xml",
    ]

    if_gui = True

    multi_process = True
    n_workers = 100
    process_list = []
    for traffic in os.listdir("records/" + memo):
        print(traffic)
        if not ".xml" in traffic:
            continue

        if traffic != "cross.2phases_rou01_equal_100.xml_12_12_19_49_03":
            continue
        test_round_dir = os.path.join("records", memo, traffic, "test_round")
        if os.path.exists(test_round_dir):
            print("exist")
            # continue
        # if traffic[0:-15] not in given_traffic_list:
        #    continue

        work_dir = os.path.join(memo, traffic)

        if given_round_list:
            for one_round in given_round_list:
                _round = "round_" + str(one_round)
                if multi_process:
                    p = Process(target=run_wrapper, args=(work_dir, _round, run_cnt, if_gui))
                    process_list.append(p)
                else:
                    run_wrapper(work_dir, _round, run_cnt, if_gui)
        else:
            train_round_dir = os.path.join("records", memo, traffic, "train_round")
            for one_round in os.listdir(train_round_dir):
                if "round" not in one_round:
                    continue

                if multi_process:
                    p = Process(target=run_wrapper, args=(work_dir, one_round, run_cnt, if_gui))
                    process_list.append(p)
                else:
                    run_wrapper(work_dir, one_round, run_cnt, if_gui)

    if multi_process:
        i = 0
        list_cur_p = []
        for p in process_list:
            if len(list_cur_p) < n_workers:
                print(i)
                p.start()
                list_cur_p.append(p)
                i += 1
            if len(list_cur_p) < n_workers:
                continue

            idle = check_all_workers_working(list_cur_p)

            while idle == -1:
                time.sleep(1)
                idle = check_all_workers_working(
                    list_cur_p)
            del list_cur_p[idle]

        for p in list_cur_p:
            p.join()

if __name__ == "__main__":
    # main()
    model_file_list = ['./model/test/round_216_inter_0.h5', './model/test/round_216_inter_1.h5',
                       './model/test/round_216_inter_2.h5', './model/test/round_216_inter_3.h5',
                       './model/test/round_216_inter_4.h5', './model/test/round_216_inter_5.h5']

    dir = 'test/anon_1_6_300_0.3_synthetic.json_08_05_19_55_50'
    NUM_COUNT = 700

    run_inference(dir, model_file_list, NUM_COUNT)