import collections
import math
import fractions
import ast
import random
import re
from functools import reduce
from itertools import count
from difflib import SequenceMatcher

letters = "abcdefghijklmnopqrstuvwxyz"
numbers = "0123456789"

def apply_safe(function, *args):
    try:
        return function(*args)
    except:
        return args[0]

def ast_int_eval(number):
    a = str(number)
    try:
        a = ast.literal_eval(a)

        # prevents conversion to a tuple, dict, set, and so on
        if type(a) is not list and type(a) is not str and type(a) is not int and type(a) is not float:
            a = str(number)
    except:
        a = int(a)

    return a

def is_digit_value(value):
    value = str(value)
    try:
        int(value)
        return 1
    except:
        return 0

def flatten(x):
    if type(x) is list:
        return [a for i in x for a in flatten(i)]
    return [x]

def deep_flatten(S):
    if type(S) is not list or not S:
        return S

    buf = []
    for i in S:
        if type(i) is list:
            buf += deep_flatten(i)
        else:
            buf.append(i)

    return buf

def is_alpha_value(value):
    value = str(value)

    if len(value) == 0:
        return 0

    try:
        for X in value:
            if not X.lower() in letters:
                return 0
        return 1
    except:
        return 0

def convert_to_base(n, base):
    """
    convert positive decimal integer n to equivalent in another base(1-255)
    """

    digits = "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039" \
             "\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004A" \
             "\u004B\u004C\u004D\u004E\u004F\u0050\u0051\u0052\u0053\u0054" \
             "\u0055\u0056\u0057\u0058\u0059\u005A\u0061\u0062\u0063\u0064" \
             "\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C\u006D\u006E" \
             "\u006F\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078" \
             "\u0079\u007A\u01DD\u0292\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6" \
             "\u03B7\u03B8\u0432\u0438\u043C\u043D\u0442\u000A\u0393\u0394" \
             "\u0398\u03B9\u03A3\u03A9\u2260\u220A\u220D\u221E\u2081\u2082" \
             "\u2083\u2084\u2085\u2086\u0020\u0021\u0022\u0023\u0024\u0025" \
             "\u0026\u0027\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F" \
             "\u003A\u003B\u003C\u003D\u003E\u003F\u0040\u005B\u005C\u005D" \
             "\u005E\u005F\u0060\u007B\u007C\u007D\u007E\u01B5\u20AC\u039B" \
             "\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039" \
             "\u0152\u0106\u017D\u01B6\u0100\u2018\u2019\u201C\u201D\u2013" \
             "\u2014\u02DC\u2122\u0161\u203A\u0153\u0107\u017E\u0178\u0101" \
             "\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA" \
             "\u00AB\u00AC\u03BB\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4" \
             "\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE" \
             "\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8" \
             "\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2" \
             "\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC" \
             "\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6" \
             "\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0" \
             "\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA" \
             "\u00FB\u00FC\u00FD\u00FE\u00FF"

    n = int(n)
    base = int(base)

    if n == 0:
        return "0"

    if n < 0 or base < 2 or base > 255:
        if n > 0 and base == 1:
            return "0" * n
        return str(n)

    s = ""
    while True:
        r = n % base
        s = digits[int(r)] + s
        n = n // base
        if n == 0:
            break

    try:
        while s[0] == "0":
            s = s[1:]
    except:
        pass

    return s

def convert_to_base_arbitrary(n, base):
    n = int(n)
    base = int(base)

    if n == 0:
        return [0]

    if n > 0 and base == 1:
        return [0] * n

    s = []
    if base > 0:
        while 1:
            r = n % base
            s = [int(r)] + s
            n = n // base
            if n == 0:
                break
    else:
        while True:
            n, remainder = divmod(n, base)

            if remainder < 0:
                n, remainder = n + 1, remainder - base

            s = [remainder] + s
            if n == 0:
                break

    try:
        while s[0] == "0":
            s = s[1:]
    except:
        pass

    return s

def convert_from_base(n, base):
    digits = "\u0030\u0031\u0032\u0033\u0034\u0035\u0036\u0037\u0038\u0039" \
             "\u0041\u0042\u0043\u0044\u0045\u0046\u0047\u0048\u0049\u004A" \
             "\u004B\u004C\u004D\u004E\u004F\u0050\u0051\u0052\u0053\u0054" \
             "\u0055\u0056\u0057\u0058\u0059\u005A\u0061\u0062\u0063\u0064" \
             "\u0065\u0066\u0067\u0068\u0069\u006A\u006B\u006C\u006D\u006E" \
             "\u006F\u0070\u0071\u0072\u0073\u0074\u0075\u0076\u0077\u0078" \
             "\u0079\u007A\u01DD\u0292\u03B1\u03B2\u03B3\u03B4\u03B5\u03B6" \
             "\u03B7\u03B8\u0432\u0438\u043C\u043D\u0442\u000A\u0393\u0394" \
             "\u0398\u03B9\u03A3\u03A9\u2260\u220A\u220D\u221E\u2081\u2082" \
             "\u2083\u2084\u2085\u2086\u0020\u0021\u0022\u0023\u0024\u0025" \
             "\u0026\u0027\u0028\u0029\u002A\u002B\u002C\u002D\u002E\u002F" \
             "\u003A\u003B\u003C\u003D\u003E\u003F\u0040\u005B\u005C\u005D" \
             "\u005E\u005F\u0060\u007B\u007C\u007D\u007E\u01B5\u20AC\u039B" \
             "\u201A\u0192\u201E\u2026\u2020\u2021\u02C6\u2030\u0160\u2039" \
             "\u0152\u0106\u017D\u01B6\u0100\u2018\u2019\u201C\u201D\u2013" \
             "\u2014\u02DC\u2122\u0161\u203A\u0153\u0107\u017E\u0178\u0101" \
             "\u00A1\u00A2\u00A3\u00A4\u00A5\u00A6\u00A7\u00A8\u00A9\u00AA" \
             "\u00AB\u00AC\u03BB\u00AE\u00AF\u00B0\u00B1\u00B2\u00B3\u00B4" \
             "\u00B5\u00B6\u00B7\u00B8\u00B9\u00BA\u00BB\u00BC\u00BD\u00BE" \
             "\u00BF\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C6\u00C7\u00C8" \
             "\u00C9\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D0\u00D1\u00D2" \
             "\u00D3\u00D4\u00D5\u00D6\u00D7\u00D8\u00D9\u00DA\u00DB\u00DC" \
             "\u00DD\u00DE\u00DF\u00E0\u00E1\u00E2\u00E3\u00E4\u00E5\u00E6" \
             "\u00E7\u00E8\u00E9\u00EA\u00EB\u00EC\u00ED\u00EE\u00EF\u00F0" \
             "\u00F1\u00F2\u00F3\u00F4\u00F5\u00F6\u00F7\u00F8\u00F9\u00FA" \
             "\u00FB\u00FC\u00FD\u00FE\u00FF"

    n = str(n)[::-1]
    base = int(base)
    r = 0
    range_v = 0

    for Q in n:
        r += digits.index(Q) * base ** range_v
        range_v += 1

    return r

def convert_from_base_arbitrary(n, base):
    if type(n) is not list:
        n = str(n)

    base = int(base)

    n = n[::-1]
    r = 0
    range_v = 0

    for Q in n:
        r += int(Q) * base ** range_v
        range_v += 1

    return r

def is_prime(n):
    try:
        n = int(n)
    except:
        return 0

    if n == 2 or n == 3:
        return 1
    if n < 2 or n % 2 == 0 or n % 3 == 0:
        return 0
    if n < 9:
        return 1
    r = int(n ** 0.5)
    f = 5
    while f <= r:
        if n % f == 0:
            return 0
        if n % (f + 2) == 0:
            return 0
        f += 6

    return 1

def first_n_primes(n):
    prime_list = []
    primes = prime_sieve()
    for x in range(n):
        prime_list.append(next(primes))
    return prime_list

def primes_upto_n(n):
    prime_list = []
    primes = prime_sieve()
    while True:
        current_prime = next(primes)
        if current_prime > n:
            break
        prime_list.append(current_prime)
    return prime_list

def prime_sieve():
    yield 2
    yield 3
    yield 5
    yield 7
    sieve = {}
    ps = prime_sieve()
    p = next(ps) and next(ps)
    q = p * p
    for c in count(9, 2):
        if c in sieve:
            s = sieve.pop(c)
        elif c < q:
            yield c
            continue
        else:
            s = count(q + 2 * p, 2 * p)
            p = next(ps)
            q = p * p
        for m in s:
            if m not in sieve:
                break
        sieve[m] = s

def combinations(n, r):
    n = int(n)
    r = int(r)
    if r > n:
        return 0
    return int(
        math.factorial(n) // (math.factorial(r) * math.factorial(n - r))
    )

def permutations(n, r):
    n = int(n)
    r = int(r)
    if r > n:
        return 0
    return int(math.factorial(n) // math.factorial(n - r))

def prime_factorization(n):
    n = int(n)

    if n < 2:
        return []
    if n == 2:
        return [2]

    list_of_factors = []
    for Q in range(2, n + 1):
        if is_prime(Q):
            if n % Q == 0:
                list_of_factors.append(Q)
    return list_of_factors

def get_letter(n):
    n = int(n)
    if n in range(1, 27):
        return chr(64 + n)

def prime_factorization_duplicates(n):
    n = int(n)

    if n < 2:
        return []
    if n == 2:
        return [2]
    list_of_factors = []

    for Q in range(2, n + 1):
        if is_prime(Q):
            while n % Q == 0:
                list_of_factors.append(Q)
                n = int(int(n) // int(Q))
        if n == 1:
            break

    return list_of_factors

def prime_factorization_powers(n):
    n = int(n)

    if n < 2:
        return []
    if n == 2:
        return [1]

    list_of_factors = []

    for Q in range(2, n + 1):
        if is_prime(Q):
            value = 0
            while n % Q == 0:
                value += 1
                n = int(int(n) // int(Q))
            list_of_factors.append(value)

    try:
        while list_of_factors[len(list_of_factors) - 1] == 0:
            list_of_factors.pop()
    except:
        pass

    return list_of_factors

def get_nth_prime(n):
    n = int(n) + 1
    current_prime = 2
    while n > 0:
        if is_prime(current_prime):
            n -= 1
            if n > 0:
                current_prime += 1
        else:
            current_prime += 1
    return current_prime

def get_index_of_prime(n):
    from itertools import takewhile
    n = int(n)
    g = prime_sieve()
    return len(list(takewhile(lambda x: x <= n, g)))-1

def get_all_substrings(input_string):
    if type(input_string) is not list:
        input_string = str(input_string)

    length = len(input_string)
    return [
        input_string[i:j+1] for i in range(length) for j in range(i, length)
    ]

def floatify(string):
    # force an exception if not a number
    ast_int_eval(string)

    a = str(string).lower()
    is_neg = False

    # handling scientific notation
    if "e" in a:
        if "e-" in a:
            dec = re.search("e-([0-9]+)", a)
            a = ("%." + dec.group(1) + "f") % float(a)
        else:
            a = "%f" % float(a)

    if a[0] == "-":
        is_neg = True
        a = a[1:]

    if not str(a).__contains__("."):
        a += "."
    while a[0] == "0":
        a = a[1:]
    while a[-1] == "0":
        a = a[0:-1]
    if a[0] == ".":
        a = "0" + a
    if a[-1] == ".":
        a += "0"

    if is_neg:
        a = "-" + a

    return a

def trim_float(string):
    if str(string)[-2:] == ".0":
        return int(string)
    return floatify(string)

def is_float_value(string):
    number_of_dots = str(string).count(".")
    string = str(string).replace(".", "")

    if is_digit_value(string) and number_of_dots < 2:
        return 1
    return 0

def euler_totient(n):
    n = int(n)
    if is_prime(n):
        return n - 1

    amount = 0

    for k in range(1, n + 1):
        if fractions.gcd(n, k) == 1:
            amount += 1

    return amount

def chunk_divide(seq, num):
    num = ast_int_eval(num)
    avg = len(seq) / float(num)
    is_list = type(seq) is list

    if not is_list:
        seq = str(seq)

    if avg < 1:
        return seq
    # special case: there are more parts than possible groupings
    elif avg < 2:
        seq = [[x] for x in seq]

        idx = 0
        while len(seq) > num:
            seq.insert(idx, seq.pop(idx) + seq.pop(idx))
            idx = idx + 1 if idx < len(seq) - 1 else 0

        if not is_list:
            seq = [''.join(x) for x in seq]
        return seq
    else:
        seq = seq[::-1]
        out = []
        last = 0.0

        while last < len(seq):
            out.append(seq[int(last):int(last + avg)][::-1])
            last += avg

        return out[::-1]

def minimum_edit_distance(s1, s2):
    s1 = str(s1)
    s2 = str(s2)
    if len(s1) > len(s2):
        s1, s2 = s2, s1

    distances = range(len(s1) + 1)
    for index2, char2 in enumerate(s2):
        newDistances = [index2 + 1]
        for index1, char1 in enumerate(s1):
            if char1 == char2:
                newDistances.append(distances[index1])
            else:
                newDistances.append(1 + min((distances[index1],
                                             distances[index1 + 1],
                                             newDistances[-1])))
        distances = newDistances
    return distances[-1]

def infinite_replace(object1, object2, object3):

    if type(object1) is list:
        object1 = [str(x) for x in object1]
    elif type(object1) is not list:
        object1 = str(object1)

    if type(object2) is list:
        object2 = [str(x) for x in object2]
    elif type(object2) is not list:
        object2 = str(object2)

    if type(object3) is list:
        object3 = [str(x) for x in object3]
    elif type(object3) is not list:
        object3 = str(object3)

    type1 = type(object1)
    type2 = type(object2)
    type3 = type(object3)

    # [String String String]
    if type1 is str and type2 is str and type3 is str:
        while object1.replace(object2, object3) != object1:
            object1 = object1.replace(object2, object3)
        return object1

    # [String List String]
    elif type1 is str and type2 is list and type3 is str:
        previous_object = ""
        while previous_object != object1:
            previous_object = object1
            for element in object2:
                while object1.replace(element, object3) != object1:
                    object1 = object1.replace(element, object3)
        return object1

    # [String List List]
    elif type1 is str and type2 is list and type3 is list:
        previous_object = ""
        while previous_object != object1:
            previous_object = object1
            for index in range(0, len(object2)):
                while object1.replace(object2[index], object3[index])\
                        != object1:
                    object1 = object1.replace(object2[index], object3[index])
        return object1

    # [List String String]
    elif type1 is list and type2 is str and type3 is str:
        result_list = []
        for sub_element in object1:
            while sub_element.replace(object2, object3) != sub_element:
                sub_element = sub_element.replace(object2, object3)

            result_list.append(sub_element)
        return result_list

    # [List List String]
    elif type1 is list and type2 is list and type3 is str:
        result_list = []
        for sub_element in object1:
            previous_object = ""
            while previous_object != sub_element:
                previous_object = sub_element
                for sub_start in object2:
                    while sub_element.replace(sub_start, object3)\
                            != sub_element:
                        sub_element = sub_element.replace(sub_start, object3)
            result_list.append(sub_element)
        return result_list

    # [List List List]
    elif type1 is list and type2 is list and type3 is list:
        result_list = []
        for sub_element in object1:
            previous_object = ""
            while previous_object != sub_element:
                previous_object = sub_element
                for sub_index in range(0, len(object2)):
                    while sub_element.replace(
                            object2[sub_index], object3[sub_index])\
                            != sub_element:
                        sub_element = sub_element.replace(
                            object2[sub_index], object3[sub_index])
            result_list.append(sub_element)
        return result_list

    raise Exception

def single_replace(object1, object2, object3):

    if type(object1) is list:
        object1 = [str(x) for x in object1]
    else:
        object1 = str(object1)

    if type(object2) is list:
        object2 = [str(x) for x in object2]
    else:
        object2 = str(object2)

    if type(object3) is list:
        object3 = [str(x) for x in object3]
    else:
        object3 = str(object3)

    type1 = type(object1)
    type2 = type(object2)
    type3 = type(object3)

    # [String String String]
    if type1 is str and type2 is str and type3 is str:
        object1 = object1.replace(object2, object3)
        return object1

    # [String List String]
    elif type1 is str and type2 is list and type3 is str:
        for element in object2:
            object1 = object1.replace(str(element), object3)
        return object1

    # [String List List]
    elif type1 is str and type2 is list and type3 is list:
        for index in range(0, len(object2)):
            object1 = object1.replace(str(object2[index]), str(object3[index]))
        return object1

    # [List String String]
    elif type1 is list and type2 is str and type3 is str:
        result_list = []
        for sub_element in object1:
            sub_element = sub_element.replace(object2, object3)
            result_list.append(sub_element)
        return result_list

    # [List List String]
    elif type1 is list and type2 is list and type3 is str:
        result_list = []
        for sub_element in object1:
            for old in object2:
                sub_element = sub_element.replace(str(old), str(object3))
            result_list.append(sub_element)
        return result_list

    # [List List List]
    elif type1 is list and type2 is list and type3 is list:
        result_list = []
        for current in object1:
            for index in range(0, len(object2)):
                current = current.replace(object2[index], object3[index])
            result_list.append(current)
        return result_list

    raise Exception

def first_replace(object1, object2, object3):

    if type(object1) is list:
        object1 = [str(x) for x in object1]
    elif type(object1) is not list:
        object1 = str(object1)

    if type(object2) is list:
        object2 = [str(x) for x in object2]
    elif type(object2) is not list:
        object2 = str(object2)

    if type(object3) is list:
        object3 = [str(x) for x in object3]
    elif type(object3) is not list:
        object3 = str(object3)

    type1 = type(object1)
    type2 = type(object2)
    type3 = type(object3)

    # [String String String]
    if type1 is str and type2 is str and type3 is str:
        object1 = object1.replace(object2, object3, 1)
        return object1

    # [String List String]
    elif type1 is str and type2 is list and type3 is str:
        for element in object2:
            object1 = object1.replace(str(element), object3, 1)
        return object1

    # [String List List]
    elif type1 is str and type2 is list and type3 is list:
        for index in range(0, len(object2)):
            object1 = object1.replace(
                str(object2[index]), str(object3[index]), 1)
        return object1

    # [List String String]
    elif type1 is list and type2 is str and type3 is str:
        result_list = []
        for sub_element in object1:
            sub_element = sub_element.replace(object2, object3, 1)
            result_list.append(sub_element)
        return result_list

    # [List List String]
    elif type1 is list and type2 is list and type3 is str:
        result_list = []
        for sub_element in object1:
            for old in object2:
                sub_element = sub_element.replace(str(old), str(object3), 1)
            result_list.append(sub_element)
        return result_list

    # [List List List]
    elif type1 is list and type2 is list and type3 is list:
        result_list = []
        for current in object1:
            for index in range(0, len(object2)):
                current = current.replace(object2[index], object3[index], 1)
            result_list.append(current)
        return result_list

    raise Exception

def divisors_of_number(n):

    temp_list = []
    for N in range(1, int(n) + 1):
        if int(n) % N == 0:
            temp_list.append(N)

    return temp_list

def insert(object1, character, location):
    if type(object1) is not list:
        object1 = str(object1)

    location = int(location)

    if location > len(object1):
        return object1

    if type(object1) is list:
        return object1[0:location] + [character] + object1[location + 1:]

    return object1[0:location] + str(character) + object1[location + 1:]

def mirror(a):
    if type(a) is not list:
        a = str(a).split("\n")

    result = []
    for element in a:
        reversed_element = transliterate(
            element[::-1], "<>{}()[]\\/", "><}{)(][/\\")
        result.append(element + reversed_element)

    return '\n'.join(result)

def vertical_mirror(a):
    if type(a) is not list:
        a = str(a).split("\n")

    result = []
    for element in a:
        result.append(element)

    for element in a[::-1]:
        reversed_element = transliterate(element,  "\\/", "/\\")
        result.append(reversed_element)

    return '\n'.join(result)

def vertical_intersected_mirror(a):
    if type(a) is not list:
        a = str(a).split("\n")

    result = []
    for element in a:
        result.append(element)

    for element in a[::-1][1:]:
        reversed_element = transliterate(element,  "\\/", "/\\")
        result.append(reversed_element)

    return '\n'.join(result)

def intersected_mirror(a):
    if type(a) is not list:
        a = str(a).split("\n")

    result = []
    for element in a:
        reversed_element = transliterate(
            element[::-1], "<>{}()[]\\/", "><}{)(][/\\")
        result.append(element[:-1] + reversed_element)

    return '\n'.join(result)

def transliterate(string: str, prev, next):
    processed = ""

    for character in string:
        has_replaced = False
        for Q in range(min(len(prev), len(next))):
            if character == prev[Q]:
                processed += next[Q]
                has_replaced = True
                break

        if not has_replaced:
            processed += character

    return processed

def string_multiplication(a, b):
    if type(a) is not list and type(b) is not list:
        try:
            try:
                return ast_int_eval(b) * str(a)
            except:
                return ast_int_eval(a) * str(b)
        except:
            result = []
            for x in a:
                temp_list = []
                for y in b:
                    temp_list.append(str(x) + str(y))
                result.append(temp_list)
            return result
    elif type(a) is not list and type(b) is list:
        try:
            return [str(a) * ast_int_eval(x) for x in b]
        except:
            return [str(x) * ast_int_eval(a) for x in b]
    elif type(b) is not list and type(a) is list:
        try:
            return [str(x) * ast_int_eval(b) for x in a]
        except:
            return [str(b) * ast_int_eval(x) for x in a]
    else:
        try:
            result = []
            for x in range(0, len(a)):
                result.append(str(a[x]) * ast_int_eval(b[x]))
            return result
        except:
            try:
                result = []
                for x in range(0, len(a)):
                    result.append(str(b[x]) * ast_int_eval(a[x]))
                return result
            except:
                result = []
                for x in a:
                    temp_list = []
                    for y in b:
                        temp_list.append(str(x) + str(y))
                    result.append(temp_list)
                return result

def even_divide(a, b):
    b = int(ast_int_eval(b))

    if type(a) is not list:
        a = str(a)

    result = []
    temp_list = []

    for index in range(0, len(a)):
        temp_list.append(a[index])
        if len(temp_list) == b:
            result.append(temp_list)
            temp_list = []

    if temp_list:
        result.append(temp_list)

    if type(a) is str:
        return list(map(''.join, result))

    return result

def get_hash(string):
    string = "filler for the string" + str(string)

    hash_num = 0
    for index in range(0, len(string)):
        hash_num += ord(string[index]) * (index + 1) ** 8
        hash_num %= 2**32

    return hash_num

def closest_to(a, b):
    if type(b) is list:
        return a

    try:
        b = ast_int_eval(b)
    except:
        # if a is not a list, applies char difference
        # otherwise we can use string distances
        if type(a) is not list:
            b = ord(b)
        pass

    if type(a) is list:
        a = deep_flatten(a)

        if type(b) is not str:
            try:
                a = [ast_int_eval(x) for x in a]
            except:
                # fallback to string distances if there is a string in a
                b = str(b)
    else:
        a = str(a)

    closest = a
    distance = float('inf')

    for element in a:
        if type(a) is str:
            if abs(ord(element) - b) < distance:
                distance = abs(ord(element) - b)
                closest = element
        elif type(b) is str:
            element_dist = 1 - SequenceMatcher(None, str(element), b).ratio()

            if element_dist < distance:
                distance = element_dist
                closest = str(element)
        elif abs(element - b) < distance:
            distance = abs(element - b)
            closest = element

    return closest

def zip_with(a, b):
    temp = a

    if type(temp) is not list:
        temp = str(temp)

    if type(temp) is str:
        temp = temp.split("\n")

    maximum_length = max([len(x) for x in temp])

    if type(a[0]) is list:
        temp_list = []
        for element in temp:
            temp_list.append(element + [str(b)] * (maximum_length - len(element)))
        filled_list = temp_list[:]
    else:
        filled_list = [str(x) + (str(b) * (maximum_length - len(x))) for x in temp]

    zipped_list = [list(x) for x in zip(*filled_list)]
    result = []

    if type(a) is not list:
        for element in zipped_list:
            result.append(''.join(element))
        result = '\n'.join(result)

    elif type(a[0]) is str:
        for element in zipped_list:
            result.append(''.join(element))

    else:
        result = zipped_list[:]

    return result

def multi_split(a, b: list):
    if type(a) is not list:
        a = str(a)

    delimiter = ''.join(
        random.choice(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM"
                      "NOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
        for _ in range(10))
    while delimiter in a:
        delimiter = ''.join(
            random.choice(" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLM"
                          "NOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
            for _ in range(10))

    a = transliterate(a, b, [delimiter] * len(b))

    return [i for i in a.split(delimiter) if i]

def shape_like(a, b):

    if type(b) is str:
        b = int(ast_int_eval(b))

    if type(a) is list and type(b) is list:
        result = []
        for element in b:
            result.append(shape_like(a, element))
        return result

    elif type(a) is list and type(b) is int:
        return (a * b)[:b]

    elif type(a) is not list and type(b) is list:
        result = []
        for element in b:
            result.append(shape_like(a, element))
        return result

    elif type(a) is not list and type(b) is not list:
        return (str(a) * b)[:b]

def sentence_case(a):
    a = str(a)
    temp_string = ""
    begin_sentence = True
    for Q in a:
        if begin_sentence:
            temp_string += Q.upper()
            if not Q == " ":
                begin_sentence = False
        else:
            temp_string += Q
        if Q == "." or Q == "?" or Q == "!":
            begin_sentence = True
    return temp_string

def list_multiply(a, b, recur=True):

    try:
        if type(a) is not list:
            a = [a]

        if type(b) is list:
            result = []

            for element in b:
                try:
                    result.append(a * int(element))
                except:
                    result.append(list_multiply(a, element, False))

            return result
        else:
            return a * int(b)

    except Exception as e:

        if recur:
            return list_multiply(b, a, False)

        else:
            raise e

def deltaify(a):
    if type(a) is not list:
        a = str(a)

    sublists = []
    values = []

    for i in a:
        if type(i) is list:
            sublists.append(i)
        else:
            try:
                values.append(ast_int_eval(i))
            except:
                pass

    result = []
    for Q in range(len(values) - 1):
        result.append(values[Q+1] - values[Q])

    if len(sublists):
        result += [deltaify(l) for l in sublists]

    return result

def filtered_to_the_front(a, b):
    if type(a) is not list:
        a = str(a)

    filtered = []
    remaining = []

    for i in a:
        if type(i) is list:
            remaining.append(filtered_to_the_front(i, b))
        elif str(i) == str(b):
            filtered.append(str(i))
        else:
            remaining.append(str(i))

    result = filtered + remaining

    return result if type(a) is list else ''.join([str(x) for x in result])

def bijective_base_conversion(a, to_base):
    a = int(a)

    number = ""
    while a:
        a -= 1
        r = a % to_base
        a = a // to_base
        r += 1
        if r < 0:
            a += 1
            r -= to_base
        number += str(r)
    return number[::-1]

def bijective_decimal_conversion(a, from_base):
    a = str(int(a))

    number = 0
    for Q in a:
        number = number * from_base + int(Q)
    return number

def uniquify(a, connected=False):
    buf = []

    if type(a) is not list:
        a = str(a)

    for item in a:
        if type(item) is not list:
            item = str(item)

        if item not in buf:
            buf.append(item)
        elif connected and len(buf) and buf[-1] != item:
            buf.append(item)

    return buf if type(a) is list else ''.join(buf)


import sys
import RPi.GPIO
import time
import os
from flask import Flask, render_template, Response, request
import signal
import atexit

from getcpu import *

# import camera driver
if os.environ.get('CAMERA'):
    Camera = import_module('camera_' + os.environ['CAMERA']).Camera
else:
    from camera import Camera

# Raspberry Pi camera module (requires picamera package)
from camera_pi import Camera

global lcontrol
global rcontrol
global ucontrol
global dcontrol
lcontrol = 6.25
rcontrol = 6.25
ucontrol = 6.25
dcontrol = 6.25

RPi.GPIO.setmode(RPi.GPIO.BCM)
RPi.GPIO.setup(18,RPi.GPIO.OUT)
RPi.GPIO.setup(23,RPi.GPIO.OUT)

app = Flask(__name__)

@app.route("/")
def main():
        return render_template("main.html")

def gen(camera):
    """Video streaming generator function."""
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

@app.route("/on")
def ledon():
        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(18,RPi.GPIO.OUT)
        RPi.GPIO.setup(23,RPi.GPIO.OUT)

        RPi.GPIO.output(23,True)

        return render_template("main.html")

@app.route("/off")
def ledoff():

        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(18,RPi.GPIO.OUT)
        RPi.GPIO.setup(23,RPi.GPIO.OUT)

        RPi.GPIO.output(23,False)
        RPi.GPIO.cleanup()

        return render_template("main.html")

@app.route("/shining")
def shining():
        RPi.GPIO.setmode(RPi.GPIO.BCM)
        RPi.GPIO.setup(18,RPi.GPIO.OUT)
        RPi.GPIO.setup(23,RPi.GPIO.OUT)

        pwm = RPi.GPIO.PWM(23,180)
        pwm.start(0)
        for i in range(0,5):
            for x in range (0,101,1):
                pwm.ChangeDutyCycle(x)
                time.sleep(.02)

            for x in range(100,-1,-1):
                pwm.ChangeDutyCycle(x)
                time.sleep(.02)
        pwm.stop()
        RPi.GPIO.cleanup()
        time.sleep(0.2)

        return render_template("main.html")

@app.route("/cpu")
def cpu():

    return getCPUtemperature()

@app.route('/cmd',methods=['POST'])
def cmd():

    atexit.register(RPi.GPIO.cleanup)
    RPi.GPIO.setmode(RPi.GPIO.BCM)
    RPi.GPIO.setup(12, RPi.GPIO.OUT)
    RPi.GPIO.setup(21, RPi.GPIO.OUT)
    b = RPi.GPIO.PWM(21,50)
    p = RPi.GPIO.PWM(12,50) #50HZ
    p.start(0)
    b.start(0)
    time.sleep(0.2)
    cont = str(request.get_data())

    if "left" in cont:
          global lcontrol
          if lcontrol <=12.5:
            lcontrol = lcontrol + 0.25
            atexit.register(RPi.GPIO.cleanup)
            p.ChangeDutyCycle(lcontrol)
            time.sleep(0.2)

    if "right" in cont:

        if lcontrol >=2.5:
            lcontrol = lcontrol - 0.25
            atexit.register(RPi.GPIO.cleanup)
            p.ChangeDutyCycle(lcontrol)
            time.sleep(0.2)
    if "up" in cont:

        global ucontrol
        if ucontrol >= 2.5:
            ucontrol -= 0.25
            atexit.register(RPi.GPIO.cleanup)
            b.ChangeDutyCycle(ucontrol)
            time.sleep(0.2)

    if "down" in cont:

        if ucontrol <= 12.5:
            ucontrol += 0.25
            atexit.register(RPi.GPIO.cleanup)
            b.ChangeDutyCycle(ucontrol)
            time.sleep(0.2)
    if "stop" in cont:
        atexit.register(RPi.GPIO.cleanup)
        b.ChangeDutyCycle(6.25)
        p.ChangeDutyCycle(6.25)
        time.sleep(0.2)

    return request.get_data()

@app.route('/video_feed')

def video_feed():
    """Video streaming route. Put this in the src attribute of an img tag."""
    return Response(gen(Camera()),
                    mimetype='multipart/x-mixed-replace; boundary=frame')

if __name__ == "__main__":
    app.run(host="0.0.0.0",port=1112)


import torch
from architectures import common_blocks
from architectures import encoders
import collections

InputResolution = collections.namedtuple(
    "input_resolution",
    "height width")

BOT_MID_CHANNELS = [512, 256, 128, 64, 32, 1]
DOWNSAMPLE_FACTOR = 16

class ConvBottleneck(torch.nn.Module):
    """
    The encoder for the convolutional autoencoder
    """
    def __init__(self,
                 input_res,
                 latent_size,
                 flatten_bottleneck=False,
                 compress_bottleneck=False,
                 bottleneck_channels=False):
        """
        :param input_res: the input resolution of the model (typically [256, 512] or [512, 1024]
        :param latent_size: the desired latent size of the bottleneck
        :param flatten_bottleneck: whether or not to apply flatten/linear operations to the bottleneck
        :param compress_bottleneck: whether or not to utilise bottleneck compression
        :param bottleneck_channels: if compression of bottlenecks, provide a list of channels
        """
        super(ConvBottleneck, self).__init__()

        self.input_res = InputResolution(input_res[0], input_res[1])
        self.bottleneck_h, self.bottleneck_w = common_blocks.get_bottleneck_res(input_res=self.input_res,
                                                                                downsample_factor=DOWNSAMPLE_FACTOR)
        # self.bottleneck_channels = bottleneck_channels[-1] if compress_bottleneck else bottleneck_channels[0]
        self.bottleneck_channels = 256
        self.bottleneck_mid_channels = bottleneck_channels
        self.latent_size = latent_size
        self.compress_bottleneck = compress_bottleneck
        self.flatten_bottleneck = flatten_bottleneck

        self.bridge = torch.nn.Sequential()
        self.bottleneck = torch.nn.Sequential()
        self.build_bottleneck()
        self.build_bridge()

    def build_bridge(self):
        if self.compress_bottleneck:
            self.bridge.add_module("bridge(downsample1)",
                                   common_blocks.ERFNetDownsampler(in_channels=256,
                                                                 out_channels=512))
            # self.bridge.add_module("bridge(downsample2",
            #                        common_blocks.ERFNetDownsampler(in_channels=450,
            #                                                      out_channels=512))

    def build_bottleneck(self):
        """
        Build the components related to the autoencoder bottleneck
        """
        if self.compress_bottleneck:
            self.bottleneck.add_module("stage(bottleneck_down)",
                                       common_blocks.BottleneckReduction(self.bottleneck_mid_channels))

        if self.flatten_bottleneck:
            self.bottleneck.add_module("stage(flatten)", torch.nn.Flatten())
            self.bottleneck.add_module("stage(linear)",
                                       torch.nn.Linear(self.bottleneck_h * self.bottleneck_w * self.bottleneck_channels,
                                                       self.latent_size))
            self.bottleneck.add_module("stage(1DBatchNorm)", torch.nn.BatchNorm1d(num_features=self.latent_size))
            self.bottleneck.add_module("stage(ReLU)", torch.nn.ReLU())

    def forward(self, x):
        # x = self.bridge(x)

        return self.bottleneck(x)

class ConvDecoder(torch.nn.Module):
    """
    The decoder of the convolutional autoencoder
    """
    def __init__(self,
                 input_res,
                 num_layers,
                 latent_size,
                 use_residual,
                 upsample_factor,
                 input_channels=256,
                 dropout_rate=None,
                 flatten_bottleneck=False,
                 compress_bottleneck=False,
                 bottleneck_channels=None):
        """
        :param input_res: the input resolution of the model (typically [256, 512] or [512, 1024]
        :param num_layers: the desired number of layers for the convolutional autoencoder
        :param latent_size: the desired latent size of the bottleneck
        :param use_residual: whether or not to use residual modules
        :param input_channels: the number of input channels (usually 3 for RGB images)
        :param dropout_rate: the desired dropout rate to use
        :param upsample_factor: the scale to upsample
        :param flatten_bottleneck: whether or not to apply flatten/linear operations to the bottleneck
        :param compress_bottleneck: whether or not to utilise bottleneck compression
        :param bottleneck_channels: if compression of bottlenecks, provide a list of channels
        """
        super(ConvDecoder, self).__init__()

        self.input_res = InputResolution(input_res[0], input_res[1])
        self.bottleneck_h, self.bottleneck_w = common_blocks.get_bottleneck_res(input_res=self.input_res,
                                                                                downsample_factor=DOWNSAMPLE_FACTOR)
        self.enc_out_channels, self.dec_out_channels, self.dilations = encoders.get_layer_size(num_layers, use_residual)

        self.latent_size = latent_size
        self.in_channels = input_channels
        self.upsample_factor = upsample_factor
        self.dropout = dropout_rate

        self.encoder_length = len(self.enc_out_channels)
        self.encoder_start = 1
        self.decoder_start = int(self.encoder_length + self.encoder_start)
        self.decoder_end = int(self.decoder_start + self.encoder_length - 1)
        self.use_residual = use_residual
        self.compress_bottleneck = compress_bottleneck
        self.flatten_bottleneck = flatten_bottleneck
        # self.bottleneck_channels = bottleneck_channels[-1] if compress_bottleneck else bottleneck_channels[0]
        self.bottleneck_channels = 256
        self.bot_mid_channels = bottleneck_channels

        self.decoder = torch.nn.Sequential()
        self.bottleneck = torch.nn.Sequential()
        self.bridge = torch.nn.Sequential()

        self.unpack_bridge()
        self.unpack_bottleneck()
        self.build_decoder()

    def unpack_bottleneck(self):
        """
        Build the components related to the autoencoder bottleneck
        """
        if self.flatten_bottleneck:
            self.bottleneck.add_module("stage(linear)",
                                       torch.nn.Linear(self.latent_size,
                                                       self.bottleneck_h * self.bottleneck_w * self.bottleneck_channels))
            self.bottleneck.add_module("stage(reshape)", common_blocks.Reshape(
                shape=(-1, self.bottleneck_channels, self.bottleneck_h, self.bottleneck_w)))

        if self.compress_bottleneck:
            self.bottleneck.add_module("stage(bottleneck_up)",
                                       common_blocks.BottleneckReduction(channels=self.bot_mid_channels,
                                                                         upsample=True))

    def unpack_bridge(self):
        """
        Unpack the bridge to match original encoder output EDANet dimensions
        """
        if self.compress_bottleneck:
            self.bridge.add_module("stage(upsample1)",
                                   common_blocks.ConvBlock(in_channels=512,
                                                           out_channels=256,
                                                           kernel_size=1,
                                                           stride=1,
                                                           padding=0,
                                                           use_bn=True,
                                                           activation=True,
                                                           upsample_factor=self.upsample_factor))

            # self.bridge.add_module("stage(upsample2)",
            #                        common_blocks.ConvBlock(in_channels=450,
            #                                                out_channels=256,
            #                                                kernel_size=1,
            #                                                stride=1,
            #                                                padding=0,
            #                                                use_bn=True,
            #                                                activation=True,
            #                                                upsample_factor=self.upsample_factor))

    def build_decoder(self):
        """
        Iteratively construct the decoder
        """
        for j, out_channels in enumerate(self.dec_out_channels, start=self.decoder_start):
            block_dilation = list(reversed(self.dilations))[j - self.decoder_start]

            if self.use_residual and self.in_channels / out_channels == 1:
                unit = common_blocks.ERFNetFactorisedResidualBlock(channels=out_channels,
                                                                   dilation=block_dilation,
                                                                   dropout_rate=self.dropout)

            else:
                if self.in_channels / out_channels == 2 or j == self.decoder_end:
                    upsample = self.upsample_factor
                else:
                    upsample = None

                unit = common_blocks.ConvBlock(in_channels=self.in_channels,
                                               out_channels=out_channels,
                                               kernel_size=3,
                                               stride=1,
                                               padding=1,
                                               dilation=block_dilation,
                                               use_bn=False if j == self.decoder_end else True,
                                               activation=False if j == self.decoder_end else True,
                                               upsample_factor=upsample,
                                               dropout_rate=None if j == self.decoder_end else self.dropout)

            self.in_channels = out_channels
            self.decoder.add_module(f"stage{j}", unit)

        self.decoder.add_module("stage(tanh)", torch.nn.Tanh())

    def forward(self, x):
        if self.compress_bottleneck or self.flatten_bottleneck:
            x = self.bottleneck(x)

        # x = self.bridge(x)
        x = self.decoder(x)
        return x

class ConvAE(torch.nn.Module):
    """
    A convolutional autoencoder comprising of 4, 6, 8, 10 layers. Implementations of residual modules and bottleneck
    compressions also included.
    """
    def __init__(self,
                 input_res,
                 num_layers,
                 latent_size,
                 use_residual,
                 upsample_factor=2,
                 input_channels=3,
                 dropout_rate=None,
                 flatten_bottleneck=False,
                 compress_bottleneck=False,
                 bottleneck_channels=BOT_MID_CHANNELS):
        """
        :param input_res: the input resolution of the model (typically [256, 512] or [512, 1024]
        :param num_layers: the desired number of layers for the convolutional autoencoder
        :param latent_size: the desired latent size of the bottleneck
        :param use_residual: whether or not to use residual modules
        :param input_channels: the number of input channels (usually 3 for RGB images)
        :param dropout_rate: the desired dropout rate to use
        :param upsample_factor: the scale to upsample
        :param flatten_bottleneck: whether or not to apply flatten/linear operations to the bottleneck
        :param compress_bottleneck: whether or not to utilise bottleneck compression
        :param bottleneck_channels: if compression of bottlenecks, provide a list of channels
        """
        super(ConvAE, self).__init__()

        self.flatten_bottleneck = flatten_bottleneck
        self.compress_bottleneck = compress_bottleneck

        self.encoder = encoders.ConvEncoder(input_res=input_res,
                                            num_layers=num_layers,
                                            use_residual=use_residual,
                                            input_channels=input_channels,
                                            dropout_rate=dropout_rate)

        self.bottleneck = ConvBottleneck(input_res=input_res,
                                         latent_size=latent_size,
                                         flatten_bottleneck=flatten_bottleneck,
                                         compress_bottleneck=compress_bottleneck,
                                         bottleneck_channels=bottleneck_channels)

        self.decoder = ConvDecoder(input_res=input_res,
                                   num_layers=num_layers,
                                   latent_size=latent_size,
                                   upsample_factor=upsample_factor,
                                   use_residual=use_residual,
                                   dropout_rate=dropout_rate,
                                   flatten_bottleneck=flatten_bottleneck,
                                   compress_bottleneck=compress_bottleneck,
                                   bottleneck_channels=bottleneck_channels)

    def forward(self, x):
        x = self.encoder(x)

        if self.compress_bottleneck or self.flatten_bottleneck:
            x = self.bottleneck(x)
        x = self.decoder(x)

        return x


from PIL import Image
im = Image.open('cave.jpg')
w, h = im.size
imgs = [Image.new(im.mode, (w / 2, h / 2)) for dummy in xrange(4)]
imgs_load = [i.load() for i in imgs]
org = im.load()
for i in xrange(w):
    for j in xrange(h):
        org_pos = (i, j)
        new_pos = (i // 2, j // 2)
        imgs_load[i % 2 + j % 2 * 2 ][new_pos] = org[org_pos]
[imgs[i].save('%d.png' % i) for i in xrange(4)]


# http://localhost:63342/website-olivier-codol/docs/index.html
import os
import shutil
import datetime
import copy

# function to get file content
def readfile(filename):
    pointer = open(filename, 'r')
    content = pointer.read()
    pointer.close()
    return content

# find a snippet between a start landmark and an end landmark
def find_content_snippet(content, start_snippet, end_snippet):
    start_pos = content.find(start_snippet) + len(start_snippet)
    truncated_content = content[start_pos:]
    relative_end_pos = truncated_content.find(end_snippet)
    end_pos = relative_end_pos + start_pos
    content_snippet = content[start_pos:end_pos]
    if content.find(start_snippet) == -1:
        content_snippet = ""
    return content_snippet

# get directory paths
script_dir = os.path.dirname(os.path.realpath(__file__))
src_dir = os.path.join(script_dir, 'src')
compil_dir = os.path.join(script_dir, 'docs')
template_dir = os.path.join(script_dir, 'template')

##########################
# BLOG LIST CREATION
##########################

files = [os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser(src_dir + "\\blogposts")) for f in fn]
ix = len(src_dir) + 1

bloglist = readfile(template_dir + '/bloglist-header.html')
bloglist_entry = readfile(template_dir + '/bloglist-entry.html')
files.reverse()  # most recent item first

for source_file in files:

    # find post title, creation and modification date, and relative path
    blog_content = readfile(source_file)
    title = find_content_snippet(blog_content, """ id="blogpost-title">""", "<")
    mtime = find_content_snippet(blog_content, """Posted on """, ".")
    ctime = find_content_snippet(blog_content, """Last modified on """, ".")
    if mtime:
        mtime = "posted on " + str(datetime.datetime.strptime(mtime, "%A %d %B %Y").date())
    if ctime:
        ctime = "last modified on " + str(datetime.datetime.strptime(ctime, "%A %d %B %Y").date())
    rel_path = source_file[ix:]

    bloglist_entry_new = copy.deepcopy(bloglist_entry)
    bloglist_entry_new = bloglist_entry_new.replace("insert-path-here", rel_path)
    bloglist_entry_new = bloglist_entry_new.replace("insert-title-here", title)
    bloglist_entry_new = bloglist_entry_new.replace("posted on insert-creation-date-here", mtime)
    bloglist_entry_new = bloglist_entry_new.replace("last modified on insert-modification-date-here", ctime)

    # create entry
    bloglist = bloglist + bloglist_entry_new

# add footer
bloglist = bloglist + readfile(template_dir + '/bloglist-footer.html')

# write final page
fp = open(src_dir + "\\blog.html", 'r+')
fp.truncate(0)  # erase all previous content
fp.seek(0)
fp.write(bloglist)
fp.close()

##########################
# END OF BLOG LIST CREATION
##########################

##########################
# FULL WEBSITE CREATION
##########################

# get template (static) contents
header = readfile(template_dir + '/header.html')
navgtn = readfile(template_dir + '/navigation.html')
footer = readfile(template_dir + '/footer.html')
blog_header = readfile(template_dir + '/blogpost-header.html')
blog_footer = readfile(template_dir + '/blogpost-footer.html')

# get list of files to compile
files = [os.path.join(dp, f) for dp, dn, fn in os.walk(os.path.expanduser(src_dir)) for f in fn]
ix = len(src_dir) + 1

for source_file in files:

    file = source_file[ix:]
    if file == "index-content.html":
        file = "index.html"
    compil_file = os.path.join(compil_dir, file)

    # this highlights the navigation tab on the corresponding page
    navitem_old_not = """li class="nav-item not-selected">"""[1:]
    navitem_old = navitem_old_pre + file + navitem_old_suf
    navitem_new_pre = """li class="nav-item selected"> ' + compil_file)

    fp = open(compil_file, 'r+')
    source = fp.read()

    # this rewrites href and src paths for files in subdirectories
    relative_path = "../" * file.count("\\")
    href_flag = """href=" """[:-1]
    src_flag = """src=" """[:-1]
    header_new = header.replace(href_flag + "css", href_flag + relative_path + "css")
    navgtn_new = navgtn.replace(navitem_old, navitem_new)
    navgtn_new = navgtn_new.replace(navitem_old_pre, navitem_old_not)
    navgtn_new = navgtn_new.replace(src_flag, src_flag + relative_path)
    navgtn_new = navgtn_new.replace(
        """class="nav-link" """ + href_flag,
        """class="nav-link" """ + href_flag + relative_path)
    navgtn_new = navgtn_new.replace(
        """class="navbar-brand" """ + href_flag,
        """class="navbar-brand" """ + href_flag + relative_path)
    source_new = source.replace(href_flag + "http", """hr__ef="http""")  # hide http hrefs from next line's replacement
    source_new = source_new.replace(href_flag, href_flag + relative_path)
    source_new = source_new.replace("""hr__ef="http""", href_flag + "http")  # restore http hrefs
    source_new = source_new.replace(src_flag, src_flag + relative_path)

    footer_new = footer.replace(src_flag + "http", """sr__c="http""")
    footer_new = footer_new.replace(src_flag, src_flag + relative_path)
    footer_new = footer_new.replace("""sr__c="http""", src_flag + "http")

    # add blogpost header if page is a blog post
    if file[:file.find("\\")] == "blogposts":
        source_new = blog_header + source_new + blog_footer

    # write final page
    fp.truncate(0)  # erase all previous content
    fp.seek(0)
    page = header_new + navgtn_new + source_new + footer_new
    fp.write(page)
    fp.close()

##########################
# ENF OF FULL WEBSITE CREATION
##########################


import torchvision
import torch
from torch.utils.data import Dataset
import numpy as np
import random

# ラベル	クラス
# 0	T-シャツ/トップ (T-shirt/top)
# 1	ズボン (Trouser)
# 2	プルオーバー (Pullover)
# 3	ドレス (Dress)
# 4	コート (Coat)
# 5	サンダル (Sandal)
# 6	シャツ (Shirt)
# 7	スニーカー (Sneaker)
# 8	バッグ (Bag)
# 9	アンクルブーツ (Ankle boot)

near_cat_dict = {0:[0,1,5,6,7,9], 1:[0,1,2,5,6,7,9], 2:[1,2,5,7], 3:[3,4,8],
                 4:[1,4,6,8,9], 5:[0,1,5,6], 6:[1,5,6,7,9], 7:[0,1,6,7],
                 8:[3,4,8,9], 9:[1,4,6,8,9]}
# far_cat = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}

class FMNISTDataset(Dataset):
    def __init__(self, n_class=10, train=True):
        super().__init__()
        self.n_class = n_class
        self.train = train
        self.n_relation = self.n_class**2
        self.fashion_mnist_data = torchvision.datasets.FashionMNIST(
            './fashion-mnist',
            transform=torchvision.transforms.ToTensor(),
            train=train,
            download=True)
        self.labels = [fnmn[1] for fnmn in torchvision.datasets.FashionMNIST('./fashion-mnist')]
        self.labels = np.array(self.labels, dtype=np.int32)

    def __len__(self):
        return len(self.fashion_mnist_data)

    def __getitem__(self, idx):
        image, cat = self.fashion_mnist_data[idx]
        return image, cat

def loader(dataset, batch_size, shuffle=True):
    loader = torch.utils.data.DataLoader(
        dataset,
        batch_size=batch_size,
        shuffle=shuffle,
        num_workers=0)
    return loader


import glob
import re

import win32ui
import win32gui
import win32con
import win32api
import os
from PIL import Image

class IconExtract():

    def __init__(self):
        self.patternEXE = r'./**/*.exe'
        self.fileListEXE = glob.glob(self.patternEXE, recursive=True)
        regex = re.compile(r"^.*minst.*$", re.IGNORECASE)
        self.fileListEXE_filtered = [i for i in self.fileListEXE if not regex.match(i)]

        self.patternMSI = r'./**/*.msi'
        self.fileListMSI = glob.glob(self.patternMSI, recursive=True)

        self.fileList = self.fileListEXE_filtered + self.fileListMSI

        self.pathToSaveIcons = r'./icons'
        if not os.path.exists(self.pathToSaveIcons):
            os.mkdir(self.pathToSaveIcons)

        self.numberOfSetupFiles = 0

    def extract_icon_from_exe(self, icon_in_path, icon_name, icon_out_path, size=120):

        try:
            ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON)
            ico_y = win32api.GetSystemMetrics(win32con.SM_CYICON)

            large, small = win32gui.ExtractIconEx(icon_in_path, 0)
            win32gui.DestroyIcon(small[0])

            hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
            hbmp = win32ui.CreateBitmap()
            hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_y)
            hdc = hdc.CreateCompatibleDC()

            hdc.SelectObject(hbmp)
            hdc.DrawIcon((0, 0), large[0])

            bmpstr = hbmp.GetBitmapBits(True)
            icon = Image.frombuffer('RGBA', (32, 32), bmpstr, 'raw', 'BGRA', 0, 1)

            full_outpath = os.path.join(icon_out_path, "{}.png".format(icon_name))
            # print(icon_name)
            icon.resize((size, size))
            icon.save(full_outpath)
        except:
            full_outpath = './images/msi.ico'

        #return the final path to the image
        return full_outpath

    def extract(self):
        self.filesDict = []
        for f in self.fileList:
            fileInstall = {}
            name = f.split("\\")
            # print(f)
            self.numberOfSetupFiles +=1
            # print(name[-1])
            self.extract_icon_from_exe(f, name[-1], self.pathToSaveIcons)
            # print(self.pathToSaveIcons)
            fileInstall['num'] = self.numberOfSetupFiles
            fileInstall['img'] = name[-1]
            fileInstall['path'] = f
            self.filesDict.append(fileInstall)

        return self.filesDict


from flask import Flask, request, render_template
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.preprocessing import StandardScaler
import pickle

app = Flask(__name__)
# import ridge regressor model and  standar scalar pickel file
ridge_model = pickle.load(open("Models/ridge.pkl", "rb"))
StandardScalar_s = pickle.load(open("Models/StandardScalar.pkl", "rb"))

# Route for home page

# @app.route("/")
# def home_page():
#     return render_template("index.html")

@app.route("/", methods=['GET', 'POST'])
def predict():
    if request.method == 'POST':
        Temperature = request.form.get('Temperature')
        RH = request.form.get('RH')
        Ws = request.form.get('Ws')
        Rain = request.form.get('Rain')
        FFMC = request.form.get('FFMC')
        DMC = request.form.get('DMC')
        ISI = request.form.get('ISI')
        Classes = request.form.get('Classes')
        Region = request.form.get('Region')

        new_data_sc = StandardScalar_s.transform(
            [[Temperature, RH, Ws, Rain, FFMC, DMC, ISI, Classes, Region]])
        result = ridge_model.predict(new_data_sc)

        return render_template("home.html", result=result[0])

    else:
        return render_template("home.html")

if __name__ == "__main__":
    app.run(host="0.0.0.0")


# MPIP
# DABM 2022-2

from PIL import Image
import time
import matplotlib.pyplot as plt
#from statistics import mean
import statistics

print("Por favor digital el nombre del archivo del que desees recoger la información (No ingreses el .csv al final!)")
nombre_archivo_revisar = input(">>> ")
a = open(nombre_archivo_revisar+".csv", "r")
lineas = a.readlines()
print(lineas)
# print("El tipo de dato de datos es: ", lineas.type())
valores_temperatura = []
# datos.append(lineas)
for l in (lineas):
    # print(l)
    l = l.replace("\n", "")
    l = l.replace("", "")
    l = l.split(";")
    valores_temperatura.append(l)

#print("el valor maximo encontrado fue: ", valores_temperatura[])
#print(str(max(valores_temperatura)))
#print("el valor minimo encontrado fue: ")
#print(str(min(valores_temperatura)))

#print(str(statistics.mean(valores_temperatura)))
#print("el valor promedio es: ", mean(int(valores_temperatura)))
#print("El valor promedio es : ", str(mean(valores_temperatura)))

print("La fecha en la cual fueron tomados estos datos fue: (AAAA-MM-DD)", nombre_archivo_revisar[:10])
#nombre_archivo_revisar[:nombre_archivo_revisar.rfind("_")])

print("Presentando la imagen:")
time.sleep(1)
#print("Por favor digital el nombre de la imagen (no olvides agregar el jpg al final!)")
#nombre_imagen_presentar = input(">>> ")
#plt.show(nombre_imagen_presentar)
im = Image.open(nombre_archivo_revisar+".jpg")
im.show()


# coding:utf-8
from utils.user_agent import UserAgentUtil

user_agent = UserAgentUtil()

base_header = {
    "accept": "application/json, text/plain, */*",
    "accept_encoding": "gzip, deflate, br",
    "accept_language": "zh-CN,zh;q=0.9,en;q=0.8",
    "origin": "http://haohuo.jinritemai.com",
    "sec_fetch_mode": "cors",
    "sec_fetch_site": "cross-site",
}

def build_request(url):
    """构建下载中心请求"""
    base_header["user_agent"] = user_agent.random_one()
    return {
        "url": {"url": url},
        "header": base_header
    }


import numpy
import math
import matplotlib.pyplot as plt
from scipy import stats
speed = [32,111,138,28,59,77,97]
x=numpy.mean(speed)
print("The mean is ", x)
print("The median is " ,numpy.median(speed))

# #  let calculate the mode
# print("The mode is " , stats.mode(speed))

# let me calculate standard deviation
print("The standard deviation is ",numpy.std(speed))

# let me calculate variance
"""The variance means the std square root of the std """
# python has method to handle it
variance=numpy.var(speed)
print("The variance is ",variance)
print("The std using variance is ",math.sqrt(variance))

# let me find percentile
age=[90,20,20,30,45,6,1,2,3,3,45,67,78,67,78,90,91]
print("Percentile is ",numpy.percentile(age,100))

# let me generate the random numbers 500 between 0.0 and 5.0 by using Numpy module
randomNumbes=numpy.random.uniform(0.0,5.0,1000)
# let me present the generated numbers on the histogram
plt.pie(randomNumbes)
plt.show()

# let me generate the normal
y=numpy.random.normal(5.0,1.0,1000)
plt.hist(y,10)
plt.show()


import streamlit as st
from PIL import Image
import pandas as pd
import matplotlib.pyplot as plt
from plotly.subplots import make_subplots
import plotly.subplots as sp
import plotly_express as px
import plotly.graph_objs as go
from streamlit_folium import st_folium
import altair as alt

def Visualisations():
    image3 = Image.open('streamlit/images/GettyImages-155141288-polar-bear-1200x630-2.jpeg')
    st.image(image3)
    st.title("Analyses et Visualisations")

    st.subheader("Analyse des anomalies de températures")
    st.write("Hypothèse: nous faisons l’hypothèse que les variations de températures enregistrées ont tendance à être positives, ce qui signifie que la température du globe à tendance à augmenter de manière générale. Nous essaierons de déterminer les zones où les températures ont tendance à le plus augmenter et à savoir si cette tendance haussière est homogène au fil du temps.")
    st.info("Etude des anomalies globales")

    # selecteur pour slider
    values = [1, 2, 3]
    for i, value in enumerate(values):
        key = f"value_slider_{i}"

    ### Anomalies de la température globale et de l'incertitude
    ### Notebook Analyse Temp Globales + Hémisphèriques Berkeley

    # import Data
    df = pd.read_csv("Datasets/températures_globales_hémisphériques.csv", index_col=0)

    # Data Processing
    Temp_country_mean = df.groupby('Year').mean().reset_index()

    # Define the x values for the curves
    x = Temp_country_mean.Year
    # Define the two curves to plot
    y1 = Temp_country_mean['Anomaly-Gl']
    y2 = Temp_country_mean['Uncertainty-Gl']

    # Ploting
    # Create the plot trace for the first curve
    trace1 = go.Scatter(x=x, y=y1, mode='lines', name='Anomalie', showlegend=True)

    # Create the plot trace for the second curve
    trace2 = go.Scatter(x=x, y=y2, mode='lines', name='Incertitude', visible=False, showlegend=True)

    # create the layout and add the traces
    layout = go.Layout(
        title="Anomalies de la température globale et de l'incertitude de 1753 à 2022",
        xaxis=dict(title='Année'),
        yaxis=dict(title='Valeur', range=[-3, 3.5]),
        updatemenus=[dict(
            type='buttons',
            buttons=[dict(
                label="Afficher l'Incertitude",
                method='update',
                args=[{'visible': [True, not trace2.visible]}],
                args2=[{'visible': [True, trace2.visible]}],
            )]
        )]
    )

    # create the figure and plot it
    fig = go.Figure(data=[trace1, trace2], layout=layout)

    # define a callback function to update the layout
    def update_layout(trace, points, state):
        if trace.name == 'Incertitude':
            fig.layout.title = 'Anomalies de températures globales et Incertitude (Incertitude is ' + ('visible' if trace.visible else 'hidden') + ')'

    # add the callback function to the figure
    fig.data[0].on_click(update_layout)
    fig.data[1].on_click(update_layout)
    st.write(fig)

    st.write("Nous constatons que l’incertitude était très élevée lors des premières mesures, ici 1753 et qu’elle a grandement diminué pour être pratiquement nulle depuis les années 1960. Cela est dû au fait que les équipements de mesures sont de plus en plus précis et que leur fiabilité est croissante.")

    ### Anomalies des températures hémisphériques et des incertitudes
    # Define the two curves to plot
    y1 = Temp_country_mean['Anomaly-NH']
    y2 = Temp_country_mean['Uncertainty-NH']
    y3 = Temp_country_mean['Anomaly-SH']
    y4 = Temp_country_mean['Uncertainty-SH']

    # Create the plot trace for the first curve
    trace1 = go.Scatter(x=x, y=y1, mode='lines', name='Anomalie Hémisphère Nord', line=dict(color='blue'), showlegend=True)

    # Create the plot trace for the second curve
    trace2 = go.Scatter(x=x, y=y2, mode='lines', name='Incertitude Hémisphère Nord', line=dict(color='green'), visible=False, showlegend=True)

    # Create the plot trace for the first curve
    trace3 = go.Scatter(x=x, y=y3, mode='lines', name='Anomalie Hémisphère Sud', line=dict(color='orange'), showlegend=True)

    # Create the plot trace for the second curve
    trace4 = go.Scatter(x=x, y=y4, mode='lines', name='Incertitude Hémisphère Sud', line=dict(color='purple'), visible=False, showlegend=True)

    # create the layout and add the traces
    layout = go.Layout(
        title='Anomalies des températures hémisphériques et des incertitudes de 1840 à 2022',
        xaxis=dict(title='Année', range=[1840, max(x)]),
        yaxis=dict(title='Valeur', range=[-1.5, 2]),
        updatemenus=[dict(
            type='buttons',
            buttons=[dict(
                label="Afficher l'Incertitude",
                method='update',
                args=[{'visible': [True, False, True, False]}],
                args2=[{'visible': [True, True, True, True]}],
            )]
        )]
    )

    # create the figure and plot it
    fig = go.Figure(data=[trace1, trace2, trace3, trace4], layout=layout)

    # define a callback function to update the layout
    def update_layout(trace, points, state):
        if trace.name == 'Incertitude':
            fig.layout.title = 'Anomalies de températures globales et Incertitude (Incertitude is ' + ('visible' if trace.visible else 'hidden') + ')'

    # add the callback function to the figure
    fig.data[0].on_click(update_layout)
    fig.data[3].on_click(update_layout)
    st.write(fig)

    st.write("Comme pour le graphe précédent, l’incertitude était très importante au moment des premières mesures mais elle a beaucoup diminué au fur et à mesure du temps. Les données ont été collectées plus tôt dans l’hémisphère Nord et nous constatons, comme avec les données de la NASA que l’hémisphère Nord est plus impacté par le réchauffement climatique que l’hémisphère Sud")
    st.write("Nous constatons que la variation des températures est à la hausse depuis 1880, c'est-à-dire la fin de la Révolution Industrielle.. Après la fin de cette période et la fin de l’utilisation massive du charbon, la température a légèrement diminué; mais à partir de 1910 la température a suivi une courbe légèrement exponentielle à la hausse. Cette tendance haussière s’est accentuée à partir des années 1970. La crise COVID a légèrement influé sur les températures à court terme mais sur le long terme, la tendance reste la même. Cette augmentation est homogène et suit la même évolution quelque soit la zone et le mois de l’année.")
    st.write("Nous constatons également que cette hausse est plus élevée dans l’hémisphère nord que dans l’hémisphère sud. Et enfin nous constatons que c’est aux pôles que les variations de températures sont les plus importantes et c’est au pôle Nord que les températures ont le plus augmenté.")

    st.write("Avec le graphique suivant, vous pouvez choisir l'année et avoir un aperçu des anomalies sur les mois de l'année. Vous constaterez que les anomalies ont tendance à augmenter quel que soit le mois.")

    # import Data
    df_temp_pays = pd.read_csv("Datasets/Températures_par_pays_Vertical.csv")

    # Data Processing
    # Calcul de température moyenne par pays
    Temp_country_mean = df_temp_pays.groupby(['Country', 'Year']).mean().reset_index()
    # Selection de l'année à partir de 1880
    Temp_country_mean = Temp_country_mean[Temp_country_mean['Year'] >= 1880]
    # create a dictionary to map numeric values to month names
    month_names = {1: 'Janvier', 2: 'Février', 3: 'Mars', 4: 'Avril', 5: 'Mai', 6: 'Juin',
                   7: 'Juillet', 8: 'Aout', 9: 'Septembre', 10: 'Octobre', 11: 'Novembre', 12: 'Décembre'}

    # replace numeric values in Month column with month names
    df['Month_str'] = df['Month'].replace(month_names)

    ### Diagramme polaire des Anomalies de température par mois
    # create subplots with polar charts
    fig = make_subplots(rows=1, cols=1, subplot_titles=('',),
                        specs=[[{'type': 'polar'}]])

    # create traces for each year, with a slider to select the year
    for i, year in enumerate(df['Year'].unique()):
        trace = go.Scatterpolar(r=df[df['Year']==year]['Anomaly-Gl'],
                                theta=df[df['Year']==year]['Month_str'],
                                name=str(year), # convert Year in string
                                visible=(i==0), # set first year to visible
                                showlegend=False)
        fig.add_trace(trace)

    # adjust polar plot layout
    fig.update_layout(polar=dict(radialaxis=dict(range=[-3, 3])),
                      updatemenus=[dict(type='buttons',
                                        showactive=False,
                                        buttons=[dict(label='Play',
                                                      method='animate',
                                                      args=[None, {'frame': {'duration': 100, 'redraw': True},
                                                                   'fromcurrent': True,
                                                                   'transition': {'duration': 0}}]),
                                                 dict(label='Pause',
                                                      method='animate',
                                                      args=[[None], {'frame': {'duration': 0, 'redraw': False},
                                                                     'mode': 'immediate',
                                                                     'transition': {'duration': 0}}])])],
                      sliders=[dict(active=0,
                                     yanchor='top',
                                     xanchor='left',
                                     currentvalue=dict(font=dict(size=16), prefix='Year: ', visible=True, xanchor='right'),
                                     pad=dict(t=35, r=20),
                                     len=0.9,
                                     steps=[dict(label=str(year),
                                                 method='update',
                                                 args=[{'visible': [(i == j) for j in range(len(df['Year'].unique()))]},
                                                       {'title': "Anomalies de température globales pour l'année {}".format(year)}])
                                            for i, year in enumerate(df['Year'].unique())])])

    fig.update_layout(width=700, height=480)
    st.write(fig)

    st.write(" ")
    st.markdown("**Analyse des anomalies de température depuis 2000 ans**")
    st.write("A partir des données PAGES2K, nous vons obtenu le graphique suivant qui montre les anomalies de températures depuis l'année 0 jusqu'en 2014.")
    st.write("Visualisation avec uniquement les valeurs de la Full Ensemble Median ainsi que la moyenne filtrée sur une période de 31 ans (31-year filtered full ensemble median)")
    image_pages2K = Image.open('streamlit/Graphs/Courbe Temp depuis 2000 ans V2.png')
    st.image(image_pages2K)
    st.write("Nous constatons que la courbe est assez plate pendant le 1er millénaire, avec une légère hausse vers la période 1000-1200 puis une baisse allant d’environ 1500 à 1800, puis une montée très prononcée à partir du 20 ème siècle.")
    st.write("Les données montrent que la période moderne est très différente de ce qui s'est passé dans le passé. La période chaude médiévale et le petit âge glaciaire souvent cités sont des phénomènes réels, mais petits par rapport aux changements récents.")
    st.write("Le réchauffement au cours des 50 dernières années est brutal par rapport aux variations qui se sont produites naturellement au cours des 2000 dernières années.")
    st.write(" ")

    # import Data
    df_temp_pays = pd.read_csv("Datasets/Températures_par_pays_Vertical.csv")

    # Data Processing
    # Calcul de température moyenne par pays
    Temp_country_mean = df_temp_pays.groupby(['Country', 'Year']).mean().reset_index()
    # Selection de l'année à partir de 1880
    Temp_country_mean = Temp_country_mean[Temp_country_mean['Year'] >= 1880]
    # create a dictionary to map numeric values to month names
    month_names = {1: 'Janvier', 2: 'Février', 3: 'Mars', 4: 'Avril', 5: 'Mai', 6: 'Juin',
                   7: 'Juillet', 8: 'Aout', 9: 'Septembre', 10: 'Octobre', 11: 'Novembre', 12: 'Décembre'}

    # replace numeric values in Month column with month names
    df['Month_str'] = df['Month'].replace(month_names)

    st.text("")
    st.info("Etude des anomalies par pays")

    st.text("")
    st.write("Avec le sélecteur suivant, vous pouvez afficher les anomalies de température par pays.")

    # Creation d'un selecteur mulit-pays pour afficher les courbes de températures
    countries = st.multiselect('Sélectionner un ou plusieurs pays', Temp_country_mean['Country'].unique())

    # Filter the data by selected countries
    df_countries = Temp_country_mean[Temp_country_mean['Country'].isin(countries)]

    # Create a line plot of temperature anomaly and CO2 for selected countries
    fig = go.Figure()
    for country in countries:
        df_temp = df_countries[df_countries['Country'] == country]
        fig.add_trace(go.Scatter(x=df_temp['Year'], y=df_temp['Anomaly'], name=f'{country}'))
    fig.update_layout(title='Anomalies de températures', xaxis_title='Anomalies', yaxis_title='Valeur')
    st.plotly_chart(fig)
    st.text("")

    st.write("Nous constatons que les tendances des anomalies de températures sont sensiblement identiques sur tous les pays et que ce phénomène est donc globale dans le temps.")

    ### Données par pays
    ### Notebook Analyse Température par pays - Cartes interactives

    # creation d'une fonction pour créer une colonne classe en fonction des variations
    def map_anomaly(x):
        if x < 1:
            return 'moins de 1°'
        elif x < 2:
            return '1° à 2°'
        elif x < 3:
            return '2° à 3°'
        else:
            return 'plus de 3°'

    # use the apply() function to create a new column based on the values in the 'Anomaly' column
    Temp_country_mean['Classe'] = Temp_country_mean['Anomaly'].apply(map_anomaly)

    # Pays avec les anomalies les plus importantes
    top_15 = Temp_country_mean.groupby('Country').sum().sort_values('Anomaly', ascending=False)[:15].reset_index()['Country']

    # Pays avec les anomalies les moins importantes
    bottom_15 = Temp_country_mean.groupby('Country').sum().sort_values('Anomaly', ascending=True)[:15].reset_index()['Country']

    st.write("")
    st.write("A partir des données à notre disposition, nous avons sélectionner 30 pays, 15 pays les plus impactés et 15 pays les moins impactés. La sélection a été réalisée à partir de la valeur moyenne des anomalies de températures.")
    st.write("Vous pouvez voir un aperçu des pays les plus et les moins impactés par le changement climatiques. Avec les 2 graphiques suivants, nous voyons que la hausse des températures affectent tous les pays, sur tous les continents.")
    # Visualisation des anomalies de température pour les pays les plus impactés
    Temp_country_mean_top = Temp_country_mean[Temp_country_mean['Country'].isin(top_15)]
    fig = px.bar(Temp_country_mean_top, x='Anomaly', y='Country', animation_frame='Year', hover_name='Anomaly',
    range_x=[-3,4],color='Country', labels={
                  "Anomaly": "Anomalie",
                  "Country": "Pays"}, title = "Anomalies de température pour les 15 pays les plus impactés",width=800, height=500)
    fig.update_layout(showlegend=False)
    st.write(fig)

    # Visualisation des anomalies de température pour les pays les moins impactés
    Temp_country_mean_bottom = Temp_country_mean[Temp_country_mean['Country'].isin(bottom_15)]
    fig = px.bar(Temp_country_mean_bottom,x='Anomaly', y='Country', animation_frame='Year', hover_name='Anomaly',
      range_x=[-3,4], color='Country', labels={
                            "Anomaly": "Anomalie", "Country": "Pays"},
          title = "Anomalies de température pour les 15 pays les moins impactés", width=800, height=500)
    fig.update_layout(showlegend=False)
    st.write(fig)

    st.write("Nous constatons que tous les pays sont impactés par la hausse des températures. Nous faisons l'hypothèse que la hausse est plus importante pour les pays les plus industrialisés de l'hémisphère Nord, tandis que les hausses les plus faibles sont constatés sur des territoires peu industrialisés et situé majoritairement dans l'hémisphère Sud.")
    st.write("")

    ### carte interactive
    st.markdown("**Carte interactive des anomalies de températures**")
    st.write("Avec la carte suivante, vous pouvez afficher les anomalies de températures en sélectionnant l'année. En zoomant et en pointant sur un pays, vous pouvez avoir une vision précise des anomalies à un instant T.")
    st.write("Choississez l'année pour voir les mesures:")
    # remplacement de noms de pays en corrélation avec les nom s dans notre fichier geaojson
    Temp_country_mean = Temp_country_mean.replace(to_replace=["Dominican", "Burma", "Bosnia And Herzegovina", "Czech Republic", 'United States', "Ivorycoast", "Congo (Democratic Republic Of The)", 'Tanzania'], value=["Dominican Republic", "Myanmar", "Bosnia and Herzegovina", "Czechia", "United States of America", "Ivory Coast", "Democratic Republic of the Congo", "United Republic of Tanzania"])

    # Create the slider for the year
    year = st.slider("Année", min_value=1880, max_value=2020, value=1880)

    # Filter the data by the selected year
    data = Temp_country_mean[Temp_country_mean["Year"] == year]

    # Create the figure
    fig = px.choropleth(data,
                        locations="Country",
                        locationmode="country names",
                        color="Anomaly",
                        color_continuous_scale=px.colors.sequential.Plasma_r,
                        range_color=[-2.5, 4],
                        labels={"Anomaly": "Anomalies de température en °C"})

    # Update the layout
    fig.update_layout(title=f"Anomalies de température pour l'année {year}", margin=dict(t=50, b=50),height=400)

    # Display the figure
    st.plotly_chart(fig)

    st.write("Nous constatons que globalement l'ensemble des pays subissent une hausse des températures. Cette évolution n'est pas similaire pour tous les pays et certains sont très touchés par le réchauffement climatique alors que d'autres le sont beaucoup moins.")
    st.write("A partir de l’analyse et de la visualisation des données, nous démontrons que la hausse des températures est apparue à la fin de la Révolution Industrielle, qu’elle s’est accentuée à partir des années 1970, quel que soit le mois de l’année. Les pays de l'hémisphère Nord sont les plus impactés car on trouve dans cette zone les pays les plus industrialisés. Le pôle Nord est l’endroit le plus impacté par le changement climatique.")

    ###Analyse du C02
    st.text("")
    st.subheader("Analyse des émissions de CO2")

    st.markdown("**Analyse du CO2 dans l’atmosphère**")
    st.write("Pour poursuivre dans notre étude du réchaffement climatique, nous avons fait l'hypothèse que la hausse des températures est liée à la hausse des gaz à effet de serre dans l'atmosphère.")

    st.write("Nous avons tout d'abord utilisé les données du Climate & Energy College. Après traitement des données nous faisons apparaître 2 graphiques:")
    st.write("Le 1er graphique montre le taux de C02 globale dans l’atmosphère depuis l’année 0. Ce taux était assez stationnaire mais nous constatons que depuis le début du 19ème siècle et l’avènement de la révolution industrielle le taux a très fortement augmenté.")
    image_co2_1 = Image.open('streamlit/Graphs/C02 0 à 2020.png')
    st.image(image_co2_1)
    st.write("Le 2ème graphique est un zoom sur la période 1880 à 2014, période similaire à notre jeu de données sur les anomalies de températures.Ce graphique nous montre que le taux à très largement augmenté depuis 1880 avec une tendance haussière encore plus prononcée depuis 1960. Nous constatons également que le taux de CO2 est légèrement plus élevé dans le l’hémisphère Nord que dans le l’hémisphère Sud et que cet écart à tendance à augmenter au fil du temps.")
    image_co2_2 = Image.open('streamlit/Graphs/C02 1880 à 2020.png')
    st.image(image_co2_2)

    st.markdown("**Emissions de CO2 par pays**")

    # import Data CO2
    df_co2 = pd.read_csv("Datasets/co2_par_pays.csv")

    #Data processing
    df_co2 = df_co2.rename({'country':'Country', 'year':'Year'}, axis=1)
    df_temp_co2 = Temp_country_mean.merge(right = df_co2, on = ['Year', 'Country'], how = 'outer')
    st.write("Avec le sélecteur suivant, vous pouvez afficher les émissions de C02 par pays.")

    # Creation d'un selecteur mulit-pays pour afficher les courbes de températures
    countries = st.multiselect('Sélectionner un ou plusieurs pays', df_co2['Country'].unique())

    # Filter the data by selected countries
    df_countries_co2 = df_co2[df_co2['Country'].isin(countries)]

    # Create a line plot of temperature anomaly and CO2 for selected countries
    fig = go.Figure()
    for country in countries:
        df_co2_2 = df_countries_co2[df_countries_co2['Country'] == country]
        fig.add_trace(go.Scatter(x=df_co2_2['Year'], y=df_co2_2['co2'], name=f'{country}'))
    fig.update_layout(title='Emissions de CO2', xaxis_title='Année', yaxis_title='Emissions en millions de tonnes')
    st.plotly_chart(fig)

    st.markdown("**Carte interactive des émissions de CO2**")
    st.write("Avec la carte suivante, vous pouvez afficher les émissions de CO2 en sélectionnant l'année. En zoomant et en pointant sur un pays, vous pouvez avoir une vision précise des émissions à un instant T.")

    st.write("Choississez l'année pour voir les mesures:")

           ### Carte interactive émissions de C02 par pays en fonction de l'année

    # Create the slider for the year
    year_co2 = st.slider("Année", min_value=1880, max_value=2021, value=1880)

    # Filter the data by the selected year co2
    data_2 = df_co2[df_co2["Year"] == year_co2]

    # Create the figure
    fig = px.choropleth(data_2,
                        locations="Country",
                        locationmode="country names",
                        color="co2",
                        color_continuous_scale=px.colors.sequential.Plasma_r,
                        range_color=[0, 8000],
                        labels={"co2": "Emissions de CO2 en millions de tonnes"})

    # Update the layout
    fig.update_layout(title=f"Emissions de CO2 pour l'année {year_co2}", margin=dict(t=50, b=50),height=400)

    # Display the figure
    st.plotly_chart(fig)

    st.write("Le taux de C02 dans l’atmosphère suit les mêmes tendances que les anomalies de C02. Ce n’est qu’à partir de la fin du XIXème siècle que le taux de CO2 à commencer à fortement augmenté, porté par la Révolution Industrielle et le développement des pays industrialisés.Ces émissions de CO2 n’ont rien d'égal avec ce que la terre a connu depuis 2 millénaires. Nous allons donc nous pencher un peu plus sur la relation entre anomalies de températures et émissions de C02.")
    st.write("")
    st.write("")

    ### Notebook: Analyse CO2 par pays V2
    st.write("L’évolution des émissions de CO2 dans les grands pays industrialisés et les grands pôles d’activités n’est pas la même. Dans les pays d’Europe et les Etats-Unis, les émissions de CO2 ont globalement reculées, contrairement à la Chine ou l’Asie.")
    st.write("Pour les pays avec un taux de revenu élevé (High-income countries), les émissions sont plus importantes, mais depuis 2008 et le choc économique les émissions ont baissé. Pour les pays avec un plus faible taux de revenu (Upper-middle-income et lower-middle-income countries), les émissions étaient très faibles avant 1945 mais elles ont très fortement augmenté depuis, sans pour autant atteindre celle de l’autre groupe de pays. On constate une inflexion sur l'ensemble des courbes despays lors de la crise du COVID-19.")
    st.write("Si l'on observe l’évolution des émissions des membres de l’OCDE, celle-çi est très similaire à celle des pays ayant le plus de revenus. Elle suit une tendance haussière jusqu’en 2008 puis à tendance à baisser. Alors que la courbe des pays non-membres suit une tendance haussière exponentielle, portée principalement par les émissions de la Chine. A partir du début des années 2000, ceux ce sont désormais les pays non-membres de l’OCDE qui sont les plus émetteurs de CO2.")
    st.write("")

    ### Comparaison C02 et température
    st.subheader("Comparaison entre les anomalies de température et les émissions de C02")
    st.write("Dans ce graphique, nous superposons le taux de CO2 et les anomalies de température sur la période de 1880 à nos jours en mergeant ce dataset avec celui des températures globales de la Nasa (GLB.Ts+dSST). Nous constatons que les 2 courbes suivent la même tendance haussière avec la même inflexion vers le haut depuis le milieu du XXème siècle. Il y a donc de très fortes probabilités que ces 2 paramètres soient liés et que la hausse du taux de gaz à effet de serre dans l’atmosphère contribue à faire augmenter la température à la surface du globe. Nous décidons d’utiliser une méthode statistique pour montrer la corrélation entre la hausse des températures et l'augmentation des émissions de CO2. Nous avons donc effectué un test de corrélation de Pearson entre le taux de CO2 globale et les anomalies globales de température.")
    image_co2_3 = Image.open('streamlit/Graphs/C02+Temp 1880 à 2020.png')
    st.image(image_co2_3)
    st.write("Nous obtenons les résultats de corrélation suivants:")
    st.write("Coefficient de corrélation: 0.9276260482581129")
    st.write("P Value : 9.717225418853804e-59")
    st.write("Les résultats prouvent qu’il existe une forte corrélation positive et statistiquement significative  entre le taux de CO2 et les anomalies de température.")

    st.write("")
    st.markdown("**Anomalies de température et émissions de CO2 par pays**")

    st.write("Avec le graphique interactif suivant, vous pouvez comparer les anomalies de températures et les émissions de CO2 en sélectionnant le pays.")

    # Create the multi-select widget for the countries
    countries = st.multiselect("Sélectionner un ou plusieurs pays", df_temp_co2["Country"].unique())

    # Filter the data by the selected countries
    data_co2_temp = df_temp_co2[df_temp_co2["Country"].isin(countries)]

    chart_anomaly = (
        alt.Chart(data_co2_temp)
        .mark_line(color="red")
        .encode(
            x="Year:O",
            y=alt.Y("Anomaly:Q", scale=alt.Scale(domain=[-2, 4])),
            color=alt.Color("Country:N", legend=alt.Legend(title="Pays"))
        )
        .properties(width=300, height=350, title="Anomalies de températures")
    )

    chart_co2 = (
        alt.Chart(data_co2_temp)
        .mark_line(color="blue")
        .encode(
            x="Year:O",
            y=alt.Y("co2:Q", scale=alt.Scale(domain=[0,11000])),
            color=alt.Color("Country:N", legend=alt.Legend(title="Pays"))
        )
        .properties(width=300, height=350, title="Emissions de co2 en millions de tonnes")
    )

    # Display charts
    st.altair_chart(chart_anomaly | chart_co2)

    st.write("Cette étude des émissions de CO2 nous montre que les anomalies de température sont très fortement liées à la hausse des émissions de CO2 au niveau mondial. On constate que des pays ont fait des efforts pour réduire leurs émissions de CO2 mais la montée en puissance de la Chine et des pays en voie de développement a contrebalancé ce phénomène. La hausse des émissions est croissante et ce phénomène va sûrement provoquer une hausse de la température globale de la Terre.")