from keras.models import Model
from keras.layers import Input, concatenate, SeparableConv2D, MaxPooling2D, Conv2DTranspose, SpatialDropout2D, BatchNormalization
from keras.optimizers import Adam
from keras import backend as K

smooth = 1

#Cálculo do Coeficiente de Dice para uma máscara prevista pelo modelo

def dice_coef(y_true, y_pred):
    y_true_f = K.flatten(y_true)
    y_pred_f = K.flatten(y_pred)
    intersection = K.sum(y_true_f * y_pred_f)

    return (2. * intersection + smooth) / (K.sum(y_true_f) +
            K.sum(y_pred_f) + smooth)

#Valor do coeficiente de Dice a passar para a avaliação do modelo
#Como se pretende minimizar a função de custo (loss) deve tomar-se o simétrico
#do coeficiente de Dice

def dice_coef_loss(y_true, y_pred):
    return -dice_coef(y_true, y_pred)

#Rede convolucional unet, com:
#spatial_dropout na quarta e quinta camadas para reduzir o nível de overfit
#otimizador - Adam
#função de custo (loss_function) - Coeficiente de Dice
#Inicializador de pesos (weights ou kernel) - he_normal

def unet(vis):
    input_size = (256,256,1)
    inputs = Input(input_size)

    #Encoder

    conv1_1 = SeparableConv2D(64, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(inputs)
    conv1_1 = BatchNormalization()(conv1_1)
    conv1_2 = SeparableConv2D(64, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv1_1)
    conv1_2 = BatchNormalization()(conv1_2)
    pool1 = MaxPooling2D(pool_size=(2,2))(conv1_2)

    conv2_1 = SeparableConv2D(128, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(pool1)
    conv2_1 = BatchNormalization()(conv2_1)
    conv2_2 = SeparableConv2D(128, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv2_1)
    conv2_2 = BatchNormalization()(conv2_2)
    pool2 = MaxPooling2D(pool_size=(2,2))(conv2_2)

    conv3_1 = SeparableConv2D(256, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(pool2)
    conv3_1 = BatchNormalization()(conv3_1)
    conv3_2 = SeparableConv2D(256, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv3_1)
    conv3_2 = BatchNormalization()(conv3_2)
    pool3 = MaxPooling2D(pool_size=(2,2))(conv3_2)

    conv4_1 = SeparableConv2D(512, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(pool3)
    conv4_1 = BatchNormalization()(conv4_1)
    conv4_2 = SeparableConv2D(512, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv4_1)
    conv4_2 = BatchNormalization()(conv4_2)
    drop4_2 = SpatialDropout2D(0.5)(conv4_2)
    pool4 = MaxPooling2D(pool_size=(2,2))(drop4_2)

    conv5_1 = SeparableConv2D(1024, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(pool4)
    conv5_1 = BatchNormalization()(conv5_1)
    conv5_2 = SeparableConv2D(1024, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv5_1)
    conv5_2 = BatchNormalization()(conv5_2)
    drop5_2 = SpatialDropout2D(0.5)(conv5_2)

    #Decoder

    up6_1 = Conv2DTranspose(512, (2,2), strides = (2,2), padding = 'same',
                          activation = 'relu',
                          kernel_initializer='he_normal')(drop5_2)
    up6_1 = BatchNormalization()(up6_1)
    up6_2 = concatenate([drop4_2, up6_1], axis = 3)
    conv6_1 = SeparableConv2D(512, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(up6_2)
    conv6_1 = BatchNormalization()(conv6_1)
    conv6_2 = SeparableConv2D(512, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv6_1)
    conv6_2 = BatchNormalization()(conv6_2)

    up7_1 = Conv2DTranspose(256, (2,2), strides = (2,2), padding = 'same',
                          activation = 'relu',
                          kernel_initializer='he_normal')(conv6_2)
    up7_1 = BatchNormalization()(up7_1)
    up7_2 = concatenate([conv3_2, up7_1], axis = 3)
    conv7_1 = SeparableConv2D(256, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(up7_2)
    conv7_1 = BatchNormalization()(conv7_1)
    conv7_2 = SeparableConv2D(256, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv7_1)
    conv7_2 = BatchNormalization()(conv7_2)

    up8_1 = Conv2DTranspose(128, (2,2), strides = (2,2), padding = 'same',
                          activation = 'relu',
                          kernel_initializer='he_normal')(conv7_2)
    up8_1 = BatchNormalization()(up8_1)
    up8_2 = concatenate([conv2_2, up8_1], axis = 3)
    conv8_1 = SeparableConv2D(128, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(up8_2)
    conv8_1 = BatchNormalization()(conv8_1)
    conv8_2 = SeparableConv2D(128, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv8_1)
    conv8_2 = BatchNormalization()(conv8_2)

    up9_1 = Conv2DTranspose(64, (2,2), strides = (2,2), padding = 'same',
                          activation = 'relu',
                          kernel_initializer='he_normal')(conv8_2)
    up9_1 = BatchNormalization()(up9_1)
    up9_2 = concatenate([conv1_2, up9_1], axis = 3)
    conv9_1 = SeparableConv2D(64, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(up9_2)
    conv9_1 = BatchNormalization()(conv9_1)
    conv9_2 = SeparableConv2D(64, (3,3), padding = 'same', activation = 'relu',
                   kernel_initializer='he_normal')(conv9_1)
    conv9_2 = BatchNormalization()(conv9_2)
    conv9_3 = SeparableConv2D(2, 3, activation = 'relu', padding = 'same',
                   kernel_initializer = 'he_normal')(conv9_2)
    conv9_3 = BatchNormalization()(conv9_3)

    conv10 = SeparableConv2D(1, 1, activation = 'sigmoid')(conv9_3)

    if(vis):
        outputs = [conv1_1, conv1_2, pool1, conv2_1, conv2_2, pool2, conv3_1,
                   conv3_2, pool3, conv4_1, conv4_2, drop4_2, pool4, conv5_1,
                   conv5_2, drop5_2, up6_1, up6_2,conv6_1, conv6_2, up7_1,
                   up7_2, conv7_1, conv7_2, up8_1, up8_2, conv8_1, conv8_2,
                   up9_1, up9_2, conv9_1, conv9_2, conv9_3, conv10]

        model = Model(inputs = [inputs], outputs = outputs)
        model.summary()
    else:
        model = Model(inputs = [inputs], outputs = [conv10])
        model.summary()

    model.compile(optimizer=Adam(lr=1e-3), loss=dice_coef_loss,
                  metrics=[dice_coef, 'accuracy'])

    return model


#Metody dodaja do klasy rozne funkcjonalnosci. Wszystkie metody musza miec 'self' jako ich
#pierwszy parametr

class Dog:
    legs = 4    # Klasy moga miec argumenty
                # Mozna miec do nich dostep z instancji klasy lub z samej klasy (patrz nizej)

    def __init__(self, name, colors):
        self.name = name
        self.colors = colors

    def bark(self):
        print("Woof!")

fido = Dog("Fido","brown")
print(fido.name)
fido.bark()

print(fido.legs)
print(Dog.legs)


import sys
sys.path.insert(1, "../../../")
import h2o
from tests import pyunit_utils
from h2o.estimators.coxph import H2OCoxProportionalHazardsEstimator

def coxph_smoke():
    heart = h2o.import_file(pyunit_utils.locate("smalldata/coxph_test/heart.csv"))
    coxph = H2OCoxProportionalHazardsEstimator(start_column="start", stop_column="stop")
    coxph.train(x="age", y="event", training_frame=heart)

    assert coxph.model_id != ""
    assert coxph.formula() == "Surv(start, stop, event) ~ age", \
        "Expected formula to be 'Surv(start, stop, event) ~ age' but it was " + coxph.formula()

    pred = coxph.predict(test_data=heart)
    assert len(pred) == len(heart)

if __name__ == "__main__":
    pyunit_utils.standalone_test(coxph_smoke)
else:
    coxph_smoke()


##############################################################################
#
# OSIS stands for Open Student Information System. It's an application
#    designed to manage the core business of higher education institutions,
#    such as universities, faculties, institutes and professional schools.
#    The core business involves the administration of students, teachers,
#    courses, programs and so on.
#
#    Copyright (C) 2015-2016 Université catholique de Louvain (http://www.uclouvain.be)
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    A copy of this license - GNU General Public License - is available
#    at the root of the source code of this program.  If not,
#    see http://www.gnu.org/licenses/.
#
##############################################################################
from django.db import models
from django.db.models import Q
from django.utils import timezone

from osis_common.models.serializable_model import SerializableModel, SerializableModelAdmin

class PropositionOfferAdmin(SerializableModelAdmin):
    list_display = ('proposition_dissertation', 'offer_proposition')
    raw_id_fields = ('proposition_dissertation', 'offer_proposition')
    search_fields = ('uuid', 'proposition_dissertation__title', 'offer_proposition__acronym',
                     'proposition_dissertation__author__person__last_name',
                     'proposition_dissertation__author__person__first_name')

class PropositionOffer(SerializableModel):
    proposition_dissertation = models.ForeignKey('PropositionDissertation', on_delete=models.CASCADE)
    offer_proposition = models.ForeignKey('OfferProposition', on_delete=models.CASCADE)

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

def find_visible_by_education_groups(education_groups):
    now = timezone.now()
    return PropositionOffer.objects.filter(
        proposition_dissertation__active=True,
        proposition_dissertation__visibility=True,
        offer_proposition__education_group__in=education_groups,
        offer_proposition__start_visibility_proposition__lte=now,
        offer_proposition__end_visibility_proposition__gte=now
    )

def search(education_groups, terms, active=None, visibility=None):
    queryset = PropositionOffer.objects.filter(proposition_dissertation__active=True,
                                               proposition_dissertation__visibility=True,
                                               offer_proposition__education_group__in=education_groups,
                                               offer_proposition__start_visibility_proposition__lte=timezone.now()) \
        .distinct()
    if terms:
        queryset = queryset.filter(
            Q(proposition_dissertation__title__icontains=terms) |
            Q(proposition_dissertation__description__icontains=terms) |
            Q(proposition_dissertation__author__person__first_name__icontains=terms) |
            Q(proposition_dissertation__author__person__middle_name__icontains=terms) |
            Q(proposition_dissertation__author__person__last_name__icontains=terms) |
            Q(offer_proposition__education_group__educationgroupyear__acronym__icontains=terms)
        )
    if active:
        queryset = queryset.filter(proposition_dissertation__active=active)
    elif visibility:
        queryset = queryset.filter(proposition_dissertation__visibility=visibility)
    queryset = queryset.distinct()
    return queryset


#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Python Future imports
from __future__ import unicode_literals, absolute_import, print_function

# Python System imports

PUSH_EVENT = 'push'
ISSUE_EVENT = 'issue'
TAG_EVENT = 'tag_push'
COMMENT_EVENT = 'note'
MERGE_EVENT = 'merge_request'
BUILD_EVENT = 'build'
CI_EVENT = 'ci'


from unittest import TestCase
from Solution import Solution
from Solution import ListNode

class TestSolution(TestCase):

    solution = Solution()

    def testMyAtio(self):
        num = self.solution.myAtoi('')
        self.assertEqual(0, num)

        num = self.solution.myAtoi('42')
        self.assertEqual(42, num)

        num = self.solution.myAtoi('    -42')
        self.assertEqual(-42, num)

        num = self.solution.myAtoi('4193 with words')
        self.assertEqual(4193, num)

        num = self.solution.myAtoi('words and 987')
        self.assertEqual(0, num)

        num = self.solution.myAtoi('-91283472332')
        self.assertEqual(-2147483648, num)

    def testLengthOfLongestSubstring(self):
        len1 = self.solution.lengthOfLongestSubstring("abcabcbb")
        self.assertEqual(3, len1)

        len2 = self.solution.lengthOfLongestSubstring("bbbbb")
        self.assertEqual(1, len2)

        len3 = self.solution.lengthOfLongestSubstring("pwwkew")
        self.assertEqual(3, len3)

        len4 = self.solution.lengthOfLongestSubstring("abcabzw")
        self.assertEqual(5, len4)

    def testMultiply(self):
        num1 = self.solution.multiply("2", "3")
        self.assertEqual("6", num1)

        num2 = self.solution.multiply("123", "456")
        self.assertEqual("56088", num2)

        num3 = self.solution.multiply("1", "000")
        self.assertEqual("0", num3)

        num4 = self.solution.multiply("4", "123456789")
        self.assertEqual("493827156", num4)

    def testAddTwoNumbers(self):
        l1 = self.__buildList(2, 4, 9)
        l2 = self.__buildList(5, 6, 4, 9, 9)

        lRes = self.__buildList(7, 0, 4, 0, 0, 1)

        solRes = self.solution.addTwoNumbers(l1, l2)

        self.assertTrue(lRes.equals(solRes))
        # self.assertEqual(lRes.val, solRes.val)
        # self.assertEqual(lRes.next.val, solRes.next.val)
        # self.assertEqual(lRes.next.next.val, solRes.next.next.val)

    #region Private Methods
    def __buildList(self, *nums):
        head = ListNode(0)
        currentNode = head
        for i in range(len(nums)):
            currentNode.next = ListNode(nums[i])
            currentNode = currentNode.next

        head = head.next
        return head
    #endregion


#!/usr/bin/env python
import numpy as np
import matplotlib
matplotlib.use('Agg') # force matplotlib to NOT use X (https://stackoverflow.com/questions/2801882/generating-a-png-with-matplotlib-when-display-is-undefined)
import matplotlib.pyplot as plt

LOGS_DIR = '/home/cs244/logs/'
PLOTS_DIR = '/home/cs244/plots/'

NNODES = 350

upload_caps = [16, 64, 128, 300, 512, 1024]
upload_caps_strs = [str(i) for i in upload_caps]
regular_durations = []
bittyrant_durations = []

# Generate an upload cap distribution that roughly
# models the CDF in Figure 1 of the Anderson paper
# (values are in KBps)
def generate_distribution(i):
    if 0 <= i <= 100:
        return 64
    if 101 <= i <= 200:
        return 96
    if 201 <= i <= 300:
        return 128
    if 301 <= i <= 340:
        return 1000
    else:
        return 10000

# Generate the upload cap distribution used by the peer nodes
upload_x = range(0, NNODES)
upload_y = [generate_distribution(x) for x in upload_x]

print("generating and serving plots from the log files...")

# we want to get two arrays of durations, for BitTyrant and regular,
# from the start and end times in the logs

with open(LOGS_DIR + 'regular', 'r') as ins:
    for line in ins:
        toks = line.split()
        regular_durations.append(int(toks[1]) - int(toks[0]))

with open(LOGS_DIR + 'bittyrant', 'r') as ins:
    for line in ins:
        toks = line.split()
        bittyrant_durations.append(int(toks[1]) - int(toks[0]))

# Now, generate the plots
fig, ax = plt.subplots()
index = np.arange(len(regular_durations))
bar_width = 0.35
opacity = 0.8

rects1 = plt.bar(index, regular_durations, bar_width, alpha=opacity, color='b', label='non-BitTyrant')
rects2 = plt.bar(index + bar_width, bittyrant_durations, bar_width, alpha=opacity, color='r', label='BitTyrant')

plt.xlabel('Peer upload cap')
plt.ylabel('Time-to-completion (seconds)')
plt.title('Time-to-completion vs. allowed upload cap')
plt.xticks(index + bar_width, upload_caps_strs)
plt.legend()

# TODO
#os.system('sudo python -m SimpleHTTPServer 80')

plt.tight_layout()
fig.savefig(PLOTS_DIR + 'fig1.png')
plt.close(fig)

# and generate the distribution plot
ax = plt.axes()
ax.set_yscale('log')

plt.bar(upload_x, upload_y, align="center")

plt.title("Swarm peer upload cap vs. peer number")
plt.ylabel("Upload cap (KBps)")
plt.grid(True)

plt.tight_layout()

fig = plt.figure(1)
fig.savefig(PLOTS_DIR + 'fig2.png')
plt.close(fig)

#plt.show()

# TODO
#os.system('sudo python -m SimpleHTTPServer 80')

for dur in regular_durations:
    print("reg dur: " + str(dur))

print("-----")

for dur in bittyrant_durations:
    print("bittyrant dur: " + str(dur))


from datastructures import *
from unittest import TestCase
from random import shuffle

class TestTwoThreeFourTree(TestCase):
    def test_init_and_delete(self):
        t = AdtTwoThreeFourTree()
        self.assertTrue(t.is_empty())
        self.assertEqual(t.root, None)
        t[0] = 45
        t[1] = 10
        self.assertFalse(t.is_empty())
        self.assertNotEqual(t.root, None)

    def test_insert(self):
        t = AdtTwoThreeFourTree()
        t[10] = "10a"
        t[10] = "10b"
        t[60] = "60"
        t[10] = "10c"
        self.assertFalse(t.is_empty())
        t[30] = "30"
        t[40] = "40"
        self.assertEqual(t.root.amount, 1)
        t[70] = "70"
        self.assertEqual(t.root.amount, 1)
        t[90] = "90"
        self.assertEqual(t.root.amount, 2)
        self.assertEqual(t.root.children[0].amount, 1)
        self.assertEqual(t.root.children[1].amount, 1)
        self.assertEqual(t.root.children[2].amount, 2)
        self.assertEqual(t.root.children[3], None)

    def test_insert_2(self):
        t = AdtTwoThreeFourTree()
        keys = [60, 30, 10, 20, 50, 40, 70, 80, 15, 90, 100]
        for k in keys:
            t[k] = k
        # filename = "test2.dot"
        # with open(filename, "w") as file:
        #     file.write(repr(t))
        for k in keys:
            self.assertTrue(k in t)

    def test_insert_and_delete(self):
        t = AdtTwoThreeFourTree()
        keys = [60, 30, 10, 20, 50, 40, 70, 80, 15, 90, 100]
        for k in keys:
            t[k] = k
        to_delete = keys[4:]
        # filename = "test_delete_.dot"
        # with open(filename, "w") as file:
        #     file.write(repr(t))
        # i = 0
        for k in to_delete:
            del t[k]
            keys.remove(k)
            # filename = "test_" + str(i) + "delete_" + str(k) + ".dot"
            # with open(filename, "w") as file:
            #     file.write(repr(t))
            # i += 1
        for k in keys:
            self.assertTrue(k in t)
        for k in to_delete:
            self.assertFalse(k in t)

    def test_delete(self):
        t = AdtTwoThreeFourTree()
        k1 = [10, 100, 30, 80, 50]
        k2 = [60, 70, 40]
        k3 = [90, 20]
        keys = [100, 50, 60, 40, 90, 20]
        for k in k1:
            t[k] = k
        del t[10]
        for k in k2:
            t[k] = k
        del t[80]
        for k in k3:
            t[k] = k
        del t[30]
        del t[70]
        for k in keys:
            self.assertTrue(k in t)
        for k in [10, 80, 30, 70, 4]:
            self.assertFalse(k in t)

    def test_duplicate(self):
        t = AdtTwoThreeFourTree()
        t[10] = "10a"
        t[10] = "10b"
        t[10] = "10c"
        t[30] = "30a"
        t[90] = "90a"
        t[90] = "90b"
        t[80] = "80a"
        self.assertEqual(t[10], "10a")
        del t[10]
        self.assertEqual(t[10], "10b")
        t[50] = "50a"
        del t[10]
        self.assertEqual(t[10], "10c")
        del t[10]
        self.assertFalse(10 in t)
        with self.assertRaises(KeyError):
            x = t[10]
        self.assertTrue(30 in t)
        self.assertTrue(90 in t)
        self.assertTrue(80 in t)
        self.assertTrue(50 in t)
        self.assertEqual(t[30], "30a")
        self.assertEqual(t[90], "90a")
        self.assertEqual(t[80], "80a")
        self.assertEqual(t[50], "50a")
        del t[90]
        self.assertEqual(t[90], "90b")
        del t[90]
        self.assertFalse(90 in t)
        with self.assertRaises(KeyError):
            x = t[90]
        del t[30]
        del t[80]
        del t[50]
        self.assertFalse(30 in t)
        self.assertFalse(80 in t)
        self.assertFalse(50 in t)
        with self.assertRaises(KeyError):
            x = t[30]
        with self.assertRaises(KeyError):
            x = t[80]
        with self.assertRaises(KeyError):
            x = t[50]
        self.assertTrue(t.is_empty())

    def test_inorder_traverse(self):
        t = AdtTwoThreeFourTree()
        keys = [25, 45, 15, 75, 60, 30, 10, 20, 50, 40, 70, 20, 80, 15, 90, 100]
        for k in keys:
            t[k] = k
        sorted = []
        t.inorder_traverse_table(sorted.append)
        self.assertEqual([10, 15, 15, 20, 20, 25, 30, 40, 45, 50, 60, 70, 75, 80, 90, 100], sorted)

    def test_inorder(self):
        t = AdtTwoThreeFourTree()
        t[9] = "w"
        t[4] = "g"
        t[4] = "h"
        t[4] = "i"
        t[6] = "m"
        t[8] = "t"
        t[7] = "p"
        t[2] = "a"
        t[2] = "b"
        t[6] = "n"
        t[3] = "d"
        t[9] = "x"
        t[2] = "c"
        t[7] = "q"
        t[6] = "o"
        t[7] = "r"
        t[5] = "j"
        t[3] = "e"
        t[7] = "s"
        t[3] = "f"
        t[5] = "k"
        t[8] = "u"
        t[8] = "v"
        t[5] = "l"
        t[9] = "y"
        t[9] = "z"
        sorted = []
        t.inorder_traverse_table(sorted.append)
        self.assertEqual(["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"], sorted)

    def test_mega_fuzz(self):
        rounds = 20
        unique_insertions = 10

        print()
        for k in range(1, rounds):
            print('Fuzz round', k)

            keys = [x for x in range(1, unique_insertions)]
            shuffle(keys)

            for i in range(0, 4):
                # Duplicate the last N elements for insertion
                duplicates = keys[-10:]
                keys.extend(duplicates)
                shuffle(keys)

            # Explicitly creates duplicates of a higher multiplicity.
            duplicates = keys[-3:]
            for i in range(0, 3):
                keys.extend(duplicates)
            shuffle(keys)
            tree = AdtTwoThreeFourTree()
            # Insert all keys.
            for i in keys:
                tree[i] = i

            # Check for validity.
            #inorder_list_of_keys = [x[0] for x in rb]
            #self.assertEqual(sorted(inorder_list_of_keys), inorder_list_of_keys)

            # Check if everything can be found.
            for i in keys:
                self.assertTrue(i in tree)

            copy_of_ = list(keys)
            original = list(keys)
            shuffle(keys)

            removed = []

            for i in keys:
                # if i == 3:
                #     filename = "fuz_234_" + str(k) + ".dot"
                #     with open(filename, "w") as file:
                #         file.write(repr(tree))
                del tree[i]
                original.remove(i)
                removed.append(i)

                #inorder_list_of_keys = [x[0] for x in rb]
                #self.assertEqual(sorted(inorder_list_of_keys), inorder_list_of_keys)

                # If it's still in the original, then it also must be in our tree.
                for j in original:
                    self.assertTrue(j in tree)

                for j in removed:
                    # Duplicates can be found multiple times.
                    if j in original:
                        self.assertTrue(j in tree)
                    else:
                        self.assertFalse(j in tree)


from mmmm import slack
from mmmm import email

# Platform adapter classes by name
platform_class = {
    'slack': slack.Slack,
    'email': email.Email,
}

# Platform names by adapter class
platform_name = {v: k for k, v in platform_class.items()}


import face_recognition
import cv2
import numpy as np
import config
import time, math
from datetime import datetime, timedelta

NEW_EVENT_MINS = 5

video_capture = cv2.VideoCapture(0)
video_capture.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
video_capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 300)

names, faces = config.loadJson()
print(str(len(names)) + ' items loaded')
print('names loaded')
print(names)
print('faces loaded')
print(faces)

# Create arrays of known face encodings and their names
known_face_encodings = []
known_face_names = []
known_face_metadata = []

def register_new_face(face_encoding, face_image):
    known_face_encodings.append(face_encoding)
    known_face_metadata.append({
        "first_seen": datetime.now(),
        "first_seen_this_interaction": datetime.now(),
        "last_seen": datetime.now(),
        "seen_count": 1,
        "seen_frames": 1,
        "face_image": face_image,
    })

def lookup_known_face(face_encoding):
    metadata = None

    if len(known_face_encodings) == 0:
        return metadata

    face_distances = face_recognition.face_distance(
        known_face_encodings,
        face_encoding
    )

    best_match_index = np.argmin(face_distances)

    if face_distances[best_match_index] < 0.65:
        metadata = known_face_metadata[best_match_index]
        metadata["last_seen"] = datetime.now()
        metadata["seen_frames"] += 1

        if datetime.now() - metadata["first_seen_this_interaction"] > timedelta(minutes=NEW_EVENT_MINS):
            metadata["first_seen_this_interaction"] = datetime.now()
            metadata["seen_count"] += 1

    return metadata

# Initialize some variables
face_locations = []
face_encodings = []
face_names = []
process_this_frame = True

fps = video_capture.get(cv2.CAP_PROP_FPS)
fps_count = 0

print(fps)
second = math.floor(time.time())
while True:
    fps_count += 1
    if second != math.floor(time.time()):
        fps = fps_count
        fps_count = 0
        second = math.floor(time.time())

    # Grab a single frame of video
    ret, frame = video_capture.read()

    # Resize frame of video to 1/4 size for faster face recognition processing
    small_frame = cv2.resize(frame, (0, 0), fx=0.25, fy=0.25)

    # Convert the image from BGR color (which OpenCV uses) to RGB color (which face_recognition uses)
    rgb_small_frame = small_frame[:, :, ::-1]

    # Only process every other frame of video to save time
    if process_this_frame:
        # Find all the faces and face encodings in the current frame of video
        face_locations = face_recognition.face_locations(rgb_small_frame)
        face_encodings = face_recognition.face_encodings(rgb_small_frame, face_locations)

        # face_names = []
        # for face_encoding in face_encodings:
        #     # See if the face is a match for the known face(s)
        #     matches = face_recognition.compare_faces(known_face_encodings, face_encoding)
        #     name = "Unknown"
        #
        #     # # If a match was found in known_face_encodings, just use the first one.
        #     # if True in matches:
        #     #     first_match_index = matches.index(True)
        #     #     name = known_face_names[first_match_index]
        #
        #     # Or instead, use the known face with the smallest distance to the new face
        #     face_distances = face_recognition.face_distance(known_face_encodings, face_encoding)
        #     best_match_index = np.argmin(face_distances)
        #     if matches[best_match_index]:
        #         name = known_face_names[best_match_index]
        #
        #     face_names.append(name)
        #     print(name + ' detectd at ' + str(second))

        face_labels = []
        for face_location, face_encoding in zip(face_locations, face_encodings):
            # See if this face is in our list of known faces.
            metadata = lookup_known_face(face_encoding)

            # If we found the face, label the face with some useful information.
            if metadata is not None:
                time_at_location = datetime.now() - metadata['first_seen_this_interaction']
                face_label = f"At location {int(time_at_location.total_seconds())}s"

            # If this is a brand new face, add it to our list of known faces
            else:
                face_label = "New person!"

                # Grab the image of the the face from the current frame of video
                top, right, bottom, left = face_location
                face_image = small_frame[top:bottom, left:right]
                face_image = cv2.resize(face_image, (150, 150))

                # Add the new face to our known face data
                register_new_face(face_encoding, face_image)

            face_labels.append(face_label)
            #print(face_label)

    process_this_frame = not process_this_frame

    font = cv2.FONT_HERSHEY_DUPLEX

    # Display the results
    for (top, right, bottom, left), face_label in zip(face_locations, face_labels):
        # Scale back up face locations since the frame we detected in was scaled to 1/4 size
        top *= 4
        right *= 4
        bottom *= 4
        left *= 4

        # Draw a box around the face
        cv2.rectangle(frame, (left, top), (right, bottom), (255, 255, 255), 2)

        # Draw a label with a name below the face
        #cv2.rectangle(frame, (left, bottom - 35), (right, bottom), (255, 255, 255), cv2.FILLED)

        #cv2.putText(frame, face_label, (left + 6, bottom - 6), font, 0.8, (255, 255, 255), 1)

    cv2.putText(frame, str(fps) + ' fps', (5, 22), font, 0.5, (0, 255, 0), 1)
    # Display the resulting image
    cv2.imshow('Video', frame)

    # Hit 'q' on the keyboard to quit!
    if cv2.waitKey(1) & 0xFF == ord('q'):
        #save_known_faces()
        break

    # if len(face_locations) > 0 and number_of_faces_since_save > 100:
    #     save_known_faces()
    #     number_of_faces_since_save = 0
    # else:
    #     number_of_faces_since_save += 1

# Release handle to the webcam
video_capture.release()
cv2.destroyAllWindows()


def func_high_n_pctile(InRaster, PctileValue, Pctile ="Gt"):
    import os
    import arcpy
    import numpy as np
    from arcpy.sa import *
    arcpy.CheckOutExtension("spatial")

    RasterObj = InRaster
    RasCols = RasterObj.width
    RasRows = RasterObj.height
    llx = RasterObj.extent.XMin
    lly = RasterObj.extent.YMin

    myCellheight = RasterObj.meanCellHeight
    myCellwidth = RasterObj.meanCellWidth

    RasMat = arcpy.RasterToNumPyArray(RasterObj, arcpy.Point(llx,lly),RasCols,RasRows,-9999)
    RasMat2 = RasMat[RasMat>-9998.]
    Ras_npctile = np.percentile(RasMat2, Pctile)

    if Pctile == "Gt":
        Ras_gt_npctile = Con(RasterObj > Ras_npctile, RasterObj)
    else:
        if Pctile == "Lt":
            Ras_gt_npctile = Con(RasterObj < Ras_npctile, RasterObj)
    #Ras_gt_5pctile.save(working_dir + "\\" + Outfile)

    return Ras_gt_npctile


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import os
import sys
import shutil
import make_data as md

def compile_source(src, dest):
    if os.system("g++ -g %s -o %s" % (src, dest)) != 0:
        print("%s: Compilation Error!\n" % src)
        sys.exit(-1)

def check(prog1, prog2, k, make = md.make_r):
    for i in range(k):
        print("Test %s:" % i)
        data = make(100, 3000)
        with open("running.in", "w") as f:
            f.write(data)
        os.system("%s" % prog1)
        shutil.move("./running.out", "%s.out" % prog1)
        os.system("%s" % prog2)
        shutil.move("./running.out", "%s.out" % prog2)

        if os.system("diff %s.out %s.out" % (prog1, prog2)) != 0:
            print("Difference between %s.out and %s.out" % (prog1, prog2))
            sys.exit(-1)

def check_std(src):
    compile_source(src, "./std")
    compile_source("../Chenyao2333/1-5.cpp", "./naive")
    check("./std", "./naive", 100)

def check_6_to_8(src):
    compile_source(src, "./6-8")
    compile_source("../Chenyao2333/1-5.cpp", "./naive")
    check("./6-8", "./naive", 100, md.make_l)

if __name__ == "__main__":
    # check_std("../Chenyao2333/std.cpp")
    check_6_to_8("../Chenyao2333/6-8.cpp")


import numpy as np
import random
import os
import requests, sys

module_dir = os.path.dirname(__file__)  # fetches path of directory that this file is in for use with importing .txt files

# extract variables from request

def create_oligo_dict(textfile):
    """function that creates an oligo dictionary from a text file"""
    """takes no arguments as currently written"""
    """returns the oligo dictionary"""

    with open (os.path.join(module_dir, textfile), 'r') as in_file:
        oligo_dict ={}
        for line in in_file.readlines():
            splitline = line.rstrip().split("\t")
            oligo_dict[splitline[0]]=splitline[1]
    return oligo_dict
    # tested: good

def gene_sequence():
    """function that reads a sample gene file and returns sequence as a list"""

    # with open('Homo_sapiens_RBFOX2_sequence_clean.txt', 'r') as in_file:
    with open (os.path.join(module_dir, 'Homo_sapiens_BRCA2.txt'), 'r') as in_file:
        ref_sequence=[]
        for line in in_file.readlines():
            ref_sequence += line.rstrip().upper()
    return ref_sequence
    # tested: good
    # need to fix path issues on gene file

def random_SNP_location(sequence):
    """function that generates a random SNP position in a query sequence"""
    """ --> note that the first nucleotide is defined as position 1, not 0 <-- """

    return random.randint(1, len(sequence)-5)
    # tested: good

def query_generator():
    """function that generates a sample query"""
    """takes no arguments as currently written"""
    """returns a ref_sequence, SNP locator and SNP substitution as tuple"""

    seq=gene_sequence()
    N=random_SNP_location(seq)
    major_allele = seq[N-1]
    nt_dict={1:'A', 2:'C', 3:'G', 4:'T'}
    condition = False
    while condition == False:
        n=random.randint(1,4)
        snp_outcome = nt_dict[n]
        if snp_outcome != major_allele:
            condition = True
    return (seq,N,snp_outcome)
    # tested: good

# FEED INTO THE FOLLOWING FUNCTION THE OUTPUT OF THE RANDOM QUERY GENERATOR or
# AN ACTUAL REFERENCE SEQUENCE, SNP LOCATION AND NUCLEOTIDE SUBSTITUTION

def query_neighbourhood(argtuple, size):
    """function that creates the reference and mutant neighbourhoods"""
    """this is the key data source that sets the html response"""
    """takes as args a reference sequence, locator and SNP as tuple"""
    """size arg defines length of oligo of interest"""
    """eventual neighbourhood length will be 2*(size+4)+1"""
    """returns the reference and query neighbourhoods as tuple"""
    Ol=int(size)
    S=Ol+4
    N=argtuple[1]
    wild_neighbourhood = argtuple[0][N-S-1:N+S]
    snp_neighbourhood = list(wild_neighbourhood)
    snp_neighbourhood[S]=argtuple[2].upper()
    return (wild_neighbourhood,snp_neighbourhood)
    # tested: good

def analyse_neighbourhood(neighbourhood, oligo_dict, size):
    """function that compiles dictionary hit data for a 2S+1 sized neighbourhood"""
    """may be used for analysing both wild and snp neighbourhoods"""
    """creates list of hits as '0' or '1' mask"""
    """creates list of oligos as strings"""
    """creates list of labels as strings"""
    """returns these lists in tuple wrapper"""

    Ol=int(size)
    mask=[]
    oligos=[]
    labels=[]
    for k in range((1-Ol),1):
        oligo="".join(neighbourhood[((Ol+4)+k):(2*Ol+4+k)])
        oligos.append(oligo)
        if oligo in oligo_dict:
            mask.append('1')
            labels.append(oligo_dict[oligo])
        else:
            mask.append('0')
            labels.append('')
    return (mask, oligos, labels)
    # tested: good

def analyse_effects(wild_mask, snp_mask, size):
    """function that uses the masks to generate descriptions of SNP effects"""
    """returns a list with the effect at each hexamer position"""

    Ol=int(size)
    effects=[]
    for i in range (0,Ol):
        if wild_mask[i] == '1':
            if snp_mask[i] =='1':
                effects.append('Mutated')
            else:
                effects.append('Lost')
        else:
            if snp_mask[i] =='1':
                effects.append('Gained')
            else:
                effects.append('')
    return effects
    # tested: good

def create_array(querytuple, querytype, size):
    """function that creates the character array"""
    """argtuple is a reference sequence, locator and SNP wrapped as a tuple"""
    """can simply take query_generator() as arg to create random test sample"""
    """outputs all the data needed to write the display tables"""

    Ol=int(size)
    width = int(2*(Ol+4)+1)
    height = int(2*Ol+3)
    if querytype == 'ESE':
        oligo_dict=create_oligo_dict('ESE.txt')
    elif querytype == 'ESS':
        oligo_dict=create_oligo_dict('ESS.txt')
    elif querytype == 'ISE':
        oligo_dict=create_oligo_dict('ISE.txt')
    elif querytype == 'ISS':
        oligo_dict=create_oligo_dict('ISS.txt')
    elif querytype == 'CSS5':
        oligo_dict=create_oligo_dict('GT_scores.txt')
    elif querytype == 'CSS3':
        oligo_dict=create_oligo_dict('AG_scores.txt')
    elif querytype == 'CSSBP':
        oligo_dict=create_oligo_dict('BP_scores.txt')
    else:
        oligo_dict=create_oligo_dict('ESE.txt')
    neighbourhoods=query_neighbourhood(querytuple, Ol)
    wild_neighbourhood=neighbourhoods[0]
    snp_neighbourhood=neighbourhoods[1]
    wild_lists = analyse_neighbourhood(wild_neighbourhood, oligo_dict, Ol)
    snp_lists = analyse_neighbourhood(snp_neighbourhood, oligo_dict, Ol)

    output = np.chararray((height,width)) # creates empty array
    output[:]=""
    output[Ol,0:width]=wild_neighbourhood # loads reference sequence
    output[(Ol+2),0:width]=snp_neighbourhood # loads mutant sequence
    output[(Ol+1),(Ol+4)]='*'
    for i in range (0,Ol):
        output[i,i+5:i+Ol+5]= wild_neighbourhood[i+5:i+Ol+5]
        output[(i+Ol+3),i+5:i+Ol+5]= snp_neighbourhood[i+5:i+Ol+5]
    return (output, wild_lists, snp_lists,Ol)
    # tested: now working
    # corrected error under which array was being created not truly empty
    # some cosmetics still required on * and sequence labels
    # need to resize tableone

def splice_table(create_array_result):
    """arg should be formatted to match the output of create_array(*args)"""
    """will return an html file as a dense string"""
    """call this as "splice_table" variable for splicing.html"""
    """table includes class tags for CSS"""
    """wild/snp_lists are ordered as mask, hexamers, labels"""

    html=[]
    Ol = create_array_result[3]
    width=2*(Ol+4)+1
    height=2*Ol+3
    output=create_array_result[0]
    wild_lists=create_array_result[1]
    wild_mask=wild_lists[0]
    snp_lists=create_array_result[2]
    snp_mask=snp_lists[0]
    html.append('
')
    html.append('
')
    for i in range(0,Ol):
        html.append('
')
        for j in range(0,width):
            indw=wild_mask[i]
            if j==Ol+4:
                html.append("".join(['',output[i,j],'	']))
            else:
                html.append("".join(['',output[i,j],'	']))
        html.append('
')
    for i in range(Ol,Ol+3):
        html.append('
')
        for j in range(0,width):
            if j==Ol+4:
                if output[i,j] == '*':
                    new = '▼'
                    html.append("".join([' ', new, ' 	']))
                else:
                    html.append("".join(['',output[i,j],'	']))
            else:
                html.append("".join(['',output[i,j],'	']))
        html.append('
')
    for i in range(height-Ol,height):
        html.append('
')
        for j in range(0,width):
            inds=snp_mask[i-(Ol+3)]
            if j==Ol+4:
                html.append("".join(['',output[i,j],'	']))
            else:
                html.append("".join(['',output[i,j],'	']))
        html.append('
')
    html.append('
')
    html.append('
')
    table="".join(html)
    return table
    # tested: working

def effects_table(create_array_result):
    """arg should be formatted to match the output of create_array(*args)"""
    """will return an html file as a dense string"""
    """call this function as "effects_table" variable for splicing.html"""
    """table includes class tags for CSS"""

    html=[]
    Ol = create_array_result[3]
    headings = ['Wild','Type','Mutant','Type','Effect']
    wild_lists=create_array_result[1]
    snp_lists=create_array_result[2]
    effects=analyse_effects(wild_lists[0],snp_lists[0],Ol)
    content = [wild_lists[1], wild_lists[2], snp_lists[1], snp_lists[2], effects]
    html=[]
    html.append('
')
    html.append('
')
    html.append('
')
    for p in range(0,5):
        html.append("".join(['',headings[p],'	']))
    html.append('
')
    for q in range(0,Ol):
        html.append('
')
        for p in range(0,5):
            indw=wild_lists[0][q]
            inds=snp_lists[0][q]
            if p==0:
                html.append("".join(['',content[p][q],'	']))
            elif p==2:
                html.append("".join(['',content[p][q],'	']))
            else:
                html.append("".join(['',content[p][q],'	']))
        html.append('
')
    html.append('
')
    html.append('
')
    table="".join(html)
    return table
    # tested: working

def scores_table(create_array_result):
    """arg should be formatted to match the output of create_array(*args)"""
    """will return an html file as a dense string"""
    """call this function as "effects_table" variable for splicing.html"""
    """table includes class tags for CSS"""

    html=[]
    Ol = create_array_result[3]
    headings = ['Wild','Score','Mutant','Score','Delta']
    wild_lists=create_array_result[1]
    snp_lists=create_array_result[2]
    content = [wild_lists[1], wild_lists[2], snp_lists[1], snp_lists[2],[]]

    wild=content[1]
    snp=content[3]
    scores=[]
    for i in range(0,len(wild)):
        if (wild[i]=="") and (snp[i]==""):
            scores.append("")
        elif (wild[i]=="") and (snp[i]!=""):
            scores.append(snp[i])
        elif (wild[i]!="") and (snp[i]==""):
            scores.append(str(-float(wild[i])))
        else:
            scores.append(str(float(snp[i])-float(wild[i])))
    content[4]=scores

    html=[]
    html.append('
')
    html.append('
')
    html.append('
')
    for p in range(0,5):
        html.append("".join(['',headings[p],'	']))
    html.append('
')
    for q in range(0,Ol):
        html.append('
')
        for p in range(0,5):
            indw=wild_lists[0][q]
            inds=snp_lists[0][q]
            if p==0:
                html.append("".join(['',content[p][q],'	']))
            elif p==2:
                html.append("".join(['',content[p][q],'	']))
            else:
                html.append("".join(['',content[p][q],'	']))
        html.append('
')
    html.append('
')
    html.append('
')
    table="".join(html)
    return table
    # tested: working


#!/usr/bin/python
import logging
from copy import deepcopy

from advent import Advent

"""
-- Day 15: Beverage Bandits ---
Having perfected their hot chocolate, the Elves have a new problem: the Goblins
that live in these caves will do anything to steal it. Looks like they're here
for a fight.

You scan the area, generating a map of the walls (#), open cavern (.), and
starting position of every Goblin (G) and Elf (E) (your puzzle input).

Combat proceeds in rounds; in each round, each unit that is still alive takes a
turn, resolving all of its actions before the next unit's turn begins. On each
unit's turn, it tries to move into range of an enemy (if it isn't already)
and then attack (if it is in range).

All units are very disciplined and always follow very strict combat rules. Units
never move or attack diagonally, as doing so would be dishonorable. When
multiple choices are equally valid, ties are broken in reading order:

top-to-bottom, then left-to-right. For instance, the order in which units take
their turns within a round is the reading order of their starting positions in
that round, regardless of the type of unit or whether other units have moved
after the round started. For example:

                   would take their
  These units:   turns in this order:
    #######           #######
    #.G.E.#           #.1.2.#
    #E.G.E#           #3.4.5#
    #.G.E.#           #.6.7.#
    #######           #######

Each unit begins its turn by identifying all possible targets (enemy units). If
no targets remain, combat ends.

Then, the unit identifies all of the open squares (.) that are in range of each
target; these are the squares which are adjacent (immediately up, down, left, or
right) to any target and which aren't already occupied by a wall or
another unit. Alternatively, the unit might already be in range of a target. If
the unit is not already in range of a target, and there are no open squares
which are in range of a target, the unit ends its turn.

If the unit is already in range of a target, it does not move, but continues its
turn with an attack. Otherwise, since it is not in range of a target, it moves.

To move, the unit first considers the squares that are in range and determines
which of those squares it could reach in the fewest steps. A step is a single
movement to any adjacent (immediately up, down, left, or right) open (.)
square. Units cannot move into walls or other units. The unit does this while
considering the current positions of units and does not do any prediction about
where units will be later. If the unit cannot reach (find an open path to)
any of the squares that are in range, it ends its turn. If multiple squares are
in range and tied for being reachable in the fewest steps, the square which is
first in reading order is chosen. For example:

  Targets:      In range:     Reachable:    Nearest:      Chosen:
  #######       #######       #######       #######       #######
  #E..G.#       #E.?G?#       #E.@G.#       #E.!G.#       #E.+G.#
  #...#.#  -->  #.?.#?#  -->  #.@.#.#  -->  #.!.#.#  -->  #...#.#
  #.G.#G#       #?G?#G#       #@G@#G#       #!G.#G#       #.G.#G#
  #######       #######       #######       #######       #######

In the above scenario, the Elf has three targets (the three Goblins):

  - Each of the Goblins has open, adjacent squares which are in range (marked
  with a ? on the map).
  - Of those squares, four are reachable (marked @); the other two (on the
  right) would require moving through a wall or unit to reach.
  - Three of these reachable squares are nearest, requiring the fewest steps
  (only 2) to reach (marked !).
  - Of those, the square which is first in reading order is chosen (+).

The unit then takes a single step toward the chosen square along the shortest
path to that square. If multiple steps would put the unit equally closer to its
destination, the unit chooses the step which is first in reading order. (This
requires knowing when there is more than one shortest path so that you can
consider the first step of each such path.) For example:

  In range:     Nearest:      Chosen:       Distance:     Step:
  #######       #######       #######       #######       #######
  #.E...#       #.E...#       #.E...#       #4E212#       #..E..#
  #...?.#  -->  #...!.#  -->  #...+.#  -->  #32101#  -->  #.....#
  #..?G?#       #..!G.#       #...G.#       #432G2#       #...G.#
  #######       #######       #######       #######       #######

The Elf sees three squares in range of a target (?), two of which are nearest
(!), and so the first in reading order is chosen (+). Under "Distance", each
open square is marked with its distance from the destination square; the two
squares to which the Elf could move on this turn (down and to the right) are
both equally good moves and would leave the Elf 2 steps from being in range of
the Goblin. Because the step which is first in reading order is chosen, the Elf
moves right one square.

Here's a larger example of movement:

  Initially:
  #########
  #G..G..G#
  #.......#
  #.......#
  #G..E..G#
  #.......#
  #.......#
  #G..G..G#
  #########

  After 1 round:
  #########
  #.G...G.#
  #...G...#
  #...E..G#
  #.G.....#
  #.......#
  #G..G..G#
  #.......#
  #########

  After 2 rounds:
  #########
  #..G.G..#
  #...G...#
  #.G.E.G.#
  #.......#
  #G..G..G#
  #.......#
  #.......#
  #########

  After 3 rounds:
  #########
  #.......#
  #..GGG..#
  #..GEG..#
  #G..G...#
  #......G#
  #.......#
  #.......#
  #########

Once the Goblins and Elf reach the positions above, they all are either in range
of a target or cannot find any square in range of a target, and so none of the
units can move until a unit dies.

After moving (or if the unit began its turn in range of a target), the unit
attacks.

To attack, the unit first determines all of the targets that are in range of it
by being immediately adjacent to it.  If there are no such targets, the unit
ends its turn. Otherwise, the adjacent target with the fewest hit points is
selected; in a tie, the adjacent target with the fewest hit points which is
first in reading order is selected.

The unit deals damage equal to its attack power to the selected target, reducing
its hit points by that amount. If this reduces its hit points to 0 or fewer, the
selected target dies: its square becomes . and it takes no further turns.

Each unit, either Goblin or Elf, has 3 attack power and starts with 200 hit
points.

For example, suppose the only Elf is about to attack:

         HP:            HP:
  G....  9       G....  9
  ..G..  4       ..G..  4
  ..EG.  2  -->  ..E..
  ..G..  2       ..G..  2
  ...G.  1       ...G.  1

The "HP" column shows the hit points of the Goblin to the left in the
corresponding row. The Elf is in range of three targets: the Goblin above it
(with 4 hit points), the Goblin to its right (with 2 hit points), and the Goblin
below it (also with 2 hit points). Because three targets are in range, the ones
with the lowest hit points are selected: the two Goblins with 2 hit points each
(one to the right of the Elf and one below the Elf). Of those, the Goblin first
in reading order (the one to the right of the Elf) is selected. The selected
Goblin's hit points (2) are reduced by the Elf's attack power (3), reducing its
hit points to -1, killing it.

After attacking, the unit's turn ends. Regardless of how the unit's turn ends,
the next unit in the round takes its turn. If all units have taken turns in this
round, the round ends, and a new round begins.

The Elves look quite outnumbered. You need to determine the outcome of the
battle: the number of full rounds that were completed (not counting the round in
which combat ends) multiplied by the sum of the hit points of all remaining
units at the moment combat ends. (Combat only ends when a unit finds no targets
during its turn.)

Below is an entire sample combat. Next to each map, each row's units' hit points
are listed from left to right.

  Initially:
  #######
  #.G...#   G(200)
  #...EG#   E(200), G(200)
  #.#.#G#   G(200)
  #..G#E#   G(200), E(200)
  #.....#
  #######

  After 1 round:
  #######
  #..G..#   G(200)
  #...EG#   E(197), G(197)
  #.#G#G#   G(200), G(197)
  #...#E#   E(197)
  #.....#
  #######

  After 2 rounds:
  #######
  #...G.#   G(200)
  #..GEG#   G(200), E(188), G(194)
  #.#.#G#   G(194)
  #...#E#   E(194)
  #.....#
  #######

  Combat ensues; eventually, the top Elf dies:

  After 23 rounds:
  #######
  #...G.#   G(200)
  #..G.G#   G(200), G(131)
  #.#.#G#   G(131)
  #...#E#   E(131)
  #.....#
  #######

  After 24 rounds:
  #######
  #..G..#   G(200)
  #...G.#   G(131)
  #.#G#G#   G(200), G(128)
  #...#E#   E(128)
  #.....#
  #######

  After 25 rounds:
  #######
  #.G...#   G(200)
  #..G..#   G(131)
  #.#.#G#   G(125)
  #..G#E#   G(200), E(125)
  #.....#
  #######

  After 26 rounds:
  #######
  #G....#   G(200)
  #.G...#   G(131)
  #.#.#G#   G(122)
  #...#E#   E(122)
  #..G..#   G(200)
  #######

  After 27 rounds:
  #######
  #G....#   G(200)
  #.G...#   G(131)
  #.#.#G#   G(119)
  #...#E#   E(119)
  #...G.#   G(200)
  #######

  After 28 rounds:
  #######
  #G....#   G(200)
  #.G...#   G(131)
  #.#.#G#   G(116)
  #...#E#   E(113)
  #....G#   G(200)
  #######

  More combat ensues; eventually, the bottom Elf dies:

  After 47 rounds:
  #######
  #G....#   G(200)
  #.G...#   G(131)
  #.#.#G#   G(59)
  #...#.#
  #....G#   G(200)
  #######

Before the 48th round can finish, the top-left Goblin finds that there are no
targets remaining, and so combat ends.  So, the number of full rounds that were
completed is 47, and the sum of the hit points of all remaining units is
200+131+59+200 = 590. From these, the outcome of the battle is 47 * 590 = 27730.

  Here are a few example summarized combats:

  #######       #######
  #G..#E#       #...#E#   E(200)
  #E#E.E#       #E#...#   E(197)
  #G.##.#  -->  #.E##.#   E(185)
  #...#E#       #E..#E#   E(200), E(200)
  #...E.#       #.....#
  #######       #######

  Combat ends after 37 full rounds
  Elves win with 982 total hit points left
  Outcome: 37 * 982 = 36334
  #######       #######
  #E..EG#       #.E.E.#   E(164), E(197)
  #.#G.E#       #.#E..#   E(200)
  #E.##E#  -->  #E.##.#   E(98)
  #G..#.#       #.E.#.#   E(200)
  #..E#.#       #...#.#
  #######       #######

  Combat ends after 46 full rounds
  Elves win with 859 total hit points left
  Outcome: 46 * 859 = 39514
  #######       #######
  #E.G#.#       #G.G#.#   G(200), G(98)
  #.#G..#       #.#G..#   G(200)
  #G.#.G#  -->  #..#..#
  #G..#.#       #...#G#   G(95)
  #...E.#       #...G.#   G(200)
  #######       #######

  Combat ends after 35 full rounds
  Goblins win with 793 total hit points left
  Outcome: 35 * 793 = 27755
  #######       #######
  #.E...#       #.....#
  #.#..G#       #.#G..#   G(200)
  #.###.#  -->  #.###.#
  #E#G#G#       #.#.#.#
  #...#G#       #G.G#G#   G(98), G(38), G(200)
  #######       #######

  Combat ends after 54 full rounds
  Goblins win with 536 total hit points left
  Outcome: 54 * 536 = 28944
  #########       #########
  #G......#       #.G.....#   G(137)
  #.E.#...#       #G.G#...#   G(200), G(200)
  #..##..G#       #.G##...#   G(200)
  #...##..#  -->  #...##..#
  #...#...#       #.G.#...#   G(200)
  #.G...G.#       #.......#
  #.....G.#       #.......#
  #########       #########

Combat ends after 20 full rounds
Goblins win with 937 total hit points left
Outcome: 20 * 937 = 18740
What is the outcome of the combat described in your puzzle input?

--- Part Two ---
According to your calculations, the Elves are going to lose badly. Surely, you
won't mess up the timeline too much if you give them just a little advanced
technology, right?

You need to make sure the Elves not only win, but also suffer no losses: even
the death of a single Elf is unacceptable.

However, you can't go too far: larger changes will be more likely to permanently alter spacetime.

So, you need to find the outcome of the battle in which the Elves have the
lowest integer attack power (at least 4) that allows them to win without a
single death. The Goblins always have an attack power of 3.

In the first summarized example above, the lowest attack power the Elves need to
win without losses is 15:

#######       #######
#.G...#       #..E..#   E(158)
#...EG#       #...E.#   E(14)
#.#.#G#  -->  #.#.#.#
#..G#E#       #...#.#
#.....#       #.....#
#######       #######

Combat ends after 29 full rounds
Elves win with 172 total hit points left
Outcome: 29 * 172 = 4988
In the second example above, the Elves need only 4 attack power:

#######       #######
#E..EG#       #.E.E.#   E(200), E(23)
#.#G.E#       #.#E..#   E(200)
#E.##E#  -->  #E.##E#   E(125), E(200)
#G..#.#       #.E.#.#   E(200)
#..E#.#       #...#.#
#######       #######

Combat ends after 33 full rounds
Elves win with 948 total hit points left
Outcome: 33 * 948 = 31284
In the third example above, the Elves need 15 attack power:

#######       #######
#E.G#.#       #.E.#.#   E(8)
#.#G..#       #.#E..#   E(86)
#G.#.G#  -->  #..#..#
#G..#.#       #...#.#
#...E.#       #.....#
#######       #######

Combat ends after 37 full rounds
Elves win with 94 total hit points left
Outcome: 37 * 94 = 3478
In the fourth example above, the Elves need 12 attack power:

#######       #######
#.E...#       #...E.#   E(14)
#.#..G#       #.#..E#   E(152)
#.###.#  -->  #.###.#
#E#G#G#       #.#.#.#
#...#G#       #...#.#
#######       #######

Combat ends after 39 full rounds
Elves win with 166 total hit points left
Outcome: 39 * 166 = 6474
In the last example above, the lone Elf needs 34 attack power:

#########       #########
#G......#       #.......#
#.E.#...#       #.E.#...#   E(38)
#..##..G#       #..##...#
#...##..#  -->  #...##..#
#...#...#       #...#...#
#.G...G.#       #.......#
#.....G.#       #.......#
#########       #########

Combat ends after 30 full rounds
Elves win with 38 total hit points left
Outcome: 30 * 38 = 1140
After increasing the Elves' attack power until it is just barely enough for them
to win without any Elves dying, what is the outcome of the combat described in
your puzzle input?

"""

logging.basicConfig(level=logging.INFO)
log = logging.getLogger()

class NoMoreEnemies(Exception):
    pass

class DeadElve(Exception):
    pass

class Position(object):
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return '<Pos %s,%s>' % (self.x, self.y)

    def __hash__(self):
        return hash(self.pos)

    def __eq__(self, other):
        return self.x == other.x and self.y == other.y

    @property
    def pos(self):
        return (self.x, self.y)

    def neighbours(self):
        yield Position(self.x, self.y - 1)
        yield Position(self.x - 1, self.y)
        yield Position(self.x + 1, self.y)
        yield Position(self.x, self.y + 1)

    def neighbours_on_grid(self, grid):
        """all neighbour positions that are on the grid and empty"""
        for n in self.neighbours():
            if n not in grid:
                continue  # not part of the grid (wall or outside)
            if grid[n] is None:
                yield n

    def distance(self, other):
        """manhatten distance"""
        return abs(self.x - other.x) + abs(self.y - other.y)

class Unit(Position):
    def __init__(self, x, y, race):
        super(Unit, self).__init__(x, y)
        self.hp = 200
        self.power = 3
        self.race = race

    def __str__(self):
        return '<Unit %s at %s,%s>' % (self.race, self.x, self.y)

    def free(self):
        if self.race == '.':
            return True
        if self.hp <= 0:
            return True
        return False

    def is_enemy(self, other):
        test = (self.race, other.race)
        return test in (('E', 'G'), ('G', 'E'))

    def attack(self, grid):
        enemy = None
        for n in self.neighbours():
            # we rely on neighbours to yield in preferred attack order
            # N, W, E, S
            if n not in grid:
                continue
            if grid[n] is None:
                continue
            if grid[n].race == self.race:
                continue
            if enemy is None:
                enemy = grid[n]
            else:
                if grid[n].hp < enemy.hp:
                    enemy = grid[n]
        if enemy is None:
            log.debug('%s has no target in range', self)
            return
        log.debug('%s attacks %s', self, enemy)
        enemy.hp -= self.power
        if enemy.hp <= 0:
            log.debug('%s has been killed', enemy)
            grid[enemy] = None

    def search_enemy(self, grid):
        """search the closest reachable enemy"""
        log.debug('%s is searching for enemies', self)
        targets = []
        units = filter(None, grid.values())
        units = filter(lambda u: u.race != self.race, units)
        units = list(units)  # otherwise we have filter expression
        if not units:
            raise NoMoreEnemies()
        for unit in units:
            for pos in unit.neighbours():
                try:
                    node = grid[pos]
                except KeyError:
                    # a wall or outside
                    continue
                if node is None:
                    targets.append(pos)
                    continue
                if node.pos == self.pos:
                    targets.append(pos)
                    continue
        # remove duplicates
        targets = set(targets)
        distance = 0
        seen = set()  # nodes visited
        options = [self]
        while options:
            # check if any option is good
            for p in sorted(options, key=lambda n: (n.y, n.x)):
                seen.add(p)
                if p in targets:
                    log.debug('found enemy %s with distance %s', p, distance)
                    return p, distance
            # increment distance, add all new options
            distance += 1
            work = options[:]
            options = []
            for p in work:
                for n in p.neighbours_on_grid(grid):
                    if n in seen:
                        continue
                    if n in options:
                        continue
                    options.append(n)
        return None, None  # no enemy found

    def step(self, enemy, grid):
        """walk back from enemy to one of our neighbour fields
        our neighbour fields will be sorted in preferred order
        """
        seen = set()
        targets = list(self.neighbours_on_grid(grid))
        options = [enemy]
        while options:
            for p in sorted(options, key=lambda n: (n.y, n.x)):
                seen.add(p)
                if p in targets:
                    log.debug('%s moving to %s', self, p)
                    grid[self] = None  # leave field
                    self.x = p.x
                    self.y = p.y
                    grid[self] = self
                    return
            work = options[:]
            for p in work:
                for n in p.neighbours_on_grid(grid):
                    if n in seen:
                        continue
                    if n in options:
                        continue
                    options.append(n)

def print_grid(grid):
    print()
    max_x = max([p.x for p in grid]) + 1
    max_y = max([p.y for p in grid]) + 1
    for y in range(max_y):
        for x in range(max_x):
            try:
                node = grid[Position(x, y)]
            except KeyError:
                print('#', end='')  # noqa
                continue
            if node is None:
                print('.', end='')  # noqa
            else:
                print(node.race, end='')  # noqa
        print()

class Day(Advent):
    lines = True

    def prepare(self):
        super(Day, self).prepare()
        grid = {}
        for y, line in enumerate(self.data):
            for x, value in enumerate(line):
                if value == '#':  # do not note walls
                    continue
                p = Position(x, y)
                if value == '.':
                    grid[p] = None
                else:
                    grid[p] = Unit(x, y, value)
        self.grid = grid

    def units(self, grid):
        units = filter(None, grid.values())
        units = sorted(units, key=lambda n: (n.y, n.x))
        return units

    def battle(self, grid, raise_elve=False):
        """execute one round of the battle"""
        units = self.units(grid)
        for unit in units:
            if unit.hp <= 0:
                continue  # dead, killed this round
            enemy, distance = unit.search_enemy(grid)
            if enemy is None:
                continue
            if distance > 0:
                unit.step(enemy, grid)
            unit.attack(grid)
        if raise_elve:
            for unit in units:
                if unit.hp <= 0 and unit.race == 'E':
                    raise DeadElve()

    def hastings(self, grid, raise_elve=False):
        """execute a complete battle
        (or wait for an elve to die)"""
        result = 0
        while True:
            try:
                self.battle(grid, raise_elve=raise_elve)
            except NoMoreEnemies:
                return result
            result += 1

    def solve1(self):
        grid = deepcopy(self.grid)
        battleround = self.hastings(grid)
        hp = sum([u.hp for u in self.units(grid)])
        return hp * battleround

    def solve2(self):
        power = 4
        while True:
            grid = deepcopy(self.grid)
            # upgrade elves
            for unit in self.units(grid):
                if unit.race == 'E':
                    unit.power = power
            # fight
            try:
                battleround = self.hastings(grid, raise_elve=True)
            except DeadElve:
                power += 1
                continue
            break
        hp = sum([u.hp for u in self.units(grid)])
        return hp * battleround

Day.main()


# -*- coding: utf-8 -*-
import os, scrapy, json
from urllib import parse
from tools.common import *
from scrapy.http import Request
from videoSpider.items import *
from tools.selenium_spider import *
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
from videoSpider.middlewares import SpiderStateMiddleware

class IqiyiSpider(CrawlSpider):
    name = 'iqiyi'
    allowed_domains = ['iqiyi.com']
    start_urls = ['http://list.iqiyi.com']

    # 爬取规则,只对电影 电视剧 综艺 动漫进行跟进
    rules = (
        Rule(LinkExtractor(allow=(r'.*/www/4/.*',
                                  r'.*/www/1/.*',
                                  r'.*/www/2/.*',
                                  r'.*/www/6/.*'))
             , callback='parse_item', follow=True),
    )

    def parse_item(self, response):
        # 对视频列表的解析
        post_nodes = response.css(".wrapper-piclist ul li")
        list_type = response.css(
            "div.mod_sear_menu div.mod_sear_list:nth-child(1) ul li.selected a::text").extract_first("")
        for post_node in post_nodes:
            image_url = parse.urljoin(response.url,
                                      post_node.css(".site-piclist_pic a img::attr(src)").extract_first(""))
            post_url = post_node.css(".site-piclist_pic a::attr(href)").extract_first("")
            video_name = post_node.css(".site-piclist_info .site-piclist_info_title a::text").extract_first("")
            video_actor = post_node.css(".site-piclist_info .role_info em a::text").extract()
            if 'so.iqiyi.com' in post_url or 'src=search' in post_url:
                continue
            if list_type == "电视剧":
                callback = self.parse_detail_1
            elif list_type == "电影":
                if not is_player(post_url):
                    continue
                callback = self.parse_detail_2
            elif list_type == "综艺":
                callback = self.parse_detail_3
            else:
                callback = self.parse_detail_4

            yield Request(url=parse.urljoin(response.url, post_url),
                          meta={"front_image_url": image_url,
                                "list_type": list_type,
                                "video_name": video_name,
                                "video_actor": video_actor},
                          callback=callback,
                          priority=10)
            SpiderStateMiddleware.crawling += 1

    def parse_detail_1(self, response):
        # 对电视剧细节的解析
        v_name = response.meta.get("video_name", "")
        print("正在爬取: %s" % v_name)
        item_loader = VideoItemLoader(item=VideospiderItem(), response=response)

        v_id = response.css("*::attr(data-score-tvid)").extract_first("")
        album_items = response.css(".albumSubTab-wrap .piclist-wrapper ul li")
        des = "div.episodeIntro div.episodeIntro-brief[data-moreorless='lessinfo'] span::text"
        if len(album_items) == 0:
            # 针对架构不一样的电视剧
            des = "div[data-moreorless='lessinfo'] .bigPic-b-jtxt::text"

        play_urls = self.parse_episode(v_id)

        item_loader.add_value("v_id", v_id)
        item_loader.add_value("play_url", play_urls)
        item_loader.add_value("list_type", response.meta.get("list_type", ""))
        item_loader.add_css("video_des", des)
        item_loader.add_value("video_name", v_name)
        item_loader.add_value("spell_name", v_name)
        item_loader.add_css("video_addr", ".episodeIntro-area a::text")
        item_loader.add_css("video_type", ".episodeIntro-type a::text")
        item_loader.add_css("video_time", ".episodeIntro-lang[itemprop='datePublished'] a::text")
        item_loader.add_value("video_actor", response.meta.get("video_actor", ""))
        item_loader.add_value("video_origin", "爱奇艺")
        item_loader.add_css("video_director", ".episodeIntro-director a::text")
        item_loader.add_css("video_language", ".episodeIntro-lang[itemprop='inLanguage'] a::text")
        item_loader.add_value("front_image_url", response.meta.get("front_image_url", ""))

        video_item = item_loader.load_item()
        reason = None
        if len(play_urls) == 0:
            reason = "没有播放路径"
        elif len(video_item.values()) < 6:
            reason = "抓取的字段不足"
        elif not video_item.get("v_id"):
            reason = "抓取id失败"
        else:
            yield video_item
        if reason:
            error_video(response.meta.get("list_type", ""),
                        response.url,
                        reason,
                        v_name)
        print("爬取完毕: %s" % v_name)
        SpiderStateMiddleware.crawled += 1

    def parse_detail_2(self, response):
        # 对电影细节的解析
        v_name = response.meta.get("video_name", "")
        print("正在爬取: %s" % v_name)
        v_id = response.text[response.text.find("param['tvid']") + 17: response.text.find("param['vid']") - 8]
        vid = response.text[response.text.find("param['vid']") + 16: response.text.find("param['albumId']") - 8]
        item_loader = VideoItemLoader(item=VideospiderItem(), response=response)

        item_loader.add_value("v_id", v_id)
        item_loader.add_value("play_url", response.url)
        item_loader.add_value("list_type", response.meta.get("list_type", ""))
        item_loader.add_css("video_des", ".partDes::attr(data-description)")
        item_loader.add_value("video_name", v_name)
        item_loader.add_value("spell_name", v_name)
        item_loader.add_css("video_addr", ".vInfoSide_rSpan a[rseat='707181_region']::text")
        item_loader.add_css("video_type", ".vInfoSide_rSpan a[rseat='707181_genres']::text")
        item_loader.add_value("video_actor", response.meta.get("video_actor", ""))
        item_loader.add_value("video_origin", "爱奇艺")
        item_loader.add_css("video_director", ".vInfoSide_rName a::text")
        item_loader.add_css("video_language", ".vInfoSide_rSpan a[rseat='707181_region']::text")
        item_loader.add_value("front_image_url", response.meta.get("front_image_url", ""))

        video_item = item_loader.load_item()
        reason = None
        if not video_item.get("v_id"):
            reason = "抓取id失败"
        else:
            yield video_item
        if reason:
            error_video(response.meta.get("list_type", ""),
                        response.url,
                        reason,
                        v_name)
        print("爬取完毕: %s" % v_name)
        SpiderStateMiddleware.crawled += 1

    def parse_detail_3(self, response):
        # 对综艺细节的解析
        v_name = response.meta.get("video_name", "")
        print("正在爬取: %s" % v_name)
        play_urls = {}
        album_items = response.css("#albumpic-showall-wrap li")
        for album_item in album_items:
            url = album_item.css(".site-piclist_pic > a::attr(href)").extract_first("")
            num = album_item.css(".mod-listTitle_right::text").extract_first("")
            play_urls[episode_format(num)] = url

        tab_pills = response.css("#album_pic_paging[style='']")
        if len(tab_pills) > 0:
            # 处理分页问题
            for i, k in get_last_variety(response.url, os.path.abspath("tools/chromedriver")):
                play_urls[episode_format(i)] = k
        item_loader = VideoItemLoader(item=VideospiderItem(), response=response)

        item_loader.add_css("v_id", "*::attr(data-score-tvid)")
        item_loader.add_value("play_url", play_urls)
        item_loader.add_value("list_type", response.meta.get("list_type", ""))
        item_loader.add_css("video_des", ".episodeIntro-brief[data-moreorless='lessinfo'] .briefIntroTxt::text")
        item_loader.add_value("video_name", v_name)
        item_loader.add_value("spell_name", v_name)
        item_loader.add_css("video_addr", ".episodeIntro-area a::text")
        item_loader.add_css("video_type", ".episodeIntro-type a::text")
        item_loader.add_value("video_origin", "爱奇艺")
        item_loader.add_css("video_time", ".episodeIntro-lang[itemprop='datePublished'] a::text")
        item_loader.add_css("video_language", ".episodeIntro-lang[itemprop='inLanguage'] a::text")
        item_loader.add_value("front_image_url", response.meta.get("front_image_url", ""))

        video_item = item_loader.load_item()
        reason = None
        if len(play_urls) == 0:
            reason = "没有播放路径"
        elif len(video_item.values()) < 6:
            reason = "抓取的字段不足"
        elif not video_item.get("v_id"):
            reason = "抓取id失败"
        else:
            yield video_item
        if reason:
            error_video(response.meta.get("list_type", ""),
                        response.url,
                        reason,
                        v_name)
        print("爬取完毕: %s" % v_name)
        SpiderStateMiddleware.crawled += 1

    def parse_detail_4(self, response):
        # 对动漫细节的解析
        v_name = response.meta.get("video_name", "")
        print("正在爬取: %s" % response.meta.get("video_name", ""))
        item_loader = VideoItemLoader(item=VideospiderItem(), response=response)
        v_id = response.css("*::attr(data-score-tvid)").extract_first("")
        if not v_id:
            v_id = response.text[response.text.find("param['tvid']") + 17: response.text.find("param['vid']") - 8]

        if not is_player(response.url):
            play_urls = self.parse_episode(v_id)

            item_loader.add_css("video_des",
                                "*[data-moreorless='lessinfo'][itemprop='description'] span:not(.c-999)::text")
            item_loader.add_css("video_addr", "*[itemprop='contentLocation'] a::text")
            item_loader.add_css("video_type", "*[itemprop='genre'] a::text")
            item_loader.add_css("video_time", ".main_title span::text")
            item_loader.add_css("video_language", "*[itemprop='inLanguage'] a::text")
        else:
            play_urls = response.url
            item_loader.add_css("video_des", "#datainfo-tag-desc::text")
            item_loader.add_css("video_addr", "#thirdPartyTagList::text")
            item_loader.add_css("video_language", "#thirdPartyTagList::text")

        item_loader.add_value("v_id", v_id)
        item_loader.add_value("play_url", play_urls)
        item_loader.add_value("list_type", response.meta.get("list_type", ""))
        item_loader.add_value("video_name", v_name)
        item_loader.add_value("spell_name", v_name)
        item_loader.add_value("video_origin", "爱奇艺")
        item_loader.add_value("front_image_url", response.meta.get("front_image_url", ""))

        video_item = item_loader.load_item()
        reason = None
        if len(play_urls) == 0:
            reason = "没有播放路径"
        elif len(video_item.values()) < 6:
            reason = "抓取的字段不足"
        elif not video_item.get("v_id"):
            reason = "抓取id失败"
        else:
            yield video_item
        if reason:
            error_video(response.meta.get("list_type", ""),
                        response.url,
                        reason,
                        v_name)
        print("爬取完毕: %s" % v_name)
        SpiderStateMiddleware.crawled += 1

    def parse_episode(self, v_id):
        play_urls = {}
        reload = 1
        details_url = "http://cache.video.iqiyi.com/jp/avlist/%s/%s/?albumId=%s"
        while True:
            res = request_url(details_url % (v_id, reload, v_id), proxies=False)
            text = res.text[res.text.find("=") + 1: -1]
            all_json = json.loads(text)
            vlst = all_json["data"]["vlist"]
            if len(vlst) == 0:
                break
            for lst in vlst:
                ep = lst["pd"]
                url = lst["vurl"]
                if "预" not in lst["pds"]:
                    play_urls[ep] = url

            reload += 1

        return play_urls


#!/usr/bin/env python
# coding: utf-8

# In[1]:

from Project02 import *

# In[7]:

import unittest

# In[42]:

def userstory23(ppl_df):
    for idx in range(len(ppl_df)):
        current_name = ppl_df.Name[idx]
        current_bday = ppl_df.Birthday[idx]

        check = ppl_df.drop([idx])
        for name in check.Name:
            if current_name == name:
                i = check[check.Name == current_name].index.values[0]
                if current_bday == check.Birthday[i]:
                    unique = "false"
                    break
                else:
                    unique = "true"
            else:
                unique = "true"

    if unique == "true":
        return "unique"
    else:
        return "not unique"

# In[44]:

class userstory23_test(unittest.TestCase):
    def test1(self):
        df = individuals[0:3]
        df = df.append(individuals.iloc[0]).reset_index(drop = True)
        res = "not unique"
        self.assertEqual(userstory23(df), res)

    def test2(self):
        df = individuals[0:5]
        res = "unique"
        self.assertEqual(userstory23(df), res)

    def test3(self):
        df = individuals[0:7]
        df = df.append(individuals.iloc[3]).reset_index(drop = True)
        res = "not unique"
        self.assertEqual(userstory23(df), res)

    def test4(self):
        df = individuals[0:9]
        df = df.append(individuals.iloc[11]).reset_index(drop = True)
        res = "unique"
        self.assertEqual(userstory23(df), res)

    def test5(self):
        df = individuals[0:11]
        df = df.append(individuals.iloc[4]).reset_index(drop = True)
        res = "not unique"
        self.assertEqual(userstory23(df), res)

unittest.main(argv=['first-arg-is-ignored'], exit=False)

# In[ ]:


def main():
    with open("rosalind_rstr.txt", "r") as f:
        N, x = f.readline().strip('\n').split()
        s = f.readline().strip('\n')
    N = int(N)
    x = float(x)
    at = s.count('A') + s.count('T')
    gc = s.count('G') + s.count('C')
    prob = pow(x/2, gc) * pow((1-x)/2, at)

    with open("out.txt", "w") as o:
        print(f"{(1 - pow(1-prob, N)):.3f}", file=o)

if __name__ == "__main__":
    main()


def lis(array):
	n = len(array)
	l = []
	count = []
	ar = []
	ctr=0
	for i in range(n):
		ar.append(array[i])
		ctr+=1
		for k in range(i,n):
			for j in range(k,n):
				if array[k]>ar[ctr-1]:
					ar.append(array[k])
					ctr+=1
			count.append(ctr)
			print(ar)
			print(ctr)
		ctr=0
		ar=[]
	# print(count)
	# print(max(count))

a = [1,5,3,6,2,7,8,4,9]
lis(a)


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

import pandas as pd
import phpserialize
import requests
from lxml import html

class WastonSpider:
    scroll_id_start = 15484 + 1
    product_id = 7417 + 1
    scroll_workbook = pd.ExcelWriter("scrall_img_1.xls")
    product_workbook = pd.ExcelWriter("product_info_1.xls")

    def __init__(self,
                 excel_path,
                 sheetname,
                 cate_id,
                 merchant_id,
                 labels,
                 is_local=1,
                 is_sks_delivery=1):
        self.excel_path = excel_path
        self.sheetname = sheetname
        self.user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:51.0) \
Gecko/20100101 Firefox/51.0"

        self.host = "www.watsons.com.cn"
        self.accept_language = 'en-US,en;q=0.5'
        self.base_img_name = "watsons_img"
        self.cate_id = cate_id
        self.merchant_id = merchant_id
        self.labels = labels
        self.is_local = is_local
        self.is_sks_delivery = is_sks_delivery

    def get_page_tree(self, page_url, cycle_time=0):
        headers = {
            "Accept-Language": self.accept_language,
            "Host": self.host,
            "User-Agent": self.user_agent
        }
        try:
            response = requests.get(page_url, headers=headers)
            tree = html.fromstring(response.content)
            return tree, response
        except:
            if cycle_time <= 10:
                cycle_time += 1
                print("重新第{}次采集产品主页".format(cycle_time))
                self.get_page_tree(cycle_time=cycle_time)

    def get_price(self, page_tree):
        # 获取价格
        price = page_tree.xpath('//p[@class="skuPrice"]/i/text()')[0][5:]
        return price

    def get_content_params(self, page_tree):
        # 获取产品参数
        product_params_key = page_tree.xpath(
            '//div[@class="skuSelect clearFix"]/p/b/text()')
        product_params_value = page_tree.xpath(
            '//div[@class="skuSelect clearFix"]/p/em/text()')
        product_content_params = dict(
            zip(product_params_key, product_params_value))
        if product_content_params.get('商品编码'):
            product_content_params.pop('商品编码')
        # 获取产品成分
        product_ingredient_key = page_tree.xpath(
            '//div[@class="section skuInfo"]/p/b/text()')
        product_ingredient_value = [
            each
            for each in page_tree.xpath(
                '//div[@class="section skuInfo"]/p/text()') if "\n" not in each
        ]
        product_ingredient = dict(
            zip(product_ingredient_key, product_ingredient_value))

        # 获取产品详情
        content_info_value = page_tree.xpath(
            '//div[@class="section skuInfo skuInfoHeight"]/p/text()')
        if ":" in content_info_value:
            content_info_value.remove(':')
        content_info_key = page_tree.xpath(
            '//div[@class="section skuInfo skuInfoHeight"]/p/b/text()')
        content_info_value = [
            each for each in content_info_value if "\n" not in each
        ]
        content_info = dict(zip(content_info_key, content_info_value))
        if content_info.get('会员尊享'):
            content_info.pop('会员尊享')

        product_content_params.update(product_ingredient)
        product_content_params.update(content_info)

        content_params = phpserialize.dumps(product_content_params).decode(
            'utf-8')
        return content_params

    def get_scroll_img_url_list(self, page_tree):
        # 获取滚动图链接
        super_img_list = page_tree.xpath(
            '//div[@class="slide"]/img[@class="lazy"]/@supersrc')
        hero_img_list = page_tree.xpath(
            '//div[@class="slide"]/img[@class="lazy"]/@herosrc')
        return super_img_list, hero_img_list

    def get_content_img_url_list(self, page_tree):
        # 获取详情图片
        src_list = page_tree.xpath(
            '//div[@class="section skuInfo revBox"]/p/img/@src')
        data_src = page_tree.xpath(
            '//div[@class="section skuInfo revBox"]/img/@data-src')
        # 两个列表合并，＋，append， extend等
        content_img_url_list = src_list + data_src
        return content_img_url_list

    def get_content_html(self, response):
        html = response.text

        reg = '(<div class="section skuInfo revBox".*?
)[\n\t\r\s]+?<div class="section proInfo'
        items = re.findall(reg, html, re.S)[0]
        items = items.replace(
            'section skuInfo revBox" style="max-height:12000px',
            'product-detail-img')
        if "border='0' alt='' title='' width='100%'" in items:
            items = items.replace("border='0' alt='' title='' width='100%'",
                                  '')
        items = items.replace(
            '

\n

',
            '')
        items = re.subn('src="http:.*class="lazy"', '', items)[0]
        items = re.subn('[\n\r\s\t]*', '', items)[0]
        items = items.replace("data-src", "src")
        items = re.subn('\r\n\s+\t', '', items)[0]
        items = re.subn('\s+\t\s+', '', items)[0]
        items = re.subn(
            '//img.watsons.com.cn/infinityimages/sys_master/images/\d+',
            'uploads/content', items)[0]
        return items

    def get_img(self, img_url, cycle_time=0):
        headers = {
            "Accept-Language": self.accept_language,
            "Host": "img.watsons.com.cn",
            "User-Agent": self.user_agent
        }
        try:
            response = requests.get(img_url, headers=headers)
            return response
        except:
            if cycle_time <= 10:
                cycle_time += 1
                print("重新第{}次下载图片".format(cycle_time))
                self.get_img(img_url, cycle_time=cycle_time)

    def mkdir(self, folder_path):
        if not os.path.exists(folder_path):
            os.makedirs(folder_path)

    def download_scroll_img(self, page_tree):
        base_scroll_img_name = os.path.join(self.base_img_name, "scroll")
        self.mkdir(base_scroll_img_name)
        img_ids = []
        scroll_data = []
        super_img_list = self.get_scroll_img_url_list(page_tree)[0]
        hero_img_list = self.get_scroll_img_url_list(page_tree)[1]

        for scroll_url in super_img_list:
            response = self.get_img("http:" + scroll_url)
            if response.status_code != 200:
                scroll_url = hero_img_list[super_img_list.index(scroll_url)]
                response = self.get_img("http:" + scroll_url)
                if response.status_code != 200:
                    continue
            scroll_name = scroll_url.split('/')[-1]
            scroll_img_path = os.path.join(base_scroll_img_name, scroll_name)

            open(scroll_img_path, "wb").write(response.content)

            # 生成滚动图数据
            scroll_data.append({
                "id": self.scroll_id_start,
                "status": 1,
                "type": scroll_name.split('.')[-1],
                "url": "uploads/img/" + scroll_name
            })
            # 生成滚动图对应id
            img_ids.append(str(self.scroll_id_start))
            self.scroll_id_start += 1
            print("成功下载滚动图{}".format(scroll_url))
        return ",".join(img_ids), scroll_data

    def download_content_img(self, page_tree):
        base_content_img_name = os.path.join(self.base_img_name, "desc")
        self.mkdir(base_content_img_name)
        for content_url in self.get_content_img_url_list(page_tree):
            content_name = content_url.split('/')[-1]
            content_img_path = os.path.join(base_content_img_name,
                                            content_name)
            content_url = "http:" + content_url
            response = self.get_img(content_url)
            open(content_img_path, "wb").write(response.content)
            print("成功下载详情图{}".format(content_url))

    def download_cover_img(self, page_tree):
        base_cover_img_name = os.path.join(self.base_img_name, "cover")
        self.mkdir(base_cover_img_name)
        for url in self.get_scroll_img_url_list(page_tree)[1]:
            cover_url = "http:" + url
            response = self.get_img(cover_url)
            if response.status_code != 200:
                continue
            else:
                cover_name = cover_url.split('/')[-1]
                cover_img_path = os.path.join(base_cover_img_name, cover_name)
                open(cover_img_path, "wb").write(response.content)
                img_cover = "uploads/goods/{}".format(cover_name)
                return img_cover

    def run(self):
        excel_data = pd.read_excel(self.excel_path, sheetname=self.sheetname)
        excel_dict = dict(
            zip(excel_data['url'].tolist(), excel_data['title'].tolist()))

        scroll_startrow = 0
        num = 0
        for url, title in excel_dict.items():
            print(url, title)
            page_tree, response = self.get_page_tree(url)
            try:
                price = self.get_price(page_tree)
            except IndexError as e:
                print(e)
                print("该产品已不存在")
                continue
            content_params = self.get_content_params(page_tree)
            content = self.get_content_html(response)
            self.download_content_img(page_tree)
            img_cover = self.download_cover_img(page_tree)
            img_ids, scroll_data = self.download_scroll_img(page_tree)

            if scroll_startrow:
                header = False
            else:
                header = True

            # 存储scroll信息
            df_scroll = pd.DataFrame(
                scroll_data, columns=['id', 'status', 'type', 'url'])

            df_scroll.to_excel(
                self.scroll_workbook,
                sheet_name='scroll_img',
                header=header,
                startrow=scroll_startrow,
                index=False)

            # 存储product_info
            product_data = [{
                "id": self.product_id,
                "status": 1,
                "sort": 0,
                "is_hot": 1,
                "is_rec": 1,
                "credit": 0,
                "cate_id": self.cate_id,
                "merchant_id": self.merchant_id,
                "title": title,
                "subtitle": title,
                "img_cover": img_cover,
                "price": price,
                "market_price": price,
                "shipping_price": 15,
                "img_ids": img_ids,
                "content": content,
                "is_on_sale": 1,
                "is_local": self.is_local,
                "is_sks_delivery": self.is_sks_delivery,
                "stock": 100,
                "labels": self.labels,
                "content_params": content_params,
            }]
            df_product = pd.DataFrame(
                product_data,
                columns=[
                    "id", "status", "is_hot", "is_rec", "credit", "cate_id",
                    "merchant_id", "title", "subtitle", "img_cover", "price",
                    "market_price", "shipping_price", "img_ids", "content",
                    "is_on_sale", "is_local", "is_sks_delivery", "stock",
                    "labels", "content_params"
                ])
            df_product.to_excel(
                self.product_workbook,
                sheet_name="product_info",
                header=header,
                index=False,
                startrow=num)

            print(num, scroll_startrow)
            if num:
                scroll_startrow += len(scroll_data)
                num += 1
            else:
                scroll_startrow = len(scroll_data) + 1
                num += 2
            self.product_id += 1

            self.product_workbook.save()
            self.scroll_workbook.save()

spider = WastonSpider(
    './watsons_cn_all_url.xls',
    0,
    3139,
    44,
    "certified",
    is_local=0,
    is_sks_delivery=0)
spider.run()


from pyomo.environ import Block, Expression, units as pyunits
from watertap3.utils import financials
from watertap3.wt_units.wt_unit import WT3UnitProcess

## REFERENCE: ADD REFERENCE HERE

module_name = 'irwin_brine_management'
basis_year = 2019
tpec_or_tic = 'TPEC'

class UnitProcess(WT3UnitProcess):

    def fixed_cap(self):
        time = self.flowsheet().config.time.first()
        self.flow_in = pyunits.convert(self.flow_vol_in[time], to_units=pyunits.m ** 3 / pyunits.hr)
        self.conc_mass_tot = 0
        for constituent in self.config.property_package.component_list:
            self.conc_mass_tot += self.conc_mass_in[time, constituent]
        self.density = 0.6312 * self.conc_mass_tot + 997.86  # kg/m3 # assumption from Tim's reference (ask Ariel for Excel if needed)
        self.total_mass = self.density * self.flow_in  # kg/hr to tons for Mike's Excel needs
        self.chem_dict = {}
        self.capacity_basis = 9463.5
        self.base_fixed_cap_cost = 31
        self.cap_scaling_exp = 0.873
        irwin_bm_cap = self.base_fixed_cap_cost * (self.total_mass / self.capacity_basis) ** self.cap_scaling_exp
        return irwin_bm_cap  # M$

    def elect(self):
        self.lift_height = 100 * pyunits.ft
        self.pump_eff = 0.9 * pyunits.dimensionless
        self.motor_eff = 0.9 * pyunits.dimensionless
        flow_in_gpm = pyunits.convert(self.flow_in, to_units=pyunits.gallons / pyunits.minute)
        electricity = (0.746 * flow_in_gpm * self.lift_height / (3960 * self.pump_eff * self.motor_eff)) / self.flow_in  # kWh/m3
        return electricity

    def get_costing(self, unit_params=None, year=None):
        '''
        Initialize the unit in WaterTAP3.
        '''
        financials.create_costing_block(self, basis_year, tpec_or_tic)
        self.costing.fixed_cap_inv_unadjusted = Expression(expr=self.fixed_cap(),
                                                           doc='Unadjusted fixed capital investment')  # $M
        self.electricity = Expression(expr=self.elect(),
                                      doc='Electricity intensity [kwh/m3]')  # kwh/m3
        financials.get_complete_costing(self.costing)