import os
import sentencepiece as spm
import tensorflow as tf
from tensorflow.data.experimental import AUTOTUNE
from tensorflow.keras.losses import SparseCategoricalCrossentropy
from tensorflow.keras.metrics import Mean, SparseCategoricalAccuracy
from tensorflow.keras.optimizers import Adam
from sklearn.model_selection import train_test_split
import numpy as np
import matplotlib.pyplot as plt
import time
import datetime

from models.Transformer.layers import *
from utils.utils import *
from models.Transformer.layers import Transformer

BATCH_SIZE = 64
TR_TE_RATIO = 0.05

# hyperparameters
## base
num_layers = 6
d_model = 512
dff = 2048
num_heads = 8
jpn_vocab_size = 8000
en_vocab_size = 8000

jpn_txt_path = "dataset/jpn_data.txt"
en_txt_path = "dataset/en_data.txt"
ckpt_path = "models/Transformer/ckpt/"
log_path = "logs/"
EPOCHS = 50

def main():
    # load data from the data files
    jpn_data = get_data(jpn_txt_path)
    en_data = get_data(en_txt_path)
    #train_jpn, val_jpn, train_en, val_en = train_test_split(jpn_data,
    #                                                        en_data,
    #                                                        test_size=TR_TE_RATIO)
    JPN_MAX_LEN = get_max_len(jpn_data)
    EN_MAX_LEN = get_max_len(en_data)
    # include [BOS] and [EOS] to each max len above
    JPN_MAX_LEN += 2
    EN_MAX_LEN += 2

    test_jpn_data = ["今日は夜ごはん何にしようかな？",
                     "ここ最近暑い日がずっと続きますね。",
                     "来年は本当にオリンピックが開催されるでしょうか？",
                     "将来の夢はエンジニアになることです。",
                     "子供のころはあの公園でたくさん遊んだなー。",
                     "今日は早く帰りたいな。",
                     "明日は父の日だ。",
                     "試験勉強はなかなか大変です。",
                     "来年はおいしいお店に行きたいです。",
                     "あそこの家にはまだ誰か住んでいますか？"]

    #test_en_data = [[""],
    #                [""],
    #                [""],
    #                [""],
    #                [""]]

    # preprocess for the train dataset
    train_dataset = tf.data.Dataset.from_tensor_slices((jpn_data, en_data))
    train_dataset = train_dataset.map(tf_encode)
    train_dataset = train_dataset.cache()
    train_dataset = train_dataset.shuffle(len(jpn_data)).padded_batch(BATCH_SIZE)
    train_dataset = train_dataset.prefetch(AUTOTUNE)
    ## preprocess for the validation dataset
    #val_dataset = tf.data.Dataset.from_tensor_slices((val_jpn, val_en))
    #val_dataset = val_dataset.map(tf_encode)
    #val_dataset = val_dataset.padded_batch(BATCH_SIZE)
    # preprocess for the test data
    #test_dataset = tf.data.Dataset.from_tensor_slices((test_jpn_data, test_en_data))
    #test_dataset = test_dataset.map(tf_encode)
    #test_dataset = test_dataset.cache()
    #test_dataset = test_dataset.padded_batch(len(test_jpn_data))
    #test_dataset = test_dataset.prefetch(AUTOTUNE)

    # instantiate the Transformer model
    transformer = Transformer(num_layers=num_layers,
                              d_model=d_model,
                              num_heads=num_heads,
                              dff=dff,
                              input_vocab_size=jpn_vocab_size,
                              target_vocab_size=en_vocab_size,
                              pe_input=JPN_MAX_LEN,
                              pe_target=EN_MAX_LEN)
    # set learning rate, optimizer, loss and matrics
    learning_rate = CustomSchedule(d_model)
    optimizer = Adam(learning_rate,
                     beta_1=0.9,
                     beta_2=0.98,
                     epsilon=1e-9)

    loss_object = SparseCategoricalCrossentropy(from_logits=True,
                                                reduction="none")
    def loss_function(label, pred):
        mask = tf.math.logical_not(tf.math.equal(label, 0))
        loss_ = loss_object(label, pred)
        mask = tf.cast(mask, dtype=loss_.dtype)
        loss_ *= mask
        return tf.reduce_sum(loss_) / tf.reduce_sum(mask)

    train_loss = Mean(name="train_loss")
    train_accuracy = SparseCategoricalAccuracy(name="train_accuracy")

    """
    The @tf.function trace-compiles train_step into a TF graph for faster
    execution. The function specializes to the precise shape of the argument
    tensors. To avoid re-tracing due to the variable sequence lengths or
    variable batch sizes(usually the last batch is smaller), use input_signature
    to specify more generic shapes.
    """
    train_step_signature = [tf.TensorSpec(shape=(None, None), dtype=tf.int64),
                            tf.TensorSpec(shape=(None, None), dtype=tf.int64)]

    @tf.function(input_signature=train_step_signature)
    def train_step(inp, tar):
        tar_inp = tar[:, :-1]
        tar_label = tar[:, 1:]
        training = True

        enc_padding_mask, combined_mask, dec_padding_mask = create_masks(inp,
                                                                         tar_inp)
        with tf.GradientTape() as tape:
            predictions, _ = transformer(inp,
                                         tar_inp,
                                         training,
                                         enc_padding_mask,
                                         combined_mask,
                                         dec_padding_mask)
            loss = loss_function(tar_label, predictions)

        gradients = tape.gradient(loss, transformer.trainable_variables)
        optimizer.apply_gradients(zip(gradients, transformer.trainable_variables))

        train_loss(loss)
        train_accuracy(tar_label, predictions)

    # set the checkpoint and the checkpoint manager
    ckpt = tf.train.Checkpoint(epoch=tf.Variable(0),
                               transformer=transformer,
                               optimizer=optimizer)
    ckpt_manager = tf.train.CheckpointManager(ckpt,
                                              ckpt_path,
                                              max_to_keep=5)
    # if a checkpoint exists, restore the latest checkpoint.
    if ckpt_manager.latest_checkpoint:
        ckpt.restore(ckpt_manager.latest_checkpoint)
        print("Latest checkpoint restored.")

    # set up summary writers
    current_time = datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
    log_dir = os.path.join(log_path, current_time, "train")
    #test_log_dir = os.path.join(log_path, current_time, "validation")
    summary_writer = tf.summary.create_file_writer(log_dir)
    #test_summary_writer = tf.summary.create_file_writer(test_log_dir)

    for ckpt.epoch in range(EPOCHS):
        start = time.time()
        ckpt.epoch.assign_add(1)
        train_loss.reset_states()
        train_accuracy.reset_states()

        # inp: Japanese, tar: English
        for (batch, (inp, tar)) in enumerate(train_dataset):
            train_step(inp, tar)

            if batch % 100 == 0:
                print("Epoch {} Batch {} Loss {:.4f} Accuracy {:.4f}"
                .format(ckpt.epoch,
                        batch,
                        train_loss.result(),
                        train_accuracy.result()))

        # output the training log for every epoch
        print("Epoch {} Loss {:.4f} Accuracy {:.4f}".format(ckpt.epoch,
                                                            train_loss.result(),
                                                            train_accuracy.result()))
        print("Time taken for 1 epoch: {:.3f} secs\n".format(time.time() - start))

        # check how the model performs for every epoch
        test_summary_log = test_translate(test_jpn_data, EN_MAX_LEN, transformer)

        with summary_writer.as_default():
            tf.summary.scalar("loss", train_loss.result(), step=ckpt.epoch)
            tf.summary.scalar("accuracy", train_accuracy.result(), step=ckpt.epoch)
            tf.summary.text("test_text", test_summary_log, step=ckpt.epoch)

        if (ckpt.epoch) % 5 == 0:
            ckpt_save_path = ckpt_manager.save()
            print("Saving checkpoint for epoch {} at {}".format(ckpt.epoch,
                                                                ckpt_save_path))

if __name__ == "__main__":
    main()


from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import json

from googlecloudsdk.command_lib.anthos.config.sync.common import exceptions
from googlecloudsdk.command_lib.anthos.config.sync.common import utils
from googlecloudsdk.core import log

def ListResources(project, name, namespace, repo_cluster, membership):
  """List managed resources.

  Args:
    project: The project id the repo is from.
    name: The name of the corresponding ResourceGroup CR.
    namespace: The namespace of the corresponding ResourceGroup CR.
    repo_cluster: The cluster that the repo is synced to.
    membership: membership name that the repo should be from.

  Returns:
    List of raw ResourceGroup dicts

  """
  if membership and repo_cluster:
    raise exceptions.ConfigSyncError(
        'only one of --membership and --cluster may be specified.')

  resource_groups = []
  # Get ResourceGroups from the Config Controller cluster
  if not membership:  # exclude CC clusters if membership option is provided
    cc_rg = _GetResourceGroupsFromConfigController(
        project, name, namespace, repo_cluster)
    resource_groups.extend(cc_rg)

  # Get ResourceGroups from memberships
  member_rg = _GetResourceGroupsFromMemberships(
      project, name, namespace, repo_cluster, membership)
  resource_groups.extend(member_rg)

  # Parse ResourceGroups to structured output
  return ParseResultFromRawResourceGroups(resource_groups)

def _GetResourceGroupsFromConfigController(
    project, name, namespace, repo_cluster):
  """List all ResourceGroup CRs from Config Controller clusters.

  Args:
    project: The project id the repo is from.
    name: The name of the corresponding ResourceGroup CR.
    namespace: The namespace of the corresponding ResourceGroup CR.
    repo_cluster: The cluster that the repo is synced to.

  Returns:
    List of raw ResourceGroup dicts

  """
  clusters = []
  resource_groups = []
  try:
    # TODO(b/218518163): support resources applied by kpt live apply
    clusters = utils.ListConfigControllerClusters(project)
  except exceptions.ConfigSyncError as err:
    log.error(err)
  if clusters:
    for cluster in clusters:
      if repo_cluster and repo_cluster != cluster[0]:
        continue
      try:
        utils.KubeconfigForCluster(project, cluster[1], cluster[0])
        cc_rg = _GetResourceGroups(cluster[0], name,
                                   namespace)
        if cc_rg:
          resource_groups.extend(cc_rg)
      except exceptions.ConfigSyncError as err:
        log.error(err)
  return resource_groups

def _GetResourceGroupsFromMemberships(
    project, name, namespace, repo_cluster, membership):
  """List all ResourceGroup CRs from the provided membership cluster.

  Args:
    project: The project id the repo is from.
    name: The name of the corresponding ResourceGroup CR.
    namespace: The namespace of the corresponding ResourceGroup CR.
    repo_cluster: The cluster that the repo is synced to.
    membership: membership name that the repo should be from.

  Returns:
    List of raw ResourceGroup dicts

  """
  resource_groups = []
  try:
    memberships = utils.ListMemberships(project)
  except exceptions.ConfigSyncError as err:
    raise err
  for member in memberships:
    if membership and not utils.MembershipMatched(member, membership):
      continue
    if repo_cluster and repo_cluster != member:
      continue
    try:
      utils.KubeconfigForMembership(project, member)
      member_rg = _GetResourceGroups(member, name, namespace)
      if member_rg:
        resource_groups.extend(member_rg)
    except exceptions.ConfigSyncError as err:
      log.error(err)
  return resource_groups

def _GetResourceGroups(cluster_name, name, namespace):
  """List all the ResourceGroup CRs from the given cluster.

  Args:
    cluster_name: The membership name or cluster name of the current cluster.
    name: The name of the desired ResourceGroup.
    namespace: The namespace of the desired ResourceGroup.

  Returns:
    List of raw ResourceGroup dicts

  Raises:
    Error: errors that happen when listing the CRs from the cluster.
  """
  utils.GetConfigManagement(cluster_name)
  if not namespace:
    params = ['--all-namespaces']
  else:
    params = ['-n', namespace]
  repos, err = utils.RunKubectl(
      ['get', 'resourcegroup.kpt.dev', '-o', 'json'] + params)
  if err:
    raise exceptions.ConfigSyncError(
        'Error getting ResourceGroup custom resources for cluster {}: {}'
        .format(cluster_name, err))

  if not repos:
    return []
  obj = json.loads(repos)
  if 'items' not in obj or not obj['items']:
    return []

  resource_groups = []
  for item in obj['items']:
    _, nm = utils.GetObjectKey(item)
    if name and nm != name:
      continue
    resource_groups.append(RawResourceGroup(cluster_name, item))

  return resource_groups

class RawResourceGroup:
  """Representation of the raw ResourceGroup output from kubectl."""

  def __init__(self, cluster, rg_dict):
    """Initialize a RawResourceGroup object.

    Args:
      cluster: name of the cluster the results are from
      rg_dict: raw ResourceGroup dictionary parsed from kubectl
    """
    self.cluster = cluster
    self.rg_dict = rg_dict

class ListItem:
  """Result class to be returned to gcloud."""

  def __init__(self, cluster_name='', group='', kind='', namespace='', name='',
               status='', condition=''):
    """Initialize a ListItem object.

    Args:
      cluster_name: name of the cluster the results are from
      group: group of the resource
      kind: kind of the resource
      namespace: namespace of the resource
      name: name of the resource
      status: status of the resource
      condition: condition message of the resource
    """
    self.cluster_name = cluster_name
    self.group = group
    self.kind = kind
    self.namespace = namespace
    self.name = name
    self.status = status
    self.condition = condition

  @classmethod
  def FromResourceStatus(cls, cluster_name, resource):
    """Initialize a ListItem object from a resourceStatus.

    Args:
      cluster_name: name of the cluster the results are from
      resource: individual resource status dictionary parsed from kubectl

    Returns:
      new instance of ListItem
    """
    condition = ''
    reconcile_condition = utils.GetActuationCondition(resource)
    conditions = resource.get('conditions', [])[:]
    if reconcile_condition:
      conditions.insert(0, reconcile_condition)
    if conditions:
      delimited_msg = ', '.join(
          ["'{}'".format(c['message']) for c in conditions])
      condition = '[{}]'.format(delimited_msg)
    return cls(
        cluster_name=cluster_name,
        group=resource['group'],
        kind=resource['kind'],
        namespace=resource['namespace'],
        name=resource['name'],
        status=resource['status'],
        condition=condition,
    )

  def __eq__(self, other):
    attributes = ['cluster_name', 'group', 'kind', 'namespace', 'name',
                  'status', 'condition']
    for a in attributes:
      if getattr(self, a) != getattr(other, a):
        return False
    return True

def ParseResultFromRawResourceGroups(raw_resource_groups):
  """Parse from RawResourceGroup.

  Args:
    raw_resource_groups: List of RawResourceGroup

  Returns:
    List of ListItems
  """
  resources = []
  for raw_rg in raw_resource_groups:
    cluster = raw_rg.cluster
    resource_statuses = raw_rg.rg_dict['status'].get('resourceStatuses', [])
    for rs in resource_statuses:
      resources.append(ListItem.FromResourceStatus(cluster, rs))
  return resources


from random import randint

def genererTableau(n):
    s = [0] * n
    for i in range(n):
        s[i] = randint(1, 1000)
    return s

def triParSelection(tableau):
    for i in range(0, len(tableau)):
        min = tableau[i]
        imin = i

        for j in range(i+1, len(tableau)):
            if tableau[j] < min:
                min = tableau[j]
                imin = j

        tableau[i], tableau[imin] = tableau[imin], tableau[i]

tab1 = genererTableau(100)
triParSelection(tab1)
print(tab1)

""" Le but est désormais de déplacer chaque élément à sa
position définitive.
On recherche l’élément le plus petit. Il faut donc le placer
en premier, or cette position est déjà occupée, on échange
donc les deux éléments.
Il ne reste plus qu’à répéter l’opération N fois.
Chaque échange met un élément en position définitive,
l’autre par contre est mal placé. Cependant, aucun
échange n’est inutile, car un élément qui a été bien placé,
ne sera plus testé par la suite. """


from sklearn.metrics import accuracy_score
from data_functions import train_val_split_by_fold
from xgboost import XGBClassifier
import numpy as np
import pandas as pd
from tqdm import tqdm

def cross_validation(df, processed_test_df, config):
    full_predictions = []
    full_targets = []

    for fold in df.fold.unique():
        train_df, val_df = train_val_split_by_fold(df, fold)

        fitter = Fitter(train_df, processed_test_df, config)
        fitter.train()

        predictions = fitter.predict(val_df.drop(["Transported", "fold"], axis=1))
        targets = val_df.Transported.values

        full_predictions.append(predictions)
        full_targets.append(targets)

        print(f"accuracy_score for fold {int(fold)}:", accuracy_score(targets, predictions))

    full_predictions = np.concatenate(full_predictions)
    full_targets = np.concatenate(full_targets)
    print()
    print("accuracy_score on the whole data: ", accuracy_score(full_targets, full_predictions))

class Fitter:
    def __init__(self, train_df, test_df, config):

        self.train_df = train_df
        self.test_df = test_df.copy()
        self.config = config
        self.classifiers = []

    def train(self, pseudolabels_df=None):
        self.classifiers = []

        for fold in tqdm(self.train_df.fold.unique()):
            clf = XGBClassifier(**self.config)
            train_df, val_df = train_val_split_by_fold(self.train_df, fold)

            if pseudolabels_df is not None:
                train_df = pd.concat([train_df, pseudolabels_df], axis=0)

            X_train = train_df.drop(["Transported", "fold"], axis=1)
            y_train = train_df.Transported

            X_val = val_df.drop(["Transported", "fold"], axis=1)
            y_val = val_df.Transported

            clf.fit(X_train, y_train, eval_set=[(X_val, y_val)], verbose=False)

            self.classifiers.append(clf)

        if pseudolabels_df is None:
            self.test_df["Transported"] = self.predict_proba(self.test_df)
            self.test_df = self.test_df[(self.test_df["Transported"] > 0.97) ]
            self.test_df["Transported"] = np.rint(self.test_df["Transported"].values)
            self.train(self.test_df)

    def predict(self, test_df):
        predictions = []

        for classifier in self.classifiers:
            predictions.append(np.expand_dims(classifier.predict_proba(test_df)[:,1], axis=0))

        return (np.rint(np.concatenate(predictions, axis=0).mean(axis=0)) == 1)

    def predict_proba(self, test_df):
        predictions = []

        for classifier in self.classifiers:
            predictions.append(np.expand_dims(classifier.predict_proba(test_df)[:,1], axis=0))

        return (np.concatenate(predictions, axis=0).mean(axis=0))

    def get_submission(self, test_df):
        predictions = self.predict(test_df.drop("PassengerId", axis=1))
        submission = test_df[["PassengerId"]]
        submission["Transported"] = predictions

        return submission


from copy import deepcopy
import numpy as np
import math
import itertools

class Board:
    def __init__(self, board_h, board_w, initial_board, initial_pos):
        self.humansPos = {} # dict of tuple (x, y : nb) indicating positions on board  as key and number of units as value
        self.vampiresPos = {} # list of tuple (x, y : nb) indicating positions on board  as key and number of units as value
        self.werewolvesPos = {} #list of tuple (x, y : nb) indicating positions on board  as key and number of units as value
        self.board_w = board_w
        self.board_h = board_h
        self.is_vampire = None # are we vampire or werewolves
        self.updateBoardInit(initial_board, initial_pos)

    def getSplittingOptions(self, position, max_split, depth = 2):
        """
        Return: all possible positions of splits as well as original available moves
        Depth: size of the circle around you that you consider (for the split)
        max_split: maximum number of different units we authorise
        position: original position of the unit you are considering
        """
        close_enemy_units = []
        close_enemy_positions = []
        splitting_option_score_full = []
        num_split = len(self.getOurDict()) # number of positions we have
        x, y = position
        our_unit = self.getOurDict()[tuple(position)] # number of units at this position

        if(num_split < max_split):  # limit the number of splits
            # get the close by positions
            for i in range(max(0, x - depth), min(self.board_w, x + depth + 1)): # all x-coordinates considered, dist 2 from us
                for j in range(max(0, y - depth), min(self.board_h, y + depth + 1)): # all y-coordinates considered, dist 2 from us
                    # Look at eatable enemies and humans that are present in this area
                    if (i, j) in self.humansPos:  # if we have a human
                        if self.humansPos[(i, j)] <= our_unit:
                            close_enemy_positions.append([i, j])
                            close_enemy_units.append(self.humansPos[(i, j)])
                    if (i, j) in self.getOpponentDict(): # if we have an enemy
                        if self.getOpponentDict()[(i, j)] <= 1.5*our_unit:
                            close_enemy_positions.append([i, j])
                            close_enemy_units.append(self.getOpponentDict()[(i, j)])

            # Generate the splitting options
            target_positions, target_units = [], []
            for i in range(len(close_enemy_units)):
                for j in range(i+1, len(close_enemy_units)): # loop on all units spotted in the area regarded
                    if our_unit - close_enemy_units[i] - close_enemy_units[j] >= 0: # consider next element in the list (cf previous one)
                        path_pos_i = list(np.add(np.sign(np.add(close_enemy_positions[i], -np.array(position))), position)) # pos where you want to go
                        path_pos_j = list(np.add(np.sign(np.add(close_enemy_positions[j], -np.array(position))), position)) # pos where you want to go
                        if (tuple(path_pos_i) not in self.getCurrentPositions()) and (tuple(path_pos_j) not in self.getCurrentPositions()):
                            if path_pos_i == path_pos_j or close_enemy_units[j] == 0:
                                target_positions.append([path_pos_j]) # append position of pair of units
                                target_units.append([close_enemy_units[i] + close_enemy_units[j]])  # append their number of units
                            else:
                                target_positions.append([path_pos_i, path_pos_j])
                                target_units.append([close_enemy_units[i], close_enemy_units[j]])

            # even out the remaining units
            for e, u in zip(target_positions, target_units):
                if (np.max(u) - np.min(u)) >= our_unit - np.sum(u): # if we don't need all units for the split
                    u[np.argmin(u)] += our_unit - np.sum(u) # attribute remaining ones to smaller split unit
                else:
                    mini, maxi = np.min(u), np.max(u)
                    u[np.argmin(u)] += maxi - mini  # first add some to min to make both equals
                    u[np.argmin(u)] += int((our_unit - np.sum(u)) / 2)  # then split in 2 what is left
                    u[np.argmin(u)] += our_unit - np.sum(u)
                if len(e) == 3: # deal with moves obtained without splitting, which have length 3
                    e.append(u[0])
                    e.append(x)
                    e.append(y)
                else:
                    for a, b in zip(e, u): # reformat splitting options
                        a.append(b)
                        a.append(x)
                        a.append(y)
                splitting_option_score_full.append(tuple(e))

        # adding the non-split options
        for i in range(max(0, x - 1), min(self.board_w, x + 2)):
            for j in range(max(0, y - 1), min(self.board_h, y + 2)):
                if ((i, j) not in self.getCurrentPositions()):
                    splitting_option_score_full.append(tuple([[i, j, our_unit, x, y]]))

        # removing the duplicates
        unique_splitting_options = []
        for element in splitting_option_score_full:
            if(element not in unique_splitting_options): unique_splitting_options.append(element)

        return unique_splitting_options

    def updateBoardInit(self, board, initial_pos):
        for element in board:
            if element[2] != 0: # humans
                self.humansPos[(element[0], element[1])] = element[2]
            elif element[3] != 0: # vampires
                self.vampiresPos[(element[0], element[1])] = element[3]
                #are we vampires ?
                if (element[0] == initial_pos[0]) and (element[1] == initial_pos[1]):
                    self.is_vampire = True
            elif element[4] != 0: # werewolves
                self.werewolvesPos[(element[0], element[1])] = element[4]
                #are we werewolves ?
                if (element[0] == initial_pos[0]) and (element[1] == initial_pos[1]):
                    self.is_vampire = False

    def getBoard(self):
        return self.humansPos, self.vampiresPos, self.werewolvesPos

    def updateBoard(self, updates):
        '''
        update the board with updates provided by server
        '''
        for element in updates:
            if element[2] == 0 : #erase human position if exist
                self.humansPos.pop((element[0], element[1]), -1)
            else: #set the value (insert or update)
                self.humansPos[(element[0], element[1])] = element[2]

            if element[3] == 0 : #erase vampires position if exist
                self.vampiresPos.pop((element[0], element[1]), -1)
            else: #set the value (insert or update)
                self.vampiresPos[(element[0], element[1])] = element[3]

            if element[4] == 0 : #erase werewolves position if exist
                self.werewolvesPos.pop((element[0], element[1]), -1)
            else: #set the value (insert or update)
                self.werewolvesPos[(element[0], element[1])] = element[4]

    def generate_move(self, target_pos):
        '''
            generate a new board object from a move from player
            target pos : (tuple (x, y))
            return a new board instance
        '''

        current_pos = self.getCurrentPositions()

        #generate a new board
        new_board = deepcopy(self)

        #erase current positions for our units
        for pos in current_pos:
            new_board._simulAction(pos[0], pos[1], 0)

        #generate moves
        for pos in target_pos:
            new_board._simulAction(pos[0], pos[1], pos[2])

        return new_board

    def _simulAction(self, x, y, nb_units_move):
        '''
        update the board with update(x, y, nb_units_move) provided by simulator
        '''

        if nb_units_move == 0: #we remove a position depending of our camp
            if self.is_vampire:
                self.vampiresPos.pop((x, y), -1)
            else:
                self.werewolvesPos.pop((x, y), -1)
            return

        #Were there humans on the update position ? if yes, get number and remove position
        nb_humans = self.humansPos.pop((x,y), 0)

        #Were there enemies on the update position ? if yes, get number and remove position
        if self.is_vampire:
            nb_enemy = self.werewolvesPos.pop((x, y), 0)
        else:
            nb_enemy = self.vampiresPos.pop((x, y), 0)

        #are there an unit
        if self.is_vampire:
            nb_ally = self.vampiresPos.pop((x, y), 0)
        else:
            nb_ally = self.werewolvesPos.pop((x, y), 0)

        if nb_ally > 0 : # we merge units
            if self.is_vampire:
                self.vampiresPos[(x, y)] = nb_units_move + nb_ally
            else :
               self.werewolvesPos[(x, y)] = nb_units_move + nb_ally

        elif nb_humans > 0: # we need to simulate a fight
            #calculate probability of win
            if nb_units_move >= nb_humans:
                P = 1
            else: # random battle
                P = nb_units_move / (2 * nb_humans)

            #compute the expected outcome
            outcome = math.floor((nb_humans + nb_units_move) * P)

            if outcome > 0 : # we win
                #update unit number
                if self.is_vampire:
                    self.vampiresPos[(x, y)] = outcome
                else :
                    self.werewolvesPos[(x, y)] = outcome
            else: #we loose
                self.humansPos[(x, y)] = nb_humans

        elif nb_enemy > 0: # we need to simulate a fight
            #calculate probability of win
            if nb_units_move >= (nb_enemy * 1.5):
                P = 1
            elif nb_units_move * 1.5 <= nb_enemy:
                P = 0
            elif nb_units_move >= nb_enemy :
                P = nb_units_move / nb_enemy - 0.5
            elif nb_units_move <= nb_enemy :
                P = nb_units_move / (2 * nb_enemy)

            #compute the expected outcome
            outcome = math.floor(nb_units_move * P)

            if outcome > 0 : # we win
                #update unit number
                if self.is_vampire:
                    self.vampiresPos[(x, y)] = outcome
                else :
                    self.werewolvesPos[(x, y)] = outcome
            else: #we loose
                if self.is_vampire:
                    self.werewolvesPos[(x, y)] = nb_enemy
                else :
                   self.vampiresPos[(x, y)] = nb_enemy

        else: #simple move
            if self.is_vampire:
                self.vampiresPos[(x, y)] = nb_units_move
            else :
               self.werewolvesPos[(x, y)] = nb_units_move

    def getCurrentUnitsNumber(self):
        '''
        return a list of number indicating our number of units at each position
        inline with getCurrentpositions order
        '''
        if self.is_vampire:
            return list(self.vampiresPos.values())
        return list(self.werewolvesPos.values())

    def getCurrentUnitsNumberSum(self):
        '''
        return the total number of ennemy units, for all positions
        '''
        if self.is_vampire:
            return sum(list(self.vampiresPos.values()))
        return sum(list(self.werewolvesPos.values()))

    def getCurrentPositions(self):
        '''
        return a list of tuple (x, y) indicating our positions
        (units number is not provided)
        '''
        if self.is_vampire:
            return list(self.vampiresPos.keys())
        return list(self.werewolvesPos.keys())

    def getBiggestPosition(self):
        '''
        return our biggest position ((x, y), nb)
        '''
        index = np.argmax(self.getCurrentUnitsNumber())

        return self.getCurrentPositions()[index], self.getCurrentUnitsNumber()[index]

    def getOpponentUnitsNumberSum(self):
        '''
        return the total number of ennemy units, for all positions
        '''
        if self.is_vampire:
            return sum(list(self.werewolvesPos.values()))
        return sum(list(self.vampiresPos.values()))

    def getOpponentUnitsNumber(self):
        '''
        return a list of values indicating the number of ennemies at each position
        inline with getCurrentpositions order
        '''
        if self.is_vampire:
            return list(self.werewolvesPos.values())
        return list(self.vampiresPos.values())

    def getOpponentCurrentPositions(self):
        '''
        return a list of tuple (x, y) indicating all ennemy's positions on the map
        (units number is not provided)
        '''
        if self.is_vampire:
            return list(self.werewolvesPos.keys())
        return list(self.vampiresPos.keys())

    def getOpponentDict(self):
        '''
        Return the number of opponents at each position they occupy
        '''
        if self.is_vampire:
            return self.werewolvesPos
        return self.vampiresPos

    def getOurDict(self):
        '''
        Return the number of units at each position we occupy
        '''
        if self.is_vampire:
            return self.vampiresPos
        return self.werewolvesPos

    def getAvailableMoves(self, split_max = 1):
        #TODO add a path to avoid backtracking : no going back on previous positions from tree search
        '''
            return all available positions on board from a current position
            split max is the number of maximum split allowed, so far, do not manage more than 2
        '''
        positions = []

        if self.is_vampire:
            current_units = dict(sorted(self.vampiresPos.items(), key=lambda kv: kv[1], reverse=True))
        else:
            current_units = dict(sorted(self.werewolvesPos.items(), key=lambda kv: kv[1], reverse=True))

        nb_units = len(current_units)

        #compute the available positions unit by unit
        count = 0
        for (curr_unit, curr_unit_nb) in current_units.items():
            #limit the number of position to process to avoid overload
            if count ==  split_max :
                break
            count += 1

            unit_pos = []
            x = curr_unit[0] # x of our position (x,y)
            y = curr_unit[1] # y of our position (x,y)

            for i in range(max(0, x-1),min(self.board_w, x+2)):
                for j in range(max(0, y-1),min(self.board_h, y+2)):
                    unit_pos.append([i, j, curr_unit_nb])
            unit_pos.remove([x, y, curr_unit_nb])
            positions.append(unit_pos)

        #compute combinatory as split already performed:
        if nb_units > 1 and split_max > 1:
            pos_clean = []

            for pos in positions:
                pos_clean.append([item for item in pos if tuple(item[:-1])
                                not in self.getCurrentPositions()])

            positions = list(itertools.product(*pos_clean))
            return positions

        #perform split
        if nb_units == 1 and split_max ==2 and self.getBiggestPosition()[1] > 1 : #we want to split in 2
            xs, ys, nb = zip(*positions[0])
            positions_ = zip(xs, ys)

            comb = list(itertools.combinations(positions_, split_max))
            first_half = np.array([nb[0]//2 for x in comb])
            second_half = nb[0] - first_half

            first_pos, second_pos = zip(*comb)
            first_pos_x, first_pos_y = zip(*first_pos)
            second_pos_x, second_pos_y = zip(*second_pos)

            #return split + non split
            split = [list(a) for a in zip(
                            zip(first_pos_x, first_pos_y, first_half),
                            zip(second_pos_x, second_pos_y, second_half))]
            return split + [list([item]) for item in positions[0]]

        if nb_units == 1 and split_max ==3 and self.getBiggestPosition()[1] > 3: #we want to split in 3
            xs, ys, nb = zip(*positions[0])
            positions_ = zip(xs, ys)

            comb = list(itertools.combinations(positions_, split_max))
            first_third = np.array([nb[0]//3 for x in comb])
            second_third = np.copy(first_third)
            third_third = nb[0] - 2 * second_third

            first_pos, second_pos, third_pos = zip(*comb)
            first_pos_x, first_pos_y = zip(*first_pos)
            second_pos_x, second_pos_y = zip(*second_pos)
            third_pos_x, third_pos_y = zip(*third_pos)

            #return split + non split
            split = [list(a) for a in zip(
                            zip(first_pos_x, first_pos_y, first_third),
                            zip(second_pos_x, second_pos_y, second_third),
                            zip(third_pos_x, third_pos_y, third_third))]
            return split + [list([item]) for item in positions[0]]

        # we don't want to split or default option
        return [list([item]) for item in positions[0]]

    def hash(self):
        '''
            return the hash for the board
        '''
        hash_string = ''
        for k in self.humansPos:
            hash_string += str(k[0]) + str(k[1]) + str(self.humansPos[k])
        for k in self.vampiresPos:
            hash_string += str(k[0]) + str(k[1]) + str(self.vampiresPos[k])
        for k in self.werewolvesPos:
            hash_string += str(k[0]) + str(k[1]) + str(self.werewolvesPos[k])
        return

    def PotentialUnits(self, x, y, pos_nb, w, z):
        """
        score: current value of the score. We update this value throughout this function
        position: our potential position
        potential_units: our potential number of units when we reach a human group
        Given the potential position considered, consider only humans in the chosen direction
        We sort them by distance and compute the probability to eat them using our potential number of units.
        The score is a sum dependent on the distance, the number of humans and the proba to eat them
        """

        q1 = 0.5
        q2 = 1.1
        potential_units = pos_nb
        human_score = [0]

        if ((x == w - 1) and (y == z - 1)):  # check in which direction we move
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if (human_pos[0] <= w and human_pos[1] <= z):  # only consider humans situated in specific part of the grid
                    d = np.max(np.abs(np.subtract(human_pos, [x,y]))) # compute distance to this human
                    dico[human_pos] = [d, human_unit] # store info
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True)) # sort humans by distance to us
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q /(item[0]+1)) # item[1] is the number of humans, item[0] the distance

        elif ((x == w + 1) and (y == z + 1)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if (human_pos[0] >= w and human_pos[1] >= z):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w + 1) and (y == z - 1)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if (human_pos[0] >= w and human_pos[1] <= z):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w - 1) and (y == z + 1)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if (human_pos[0] <= w and human_pos[1] >= z):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w) and (y == z + 1)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if ( human_pos[1] -z >= np.abs(human_pos[0] - w) and human_pos[1] >= z):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w) and (y == z - 1)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if ( z - human_pos[1] >= np.abs(human_pos[0] - w) and human_pos[1] <= z):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w+1) and (y == z)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if ( human_pos[0] -w >= np.abs(human_pos[1] - z) and human_pos[0] >= w):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        elif ((x == w-1) and (y == z)):  # not optimised for split
            dico = {}
            for human_pos, human_unit in zip(self.humansPos, self.humansPos.values()):
                if ( w - human_pos[0] >= np.abs(human_pos[1] - z) and human_pos[0] <= w):
                    d = np.max(np.abs(np.subtract(human_pos, [x,y])))
                    dico[human_pos] = [d, human_unit]
            dico = dict(sorted(dico.items(), key=lambda kv: kv[1][0], reverse=True))
            for item, loc in zip(dico.values(), dico):
                if(item[1] > potential_units):
                    p = 0
                    q = 1 # does not matter
                else:
                    p = 1
                    potential_units += item[1]
                    distEH = min([np.max(np.abs(np.subtract(list(enemy), loc))) for enemy in self.getOpponentDict()])
                    if ((distEH)/(d+1) <= 1/4): q= q1
                    elif (4/5 < (distEH)/(d+1) and (distEH)/(d+1) < 3/2): q = q2
                    else: q=1
                human_score.append(item[1]*p*q/(item[0]+1))

        return human_score

def testgetAvailableMoves_1():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 14, (3, 3) : 18}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getAvailableMoves(1))
    print(len(board.getAvailableMoves(1)))

def testgetAvailableMoves_2():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 14, (3, 3) : 18, (4, 3) : 11}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getAvailableMoves(2))
    print(len(board.getAvailableMoves(2)))

def testgetAvailableMoves_3():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 14}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getAvailableMoves(2))
    print(len(board.getAvailableMoves(2)))

def testgetAvailableMoves_4():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 14}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getAvailableMoves(3))
    print(len(board.getAvailableMoves(3)))

def testgetAvailableMoves_5():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 1}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getAvailableMoves(2))
    print(len(board.getAvailableMoves(2)))

def testgenerate_move():
    board_w, board_h = 5, 5
    vampires = {(2, 1) : 11}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {(1, 0) : 15}
    print(board.generate_move([[1, 0, 11]]).getBoard())

def testgenerate_move_merger():
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 14, (3, 3) : 18}
    humans = {(1, 1) : 4, (3, 1) : 4, (0, 4) : 1, (4, 4) : 1}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.generate_move([[2, 3, 14], [2, 3, 18]]).getBoard())

def testgetSplittingOptions(pos, num):
    board_w, board_h = 5, 5
    vampires = {(2, 2) : 8, (4, 4) : 1}
    humans = {(0, 0) : 3, (3, 0) : 3, (0, 4) : 3, (1, 4) : 3}
    board = Board(board_w, board_h, [], [])
    board.is_vampire = True
    board.humansPos = humans
    board.vampiresPos = vampires
    board.werewolvesPos = {}
    print(board.getSplittingOptions(pos, num))


from kraken.core.api import Project

from kraken.std.docker import build_docker_image
from kraken.std.generic.render_file import RenderFileTask

project = Project.current()

dockerfile = project.do(
    name="dockerfile",
    task_type=RenderFileTask,
    content="FROM ubuntu:focal\nRUN echo Hello world\n",
    file=project.build_directory / "Dockerfile",
)

build_docker_image(
    name="buildDocker",
    dockerfile=dockerfile.file,
    tags=["kraken-example"],
    load=True,
)


"""
try:
	#instructions
except Exception1:
	#If there is exception type 1
except Exeption2:
	#If there is exception type 2
finally:
	#Finally instead of else
else:
	#If there is no exceptions
"""
try :
	a = 5 / 0
except ZeroDivisionError as e:
	print ("Zero Division Error")
finally:
	print ("Finish code")

# Throw an exception
def RaiseException(a):
	if type(a) != type('a'):
		raise Exception("a is not a string")
a = 1
try:
	RaiseException(a)
except Exception as e:
	print(e)

# One line throwing exception
def AssertException(a, b):
	assert a > b, "a is greater than b" # If not true throw an error

try :
	AssertException(2, 6)
except AssertionError as e:
	print (e)


#Queue implemention using Lists/Array
import collections
class Queue:
    def __init__(self):
        self.items=[]

    def enque(self,value):
        self.items.append(value)

    def deque(self):
        return self.items.pop(0)

    def Display(self):
        print(self.items)

    def isEmpty(self):
        if self.items==0:
            print("Empty Queue")
        else:
            print("Not empty")

myQ=Queue()
myQ.enque(1)
myQ.enque(2)
myQ.enque(3)
myQ.enque(4)
myQ.Display()
x=myQ.deque()
print("\n Popped value:",x)
myQ.isEmpty()

myQ=collections.deque()    #the brackets are important
myQ.append(10)
myQ.append(102)
myQ.append(103)
print(myQ)
myQ.popleft()
print(myQ)


import enum
from os import path, remove

from ethereum.utils import denoms

from golem.core.common import timeout_to_string
from golem.core.variables import PICKLED_VERSION
from golem.environments.environment import Environment
from golem.task.taskstate import TaskState

DEFAULT_TIMEOUT = 4 * 3600
DEFAULT_SUBTASK_TIMEOUT = 20 * 60

class RunVerification(enum.Enum):
    """
    Enabled: (default)
        Perform verification and act accordingly.
    Lenient:
        The verification should be performed and then in the event of negative
        verification result, the subtask itself should be marked as failed as
        usual and rescheduled, the provider should also be banned from
        performing any additional subtasks in this task and a failure should
        still be reported to the golem monitor. The provider should get a
        SubtaskResultsAccepted response and be issued a payment just as it
        would had the result been correct.
    Disabled:
        Completely disable verification for the given task and treat all
        results as valid without performing any verification.
    """
    def _generate_next_value_(name, *_):  # pylint:disable=no-self-argument
        return name

    enabled = enum.auto()
    lenient = enum.auto()
    disabled = enum.auto()

class TaskDefinition(object):
    """ Task description used in GUI and in save file format"""

    def __init__(self):
        self.task_id = ""
        self.timeout = DEFAULT_TIMEOUT
        self.subtask_timeout = DEFAULT_SUBTASK_TIMEOUT

        self.resources = set()
        self.estimated_memory = 0

        self.subtasks_count = 0
        self.output_file = ""
        self.task_type = None
        self.name = ""

        self.max_price = 0

        self.run_verification: RunVerification = RunVerification.enabled
        self.options = Options()
        self.docker_images = None
        self.compute_on = "cpu"

        self.concent_enabled: bool = False

    def __getstate__(self):
        return PICKLED_VERSION, self.__dict__

    def __setstate__(self, state):
        # FIXME Move to sqlite
        if not isinstance(state, tuple):
            pickled_version, attributes = 0, state
        else:
            pickled_version, attributes = state
            if not isinstance(pickled_version, int):
                pickled_version = 1

        if pickled_version < 1:
            # Defaults for attributes that could be missing in pickles
            # from 0.17.1  #3405
            migration_defaults = (
                ('compute_on', 'cpu'),
                ('concent_enabled', False),
            )
            for key, default_value in migration_defaults:
                if key not in attributes:
                    attributes[key] = default_value

        if pickled_version < 2:
            if 'name' not in attributes:
                attributes['name'] = attributes.pop('task_name')
            if 'subtasks_count' not in attributes:
                attributes['subtasks_count'] = attributes.pop('total_subtasks')
            if 'timeout' not in attributes:
                attributes['timeout'] = attributes.pop('full_task_timeout')

        if pickled_version < 3:
            if 'run_verification' not in attributes:
                attributes['run_verification'] = RunVerification.enabled

        for key in attributes:
            setattr(self, key, attributes[key])

    def is_valid(self):
        return self._check_output_file(self.output_file)

    @staticmethod
    def _check_output_file(output_file):
        try:
            file_exist = path.exists(output_file)
            with open(output_file, 'a'):
                pass
            if not file_exist:
                remove(output_file)
                return True, None
            return True, "File {} may be overwritten".format(output_file)
        except IOError:
            return False, "Cannot open output file: {}".format(output_file)
        except TypeError as err:
            return False, "Output file {} is not properly set: {}".format(
                output_file, err)

    def add_to_resources(self):
        pass

    def remove_from_resources(self):
        pass

    def to_dict(self) -> dict:
        task_timeout = timeout_to_string(int(self.timeout))
        subtask_timeout = timeout_to_string(int(self.subtask_timeout))
        output_path = self.build_output_path()

        d = {
            'id': self.task_id,
            'type': self.task_type,
            'compute_on': self.compute_on,
            'name': self.name,
            'timeout': task_timeout,
            'subtask_timeout': subtask_timeout,
            'subtasks_count': self.subtasks_count,
            'bid': float(self.max_price) / denoms.ether,
            'resources': list(self.resources),
            'options': {
                'output_path': output_path
            },
            'concent_enabled': self.concent_enabled,
        }

        if self.run_verification != RunVerification.enabled:
            d['x-run-verification'] = self.run_verification

        return d

    def build_output_path(self) -> str:
        return self.output_file.rsplit(path.sep, 1)[0]

advanceVerificationTypes = ['forAll', 'forFirst', 'random']

class AdvanceVerificationOptions(object):
    def __init__(self):
        self.type = 'forFirst'

class TaskDesc(object):
    def __init__(self,
                 definition_class=TaskDefinition,
                 state_class=TaskState):
        self.definition = definition_class()
        self.task_state = state_class()

    def has_multiple_outputs(self, num_outputs=1):
        """
        Return False if this task has less outputs than <num_outputs>, True
        otherwise
        :param int num_outputs:
        """
        return len(self.task_state.outputs) >= num_outputs

class Options(object):
    """ Task specific options """

    def __init__(self):
        self.environment = Environment()
        self.name = ''


from ... import toolkit
from ...errors import *
from .. import Repository

class Sessions(Repository):
    def lookup(self, *tokens):
        single = False
        if len(tokens) == 1:
            if isinstance(tokens[0], list) is False:
                single = True

        tokens = toolkit.unroll(tokens)
        keys = []
        for token in tokens:
            session_id = self.query("SELECT id FROM sessions WHERE token=@token;", token=token).scalar()
            if session_id is not None:
                keys.append(session_id)
        if single is True:
            return keys[0] if len(keys) > 0 else None
        return keys

@Sessions.plugin
def restore(self, token):
    id = None if isinstance(token, int) is False else token
    if id is None:
        id = self.lookup(token)
    if id is None:
        return self.create()

    session = self.get(id)
    self.query("UPDATE sessions SET accessed=CURRENT_TIMESTAMP WHERE id={id};".format(id=id)).update()
    member_id = session.member_id
    if member_id is not None:
        self.query("UPDATE members SET accessed=CURRENT_TIMESTAMP WHERE id={id};".format(id=member_id)).update()
    self.attach(session)
    return session

@Sessions.plugin
def create(self, *member):
    member = None if len(member) == 0 else member[0]
    member_id = None if member is None else member.id
    if member_id is not None:
        session = self.session
        if session is not None:
            if session.member_id is None or session.member_id == member_id:
                session_id = session.id
                self.query(
                    "UPDATE sessions SET member_id=@member_id, accessed=CURRENT_TIMESTAMP WHERE id=@id;",
                    id=session_id,
                    member_id=member_id
                ).update()
                return self.restore(session_id)

    ip_address = self.ip_address
    user_agent_string = self.user_agent_string
    token = toolkit.guid(32)
    id = self.query(
        "INSERT INTO sessions (token, member_id, ip_address, user_agent_string, created, accessed) VALUES (@token, @member_id, @ip_address, @user_agent_string, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);",
        token=token,
        member_id=member_id,
        ip_address=ip_address,
        user_agent_string=user_agent_string
    ).insert()

    session = self.get(id)
    self.attach(session)
    return session

@Sessions.plugin
def destroy(self, *session):
    session = session[0] if len(session) > 0 else self.session
    if not session:
        return

    if toolkit.is_model(session) is False:
        session = self.get(session)
        if not session:
            return

    session_id = session.id
    self.query("DELETE FROM sessions WHERE id=@id;", id=session_id).update()
    if self.session is not None and self.session.id == session_id:
        self.clear()


from flask import render_template, flash, redirect, url_for, request
from app import app, login, db
from app.forms import (LoginForm, RegistrationForm, EditProfileForm,
                       NewPostForm)
from app.models import User, Post, PostComment
from flask_login import current_user, login_user, logout_user, login_required
from werkzeug.urls import url_parse
from werkzeug.utils import secure_filename
from random import randrange
from datetime import datetime
import os

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in allowed_extensions

def delete_file(filename):
    try:
        os.remove(os.path.join(app.config['UPLOAD_FOLDER'], filename))
    except:
        # flash(f'{filename} not found.')
        pass
    else:
        flash(f'{filename} delete.')

@app.before_request
def before_request():
    if current_user.is_authenticated:
        current_user.last_seen = datetime.utcnow()
        db.session.commit()

@app.route('/')
@app.route('/index', methods=['GET', 'POST'])
@login_required
def index():
    form = NewPostForm()
    posts = Post.query.all()[::-1]
    users = User.query.all()
    if form.validate_on_submit():
        userid = current_user.get_id()
        post = Post(body=form.postbody.data, timestamp=datetime.utcnow(),
                    user_id=userid)
        db.session.add(post)
        db.session.commit()
        flash('New post successful.')
        return redirect(url_for('index'))

    return render_template('index.html', title='Home',
                           posts=posts, users=users, form=form)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        next_page = request.args.get('next')
        if not next_page or url_parse(next_page).netloc != '':
            next_page = url_for('index')
        return redirect(next_page)
    return render_template('login.html', title='Sign In', form=form)

@app.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('index'))

@app.route('/register', methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    form = RegistrationForm()
    if form.validate_on_submit():
        user = User(username=form.username.data, email=form.email.data)
        user.set_password(form.password.data)
        db.session.add(user)
        db.session.commit()
        flash('Congratulations, you are now a registered user!')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)

@app.route('/user/<username>')
@login_required
def user(username):
    user = User.query.filter_by(username=username).first_or_404()
    posts = Post.query.filter_by(user_id=user.id)[:-100:-1]
    return render_template('user.html', user=user, posts=posts)

@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first_or_404()
    if current_user.id != user.id:
        current_user.follow(user)
        db.session.commit()
        flash(f'Now following {username}!')
    else:
        flash('You cannot follow yourself!')
    return redirect(request.referrer)

@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first_or_404()
    if current_user.id != user.id:
        current_user.unfollow(user)
        db.session.commit()
        flash(f'Unfollowed {username}.')
    else:
        flash('You cannot unfollow yourself!')
    return redirect(request.referrer)

@app.route('/edit_profile', methods=['GET', 'POST'])
@login_required
def edit_profile():
    form = EditProfileForm()
    if form.validate_on_submit():
        current_user.username = form.username.data
        current_user.about_me = form.about_me.data
        db.session.commit()
        flash('Your changes have been saved.')
        return redirect(url_for('edit_profile'))
    elif request.method == 'GET':
        form.username.data = current_user.username
        form.about_me.data = current_user.about_me
    return render_template('edit_profile.html', title='Edit Profile',
                           form=form)

@app.route('/new_post', methods=['GET', 'POST'])
@login_required
def new_post():
    form = NewPostForm()
    if form.validate_on_submit():
        userid = current_user.get_id()
        post = Post(body=form.postbody.data, timestamp=datetime.utcnow(),
                    user_id=userid)
        if form.attachment.data:
            f = form.attachment.data
            name_append = ''.join([str(randrange(9)) for n in range(9)])
            filename = str(current_user.id) + '_' + name_append + '_' + secure_filename(f.filename)
            f.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            post.attachment = filename
            flash(f'Uploaded {filename}.')
        db.session.add(post)
        db.session.commit()
        flash('New post successful.')
        return redirect(url_for('index'))
    return render_template('new_post.html', title='New Post',
                           form=form)

@app.route('/getpost/<post_id>', methods=['GET', 'POST'])
@login_required
def getpost(post_id):
    post = Post.query.get(post_id)
    comments = PostComment.query.filter_by(post_id=post.id)
    op = User.query.get(post.user_id)
    form = NewPostForm()
    if form.validate_on_submit():
        userid = current_user.get_id()
        comment = PostComment(body=form.postbody.data, timestamp=datetime.utcnow(),
                              user_id=userid, post_id=post.id)
        db.session.add(comment)
        db.session.commit()
        flash('Comment posted.')
        return redirect(request.referrer)
    else:
        return render_template('getpost.html', title=f"{post.username}'s post.",
                               form=form, post=post, comments=comments)

@app.route('/delete_post/<post_id>', methods=['GET'])
@login_required
def delete_post(post_id):
    post = Post.query.get(post_id)
    if not post:
        return 404
    if current_user.admin == 1 or current_user.username == post.username:
        if post.attachment:
            delete_file(post.attachment)
        db.session.delete(post)
        db.session.commit()
        flash(f'Post {post_id} deleted.')
        return redirect(url_for('index'))
    else:
        flash('You do not have permission to do that.')
        return redirect(request.referrer)

@app.route('/admin', methods=['GET'])
@login_required
def admin():
    if current_user.admin == 1:
        users = User.query.all()
        return render_template('admin.html', title='Admin Panel',
                               users=users)
    else:
        flash('You do not have permission to do that.')
        return redirect(request.referrer)

@app.route('/delete_user/<user_id>', methods=['GET'])
@login_required
def delete_user(user_id):
    u = User.query.get(user_id)
    if not u:
        return 404
    elif u.admin == 1:
        flash('Cannot delete admin.')
        return redirect(request.referrer)
    elif current_user.admin == 1:
        u_posts = Post.query.filter_by(user_id=user_id)
        if u_posts:
            for post in u_posts:
                if post.attachment:
                    delete_file(post.attachment)
                db.session.delete(post)
        db.session.delete(u)
        db.session.commit()
        flash('User deleted.')
        return redirect(request.referrer)
    else:
        flash('You do not have permission to do that.')
        return redirect(request.referrer)