#import matplotlib.pyplot as plt
from fractions import gcd

import math
import numpy as np
import itertools
from itertools import product
from functools import reduce

import prime
import sys

def sol_41():
    max_number = 0

    def is_pandigital(n):
        compare_list = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
        if sorted(list(str(n))) == sorted(compare_list):
            return True
        return False

    def get_generator(skiplist):
        iterlist = ['1', '2', '3', '4', '5', '6', '7']#, '8']# , '9']
        for number in skiplist:
            del iterlist[iterlist.index(str(number))]
        return iterlist

    for a in get_generator([]):
        for b in get_generator([a]):
            for c in get_generator([a, b]):
                for d in get_generator([a, b, c]):
                    for e in get_generator([a, b, c, d]):
                        for f in get_generator([a, b, c, d, e]):
                            for g in get_generator([a, b, c, d, e, f]):
                                #for h in get_generator([a, b, c, d, e, f, g]):
                                    #for i in get_generator([a, b, c, d, e, f, g, h]):
                                number = int(''.join(list(map(str, [a, b, c, d, e, f, g]))))
                                #print(number)
                                if prime.is_prime(number):
                                    #print(number)
                                    if number > max_number:
                                        max_number = number

    # iterlist = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
    # iteration_indexes = []
    # for iteration_index in iterlist:
    #    new_iterlist = iterlist
    #    del new_iterlist[new_iterlist.index(iteration_index)]
    #    pass

    # for i in range(987654321, 0, -1):
    #    print(i)
    #    if prime.is_prime(i) and is_pandigital(i):
    #        return i
    return max_number

def sol_42():
    f = open('42.txt', 'r')

    alphabet_lookup = [chr(i) for i in range(ord('A'), ord('Z')+1)]
    triangle_lookup = [n*(n+1)/2 for n in range(1, 100)]

    count = 0
    for row in f:
        words = row.split(',')
        for word in words:
            word = word.strip('"')
            wordsum = 0
            for s in word:
                wordsum += alphabet_lookup.index(s)+1
            if wordsum in triangle_lookup:
                count += 1
    return count

def sol_43():
    def check_special_property():
        pass

    def get_generator(skiplist):
        iterlist = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
        for number in skiplist:
            del iterlist[iterlist.index(str(number))]
        return iterlist

    total_sum = 0
    for a in get_generator(['0']):
        for b in get_generator([a]):
            for c in get_generator([a, b]):
                for d in get_generator([a, b, c]):
                    for e in get_generator([a, b, c, d]):
                        for f in get_generator([a, b, c, d, e]):
                            for g in get_generator([a, b, c, d, e, f]):
                                for h in get_generator([a, b, c, d, e, f, g]):
                                    for i in get_generator([a, b, c, d, e, f, g, h]):
                                        for j in get_generator([a, b, c, d, e, f, g, h, i]):
                                            #number = int(''.join(list(map(str, [a, b, c, d, e, f, g, h, i, j]))))
                                            #print(number)
                                            number_list = [a, b, c, d, e, f, g, h, i, j]
                                            print(number_list)
                                            truth_value = True
                                            for x, prime in enumerate([2, 3, 5, 7, 11, 13, 17]):
                                                number = int(''.join(number_list[x+1:x+4]))
                                                print(number)
                                                if number % prime == 0:
                                                    truth_value = (truth_value and True)
                                                else:
                                                    truth_value = False
                                            if truth_value:
                                                total_sum += int(''.join(number_list))
                                                print(int(''.join(number_list)))
    return total_sum

def sol_44():
    def check_pentagonal(num):
        n = 1/6 + np.sqrt(1/36+2*num/3)
        n = int(round(n))
        if (3*n*n - n == 2*num):
            return True
        else:
            return False

    min_diff = 1e12
    for j in range(1, 10000):  # Note; w.l.o.g i<j
        for i in range(1, 10000):
            if i == j:
                continue
            a = int(j*(3*j-1)/2)
            b = int(i*(3*i-1)/2)
            diff = int(abs(a-b))
            s = int(a+b)
            #print(a)
            #print(b)
            #print(diff)
            #print(s)
            if check_pentagonal(s) and check_pentagonal(diff):
                #print(diff)
                if diff < min_diff:
                    min_diff = diff
    return min_diff

def sol_45():
    def check_pentagonal(num):
        n = 1/6 + np.sqrt(1/36+2*num/3)
        n = int(round(n))
        # print('pentagonal')
        # print(3*n**2-n)
        # print(2*num)
        if (3*n*n - n == 2*num):# < 1e-12:
            # print('hit')
            # print(n)
            # print(2*num)
            return True
        else:
            return False

    def check_hexagonal(num):
        n = 1/4 + np.sqrt(1/16+num/2)
        n = int(round(n))
        if (2*n**2 - n == num):# < 1e-12:
            #print('hit hexagonal')
            return True
        return False

    for i in range(2, 1000000):
        triangle_number = int(i*(i+1)/2)
        #print(triangle_number)
        if check_pentagonal(triangle_number) and check_hexagonal(triangle_number):
            #print(triangle_number)
            if triangle_number > 40755:
                return triangle_number

def sol_46():
    search_size = 100000
    numbers = np.ones(search_size)
    for i in range(0, int(search_size/2)):
        numbers[i*2] = 0
    numbers[1] = 0

    primes = prime.prime_sieve(search_size)

    for p in prime.prime_sieve(search_size):
        print(p)
        for square in range(0, int(round(np.sqrt((search_size-p)/2)))):
            index = p + 2*square*square
            if index > search_size:
                continue
            else:
                numbers[index] = 0
    print(np.where(numbers == 1)[0])

def sol_47():
    for i in range(1, 1000000):
        truth_factor = True
        for j in range(0, 4):
            if len(set(prime.get_prime_factors(i+j))) == 4:
                continue
            else:
                i = j+j
                truth_factor = False
                break
        if truth_factor:
            return i

def sol_48():
    #def exp_mod_10(number, exponent):
    #    for i in range(1, exponent+1):
    #        number = (number*number)%1e11

    res = 0
    for exponent in range(1, 1001):
        res += exponent**exponent
    return str(res)[-10::]

def sol_49():
    ans = []
    for i in range(1000, 10000-3330-3330):
        a = i
        b = i+3330
        c = i+6660

        if prime.is_prime(a) and prime.is_prime(b) and prime.is_prime(c):
            #print(set(list(str(a))))
            if (set(list(str(a))) == set(list(str(b)))) and (set(list(str(b))) == set(list(str(c)))):
                if a == 1487:
                    continue
                else:
                    ans = [a, b, c]
    ans = [str(x) for x in ans]
    return ''.join(ans)

def sol_50():
    primes = prime.prime_sieve(1000000)
    print(len(primes))

    longest_prime = 1
    prime_length = 1
    for i, _ in enumerate(primes):
        if i == len(primes)-1:
            break
        j = 1
        new_prime = sum(primes[i:i+j+1])
        while new_prime < 1000000:
            if prime.is_prime(new_prime) and (j > prime_length):
                print(j)
                longest_prime = new_prime
                prime_length = j
                print(prime_length)
                print(longest_prime)
                print(primes[i:i+j+1])
            else:
                j += 1
                new_prime = sum(primes[i:j+i+1])
    return longest_prime

def sol_51():
    def count_primes(number, indices):
        count = 0
        if 0 in indices:
            for z in range(1, 10):
                number_ = []
                for num in number:
                    number_.append(num)
                for index in indices:
                    number_[index] = str(z)
                new_number = int(''.join(number_))
                print(new_number)
                if prime.is_prime(new_number):
                    count += 1
        else:
            for z in range(0,10):
                number_ = []
                for num in number:
                    number_.append(num)
                for index in indices:
                    number_[index] = str(z)
                new_number = int(''.join(number_))
                print(new_number)
                if prime.is_prime(new_number):
                    count += 1
        return count

    for i in range(56003, 200000):
        number = list(str(i))
        for subset_length in [3]:
            #print(subset_length)
            for combs in itertools.combinations(range(0,len(number)), subset_length):
                #print(number)
                #print(combs)
                num_primes = count_primes(number, combs)
                #print(num_primes)
                if num_primes > 7:
                    print(number)
                    print(combs)
                    return number

def sol_52():
    def check_digits(numbers):
        truth_val = True
        start_num = numbers[0]
        for num in numbers:
            #print(set(list(str(num)))==set(list(str(start_num))))
            truth_val = (truth_val and (set(list(str(num)))==set(list(str(start_num)))))
            start_num = num
        return truth_val
    for i in range(1, 1000000):
        numbers = [j*i for j in range(1, 7)]
        if check_digits(numbers):
            return i

def sol_53():
    def num_combinations(n,r):
        return np.math.factorial(n)/(np.math.factorial(r)*np.math.factorial(n-r))
    count = 0
    for i in range(23, 101):
        for j in range(1, i):
            if num_combinations(i, j) > 1e6:
                count += 1
    return count

def sol_54():
    def tiebreaker(p1, p2, hand):
        ordering = ['2','3','4','5','6','7','8','9','T','J','Q','K','A']

        values_p1 = [q[0] for q in p1]
        values_p2 = [q[0] for q in p2]

        if hand == 'High card':
            print(sorted([ordering.index(z) for z in values_p1])[::-1] )
            for c1, c2 in zip(sorted([ordering.index(z) for z in values_p1])[::-1], sorted([ordering.index(z) for z in values_p2])[::-1]):
                if c1 > c2:
                    return True
                elif c1 < c2:
                    return False

        elif hand == 'Pair':
            highest_pair_p1 = 0
            highest_pair_p2 = 0

            cards_p1 = list(set(values_p1))
            cards_p2 = list(set(values_p2))
            print(cards_p1)
            print(cards_p2)
            for c1, c2 in zip(cards_p1, cards_p2):
                count_p1 = values_p1.count(c1)
                count_p2 = values_p2.count(c2)
                if count_p1 == 2:
                    highest_pair_p1 = ordering.index(c1)
                if count_p2 == 2:
                    highest_pair_p2 = ordering.index(c2)

            if highest_pair_p1 > highest_pair_p2:
                return True
            elif highest_pair_p1 < highest_pair_p2:
                return False

            print(p1)
            print(p2)
            print(highest_pair_p1)
            print(highest_pair_p2)
            return False
            #s = input('---')
            #return bool(s)

        else:
            input('Fanken!')
        #elif hand == 'Straight':
        #    pass

        #elif hand == 'Three of a kind':
        #    pass
        #elif hand == 'Four of a kind':
        #    pass
        #elif hand == 'Full house':
        #    pass
        #elif hand == 'Flush':
        #    pass

    def get_hand(p):
        suits = [q[1] for q in p]
        values = [q[0] for q in p]
        ordering = ['A','2','3','4','5','6','7','8','9','T','J','Q','K','A']

        if set(values) == set(['A','K', 'Q', 'J', 'T']):
            return 'Royal flush'

        indices = [ordering.index(v) for v in values]
        if sorted(indices) == list(range(min(indices),max(indices)+1)):
            if len(list(set(suits))) == 1:
                return 'Straight flush'
            return 'Straight'

        if len(list(set(suits))) == 1:
            return 'Flush'

        counts = []
        for unique_card in list(set(values)):
            counts.append(values.count(unique_card))

        #counts = set(counts)
        if set(counts)==set([4,1]):
            return 'Four of a kind'
        elif set(counts)==set([3,2]):
            return 'Full house'
        elif set(counts) == set([3,1,1]):
            return 'Three of a kind'
        elif set(counts) == set([2, 1]) and len(counts) == 4:
            return 'Pair'
        elif set(counts) == set([2, 1]) and len(counts) == 3:
            return 'Two pairs'

        return 'High card'

    def check_winner(p1, p2):
        hands = ['High card', 'Pair', 'Two pairs', 'Three of a kind', 'Straight', 'Flush', 'Full house', 'Four of a kind', 'Straight flush', 'Royal flush']
        hand_p1 = get_hand(p1)
        hand_p2 = get_hand(p2)

        if hands.index(hand_p1) > hands.index(hand_p2):
            print('P1 winning hand')
            print(hand_p1)
            print(hand_p2)
            return True
        elif hands.index(hand_p1) < hands.index(hand_p2):
            print('P2 winning hand')
            print(hand_p1)
            print(hand_p2)
            return False

        elif hands.index(hand_p1) == hands.index(hand_p2):
            res = tiebreaker(p1, p2, hand_p1)
            return res

    f = open('54.txt','r')
    count = 0
    for i,line in enumerate(f):
        l = line.strip('\n').split(' ')
        p1 = l[0:5]
        p2 = l[5:]
        if check_winner(p1, p2) == True:
            count += 1
    return count

def sol_55():
    def is_palindrome(number):
        return str(number) == str(number)[::-1]

    def is_lychrel(number):
        for i in range(1, 50):
            number = number + int(''.join(list(str(number))[::-1]))
            if is_palindrome(number):
                return False
        return True

    count = 0
    for i in range(0, 10000):
        if is_lychrel(i):
            count += 1
        else:
            print(i)
    return count

def sol_56():
    max_digit_sum = 0
    for a in range(1, 100):
        for b in range(1, 100):
            digit_sum = sum(list(map(int,str(a**b))))
            if digit_sum > max_digit_sum:
                max_digit_sum = digit_sum
    return max_digit_sum

from decimal import *

def sol_57():
    def recursive_sqrt_2(iterations):
        if iterations == 0:
            return 0
        else:
            return Decimal(1)/(Decimal(2)+recursive_sqrt_2(iterations-1))

    import sys
    import fractions
    getcontext().prec = 28

    sys.setrecursionlimit(5000)

    numerator = 1
    denominator = 2

    count = 0
    for i in range(1, 1001):
        cmp_numerator = numerator + denominator
        cmp_denominator = denominator

        if len(str(cmp_numerator)) > len(str(cmp_denominator)):
            count += 1

        n = numerator
        m = denominator
        numerator = m
        denominator = 2*m + n

        #i = Decimal(i)
        #approximation = 1+recursive_sqrt_2(i)
        #f = fractions.Fraction(approximation)
        #print(f)
        #print(str(f).split('/')[0])
        #print(list(str(f).split('/')[0]))
        #print(len(list(str(f).split('/')[0])))
        #print(len(list(str(f).split('/')[1])))
        #if len(list(str(f).split('/')[0])) > len(list(str(f).split('/')[1])):
        #    count += 1
        #print(approximation)
        #input()
    return count

def sol_58():
    def take_diagonal_lap(lap_number, index, diagonal_1, diagonal_2):
        side_length = 2*lap_number+1
        for increment in [side_length-1, 2*(side_length-1), 3*(side_length-1), 4*(side_length-1)]:
            new_number = index+increment
            if increment == side_length-1:
                diagonal_1.append(new_number)
            if increment == (side_length-1+side_length-1):
                diagonal_2.append(new_number)
            if increment == 3*(side_length-1):
                diagonal_1.append(new_number)
            if increment == 4*(side_length-1):
                diagonal_2.append(new_number)
        lap_number += 1
        return lap_number, index+increment, diagonal_1, diagonal_2

    index = 1
    lap_number = 1

    ratio = 1
    diagonal_1 = []
    diagonal_2 = []
    while ratio > 0.1:
        if lap_number == 1:
            ratio = 0
        lap_number, index, diagonal_1, diagonal_2 = take_diagonal_lap(lap_number, index, diagonal_1, diagonal_2)
        #print(diagonal_1)
        #print(diagonal_2)
        new_primes = sum([1 for i in diagonal_1[-2:] if prime.is_prime(i)]) + sum([1 for j in diagonal_2[-2:] if prime.is_prime(j)])
        ratio = (ratio*(2*len(diagonal_1)+1-4)+new_primes)/(2*len(diagonal_1)+1)
        #ratio = (sum([1 for i in diagonal_1 if prime.is_prime(i)]) + sum([1 for j in diagonal_2 if prime.is_prime(j)]))/(2*len((diagonal_1))+1)
        print(ratio)
        if lap_number < 10:
            print(lap_number)
            print(diagonal_1)
            print(diagonal_2)
            print(ratio)
            print((sum([1 for i in diagonal_1 if prime.is_prime(i)]) + sum([1 for j in diagonal_2 if prime.is_prime(j)]))/(2*len((diagonal_1))+1))
            input()
        if lap_number % 1000000 == 0:
            print(lap_number)
            print(ratio)
            print(diagonal_1)
        #i, j, index = take_lap(lap_number, index, i, j, spiral)
        #ratio = get_prime_ratio(spiral, lap_number)
        #lap_number += 1
        #if lap_number % 100 == 0:
        #    print(lap_number)
        #    print(ratio)
        #    input()
        #if lap_number > (size-1)/2:
        #    quit()

    return 2*lap_number-1

def sol_59():
    def xor(c1, c2):
        return int(c1) ^ int(c2)

    def decipher(msg, key):
        deciphered_text = []
        for i, char in enumerate(msg):
            deciphered_text.append(chr(int(xor(char, key[int(i%3)]))))
        return deciphered_text

    def is_cleartext(msg, words):
        msg_str = ''.join(msg)
        import re
        for word in words.split(' '):
            if (re.search(word,msg_str) is not None):
                return True
        return False

    def count_normal_letters(msg):
        allowed_range = []
        allowed_range.extend(list(range(ord('0'), ord('9')+1)))
        allowed_range.extend(list(range(ord('a'), ord('z')+1)))
        allowed_range.extend(list(range(ord('A'), ord('Z')+1)))
        allowed_range.extend([ord(' ')])
        allowed_range.extend([ord('.')])
        allowed_range.extend([ord(',')])
        allowed_range.extend([ord(':')])
        allowed_range.extend([ord('!')])
        allowed_range.extend([ord('?')])
        allowed_range.extend([ord('(')])
        allowed_range.extend([ord(')')])

        #allowed_range.extend([ord('+')])
        #print(len(allowed_range))
        res = 0
        for i, char in enumerate(msg):
            if ord(char) in allowed_range:
                res += 1

        return res

    def is_normal(msg):
        allowed_range = []
        allowed_range.extend(list(range(ord('0'), ord('9')+1)))
        allowed_range.extend(list(range(ord('a'), ord('z')+1)))
        allowed_range.extend(list(range(ord('A'), ord('Z')+1)))
        allowed_range.extend([ord(' ')])
        allowed_range.extend([ord('.')])
        print(len(allowed_range))
        for i, char in enumerate(msg):
            if ord(char) not in allowed_range:
                return False
        return True

    def is_numbers(msg):
        allowed_range = range(ord('0'), ord('9')+1)
        for i, char in enumerate(msg):
            if ord(char) not in allowed_range:
                return False
        return True

    with open('59.txt','r') as f:
        for line in f:
            ciphertext = []
            for letter in line.split(','):
                ciphertext.append(letter)

            #while True:
            #s = input('Words to search for: ')
            count = 0
            counts = {}
            eta_score = 0
            top_list = ['e', 't', 'a', 'o', 'i', 'n']
            for e1 in range(ord('a'),ord('z')+1):
                for e2 in range(ord('a'),ord('z')+1):
                    for e3 in range(ord('a'),ord('z')+1):
                        key = [e1,e2,e3]
                        deciphered_text = decipher(ciphertext, key)

                        unique_chars = list(set(deciphered_text))
                        unique_chars_count = []

                        #print(len(unique_chars))
                        #for uc in unique_chars:
                        #    unique_chars_count.append(deciphered_text.count(uc))

                        #if unique_chars[np.argmax(unique_chars_count)] == 'e':
                        #    print(''.join(deciphered_text))

                        nl = count_normal_letters(deciphered_text)
                        str_key = [chr(i) for i in key]
                        counts[''.join(str_key)] = nl

            top_keys = sorted(counts.items(), key=lambda v: v[1])[::-1][0:10]
            print(top_keys)
            for i, key in enumerate(top_keys):
                c = [ord(i) for i in key[0]]
                deciphered_text = decipher(ciphertext, c)
                print(''.join(deciphered_text))

            deciphered_text = decipher(ciphertext, [ord(i) for i in top_keys[0][0]])
            dec = [ord(d) for d in deciphered_text]
            return sum(dec)
                        #if nl > count:
                            #    count = nl
                            #    print(''.join(deciphered_text))

                            #if is_normal(deciphered_text):
                            #    print(''.join(deciphered_text))

                            #if is_numbers(deciphered_text):
                            #    print(''.join(deciphered_text))

                            #if is_cleartext(deciphered_text, s):
                            #    print(''.join(deciphered_text))

def sol_60():
    def dual_concat(p1, p2):
        comparison_1 = int((str(p1)+str(p2)))
        comparison_2 = int((str(p2)+str(p1)))
        if prime.is_prime(comparison_1) and prime.is_prime(comparison_2):
            return True
        return False

    def recursive_chain(chain, chain_number):
        if len(chain) < 2:
            return [], False

        print(chain)
        if chain_number == 4:
            return [chain[0],chain[1]], True

        base_prime = chain[0]
        print(base_prime)
        subchains = build_subchains(chain, 10)

        for subchain in subchains:
            new_primes, truth_value = recursive_chain(subchain, chain_number+1)
            if truth_value:
                return [base_prime, new_primes], True
        return [], False

    def build_subchains(chain, first_n):
        subchains = []
        for p in chain[1:first_n]:
            subchain = []
            subchain.append(p)
            for q in chain:
                if q <= p:
                    continue
                if dual_concat(p, q):
                    subchain.append(q)
            subchains.append(subchain)
        return subchains

    primes = prime.prime_sieve(35000)

    initial_subchains = build_subchains(primes, 50)
    for chain in initial_subchains:
        base_prime = chain[0]
        for chain_2 in build_subchains(chain, 50):
            base_prime_2 = chain_2[0]
            for chain_3 in build_subchains(chain_2, 50):
                base_prime_3 = chain_3[0]
                for chain_4 in build_subchains(chain_3, 40):
                    base_prime_4 = chain_4[0]
                    if len(chain_4) > 1:
                        print(base_prime)
                        print(base_prime_2)
                        print(base_prime_3)
                        print(base_prime_4)
                        print(chain_4)
                        print(base_prime+base_prime_2+base_prime_3+base_prime_4+chain_4[1])
                        input()
    #    returned_primes, truth_value = recursive_chain(chain, chain_number=1)
    #    if truth_value:
    #        print(returned_primes)
    #        input()
def sol_61():
    def overlap(m, n):
        if str(m)[2:] == str(n)[0:2]:
            return True
        return False

    def recursive_check(number, numbs, remaining_indices, starting_number):
        #print(remaining_indices)
        if remaining_indices == []:
            if overlap(number, starting_number):
                print(number)
                return True

        for i in remaining_indices:
            for m in numbs[i]:
                if overlap(number, m):
                    indices = [x for x in remaining_indices if not x==i]
                    flag = recursive_check(m, numbs, indices, starting_number)
                    if flag:
                        print(m)
                        return True
        return False

    def get_rings(numbs):
        indices = [0, 1, 2, 3, 4, 5]
        starting_type = numbs[5]
        del indices[5]
        for m in starting_type:
            flag = recursive_check(m, numbs, indices, m)
            if flag:
                print(m)
    def generate_numbers():
        res = [[],[],[],[],[],[]]
        k = [0 for x in res]
        p = 0
        while min([len(str(q)) for q in k]) < 5:
            k[0] = p*(p+1)/2
            k[1] = p*p
            k[2] = p*(3*p-1)/2
            k[3] = p*(2*p-1)
            k[4] = p*(5*p-3)/2
            k[5] = p*(3*p-2)
            k = [int(i) for i in k]
            for i, z in enumerate(k):
                if len(str(z))==4:
                    res[i].append(z)
            p += 1
        return res
    nums = generate_numbers()
    rings = get_rings(nums)
    print(rings)

def sol_69():
    m = 0
    n = 0
    for i in range(2, 1000000):
        factors = prime.get_prime_factors(i)
        prod = 1
        for p in list(set(factors)):
            prod = prod*(p)/(p-1)
        if prod > m:
            n = i
            m = prod
    print(m)
    print(n)

def sol_70():
    def is_permutation(tot, n):
        return sorted(str(tot))==sorted(str(n))

    minimum = 100
    min_n = 0
    for n in range(2, 10000000):
        factors = prime.get_prime_factors(n)
        tot = n
        for p in list(set(factors)):
            tot = tot*(p-1)/p
        tot = int(tot)

        if is_permutation(tot, n):
            if n/tot < minimum:
                minimum = n/tot
                min_n = n
    print(min_n)
    print(minimum)

def sol_72():
    def totient(d):
        factors = prime.get_prime_factors(d)
        res = d
        for p in list(set(factors)):
            res = res*(p-1)/p
        return int(res)

    count = 0
    for d in range(1, 1000001):
        #if d % 100000:
        #    print(d)
        #s    print(totient(d))
        count += totient(d)
        if d == 13:
            print(d)
            print(totient(d))
        if d == 1:
            print(d)
            print(totient(d))
    return count-1 # Remove 1/1

def sol_79():
    f = open('79.txt', 'r')
    res = []
    for line in f:
        res.append(line.strip('\n'))
    print(res)

def sol_71():
    results = []
    target = 3/7
    targets = []
    for d in range(1, 1000001):
        n = math.floor(d*3/7)
        if n== 0:
            continue
        targets.append([n/d, d, n])

    sorted_results = sorted(targets, key=lambda x: x[0])[::-1]
    for i in sorted_results:
        if i[0] < 3/7:
            print(i[1])
            return i[2]
    print(sorted_results[0:100])

def sol_73():
    def is_reducible(d, n):
        if gcd(d, n) == 1:
            return False
        else:
            return True

    def in_bounds(n, d, lower_bound, upper_bound):
        if n/d > lower_bound and n/d < upper_bound:
            return True

        if d%2==0 and n%2==0:
            return True
        elif d%3==0 and n%3==0:
            return True
        #elif d%5==0 and n%5==0:
        #    return True
        else:
            for i in range(3, int(d**0.5)+1, 2):
                if d % i == 0 and n % i == 0:
                    return True
        return False

    primes = prime.prime_sieve(12000)

    lower_bound = 1/3
    upper_bound = 1/2

    count = 0
    for d in range(1, 12001):
        print(d)

        start_index = int(d*lower_bound)
        end_index = int(d*upper_bound)+1

        res = []
        for n in range(start_index, end_index):
            if in_bounds(n, d, lower_bound, upper_bound) and not is_reducible(n, d):
                res.append([n, d])
                count += 1
        if d % 1000==0:
            print(d)

        start_index = math.floor(d*lower_bound)-1
        end_index = math.ceil(d*upper_bound)+1

        print(start_index)
        print(end_index)

        for n in range(start_index, end_index+1):
            if n/d > lower_bound and n/d < upper_bound:
                if not is_reducible(d, n):
                    count += 1
    return count

def sol_74():
    def factorial(i):
        res = 1
        while i > 0:
            res = res*i
            i -= 1
        return res

    def sum_factorial(i):
        res = 0
        for s in str(i):
            res += factorial(int(s))
        return int(res)

    def count_chain_length(i, chain_lengths):
        previous_numbers = []
        previous_numbers.append(i)
        hit = False

        length = 0
        while not hit:
            i = sum_factorial(i)
            length += 1
            if i in previous_numbers:
                hit = True
            previous_numbers.append(i)
        return length

    def recursive_count(n, memory, chain):
        new_number = sum_factorial(n)
        if new_number in chain:
            return 0
        chain.append(new_number)
        if new_number > memory.shape[0]:
            return 1 + recursive_count(new_number, memory, chain)
        else:
            if memory[new_number] == 0:
                length = 1 + recursive_count(new_number, memory, chain)
                memory[new_number] = length
                return length
            else:
                return int(memory[new_number])

    memory = np.zeros(1000000)
    for n in range(999999, 0, -1):
        print(n)
        len_n = 1+recursive_count(n, memory, [n])
        memory[n] = int(len_n)
        print(len_n)

    print(memory)
    return np.where(memory==60)[0].shape[0]

def sol_75():
    # a+b+c = L
    # a**2 + b**2 = c**2

    #Intractable
    #count = 0
    #for l in range(12, 1500001, 2):
    #    print(l)
    #    hits = 0
    #    for a in range(1, int(l/3)):
    #        c = int((2*a**2+l**2-2*a*l)/(2*l-2*a))
    #        b = l-a-c
    #        if a+b+c == l and a**2+b**2 == c**2:
    #            hits += 1
    #        if hits > 1:
    #            break
    #    if hits == 1:
    #        count += 1

    #counts = np.zeros(1500001)
    #for a in range(1, int(1500000/3)):
    #    print(a)
    #    for b in range(a, int((1500000-a)/2)):
    #        c = (a**2+b**2)**(1/2)
    #        if a**2 + b**2 == c**2 and (a+b+c)<1500000:
    #            counts[a+b+c] += 1

    counts = np.zeros(1500001)
    print('here')
    print(np.sqrt(1500000))
    print(1500000**(1/2))
    for m in range(1, int(np.sqrt(1500000))+1, 2):
        print(m)
        for n in range(1, m, 2):
            if math.gcd(m, n) == 1:
                primitive_a = m*n
                primitive_b = int((m**2-n**2)/2)
                primitive_c = int((m**2+n**2)/2)

                for k in range(1, int(1500000/(primitive_a+primitive_b+primitive_c)) + 1):
                    a = k*primitive_a
                    b = k*primitive_b
                    c = k*primitive_c
                    if a+b+c <=1500000 and (a**2+b**2)==c**2:
                        counts[a+b+c] += 1

    print(np.where(counts==1)[0][0:10])
    return np.where(counts==1)[0].shape[0]

def sol_76():
    # see solution of 78.
    return 190569291

def sol_77():
    def recursively_generate_sums(current_target, primes, prime_index, result, current_result):
        if prime_index == 0:
            current_result = []

        if prime_index == (len(primes)-1):
            for n in range(int(current_target/primes[prime_index])+1):
                next_current_result = [c for c in current_result]
                next_current_result.append(n)
                result.append(next_current_result)
            return

        for n in range(int(current_target/primes[prime_index])+1):
            next_current_result = [c for c in current_result]
            next_current_result.append(n)
            new_current_target = current_target - n*primes[prime_index]
            new_prime_index = prime_index + 1
            recursively_generate_sums(new_current_target, primes, new_prime_index, result, next_current_result)

    for target_value in range(66, 80):
        count = 0
        current_target = target_value
        primes = prime.prime_sieve(target_value)
        prime_index = 0

        result = []
        recursively_generate_sums(current_target, primes, prime_index, result, [])
        count = 0
        for res in result:
            s = 0
            for n, p in zip(res, primes):
                s += n*p
            if s == target_value:
                count += 1
        if count > 5000:
            return target_value

def sol_78():
    def count_partitions(n, memory):
        num_partitions = 0

        sign_counter = 1
        sign = 1

        i = 1
        pent = 1
        print(sign*pent)
        while (n - pent) > -1:
            num_partitions += sign*int(memory[n-pent])

            sign_counter = (sign_counter + 1) % 2
            if sign_counter == 0:
                i = -i
            else:
                i = -i
                i += 1

            pent = int((3*i**2-i)/2)
            sign = (-1)**(abs(i)-1)

        memory.append(num_partitions)
        return num_partitions

    memory = []
    memory.append(1)
    memory.append(1)
    for n in range(2, 100001):
        cp = count_partitions(n, memory)
        print(cp)
        if cp % 1000000 == 0:
            return n

def sol_79():
    f = open('79.txt','r')
    attempts = []
    for row in f:
        attempts.append(row.split('\n')[0])
    attempts = list(set(attempts))

    def get_valid_indices(result, s):
        resulting_indices = []
        for i, x in enumerate(result):
            if x == [] or x == s:
                resulting_indices.append(i)
        return resulting_indices

    def recursive_solve(result, attempts, i, solutions):
        if i == len(attempts):
            # print(len(result))
            # print('Hit!')
            # print(result)
            solutions.append(result)
            return True

        s = attempts[i]
        inx_1 = get_valid_indices(result, s[0])
        inx_2 = get_valid_indices(result, s[1])
        inx_3 = get_valid_indices(result, s[2])
        if len(inx_1) == 0:
            return False
        if len(inx_2) == 0:
            return False
        if len(inx_3) == 0:
            return False

        for x in inx_1:
            new_inx_2 = [inx for inx in inx_2 if (inx > x)]
            for y in new_inx_2:
                new_inx_3 = [inx for inx in inx_3 if inx > y]
                for z in new_inx_3:
                    new_result = [r for r in result]
                    new_result[x] = s[0]
                    new_result[y] = s[1]
                    new_result[z] = s[2]
                    # print(new_result)
                    flag = recursive_solve(new_result, attempts, i+1, solutions)
                    if flag:
                        return True
        return False

    for m in range(5, 20):
        result = [[] for n in range(0, m)]

        solutions = []
        flag = recursive_solve(result, attempts, 0, solutions)
        #print(m)
        #print(solutions)
        solutions = [[s for s in sol if s != []] for sol in solutions]
        solutions = [''.join(sol) for sol in solutions]
        #print(solutions)
        print(list(set(solutions)))
        input()
        if flag:
            pass

    return num_characters

def sol_80():
    from decimal import Decimal, getcontext
    getcontext().prec = 150

    s = 0
    for i in range(1, 101):
        if int((i**(1/2)))**2 == i:
            print(i)
            continue
        else:
            num = str(Decimal(i).sqrt())
            #print(len(num[2:102]))
            decimal_part = num[2:102]
            #print(sum(map(int, decimal_part)))
            x = num.split('.')
            y = [x[0]]
            y.extend(x[1])
            #print(y)
            #print(len(y))
            print(sum(map(int, y[0:100])))

            contrib = sum(map(int, y[0:100]))
            # print(len(str(num)))
            # print(str(num)[0:10])
            # print(str(num)[2:102])
            # print(len(str(num)[2:102]))

            s += contrib
            print(s)
    return s

def sol_81():
    # See Dijkstras algorithm

    def make_array(file):
        distances = np.zeros((80, 80))
        for i, row in enumerate(file):
            x = row.split(',')
            for j, s in enumerate(x):
                distances[i, j] = int(s)
        return distances

    def get_proximate_indices(i, j, size):
        proximate_indices = []
        if i == (size-1):
            if j == (size-1):
                pass
            else:
                proximate_indices.append([i, j+1])
        else:
            if j == (size-1):
                proximate_indices.append([i+1, j])
            else:
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j+1])

        return proximate_indices

    distances = make_array(open('81.txt', 'r'))
    min_distances = np.zeros(distances.shape)
    remembering_array = np.zeros(distances.shape)
    tentative_indices = []

    i, j = 0, 0
    min_distances[i, j] = distances[i, j]
    remembering_array[i, j] = 2

    min_distances[i+1, j] = distances[i, j] + distances[i+1, j]
    min_distances[i, j+1] = distances[i, j] + distances[i, j+1]
    remembering_array[i+1, j] = 1
    remembering_array[i, j+1] = 1

    tentative_indices.append([i+1, j, min_distances[i+1, j]])
    tentative_indices.append([i, j+1, min_distances[i, j+1]])

    print(tentative_indices)

    while len(tentative_indices) > 1:
        tentative = sorted(tentative_indices, key=lambda x: x[2])[0]
        i, j, _ = tentative
        print(i)
        print(j)
        proximate_indices = get_proximate_indices(i, j, distances.shape[0])

        for m, n in proximate_indices:
            print(m)
            print(n)
            if remembering_array[m, n] == 2:
                continue

            elif remembering_array[m, n] == 1:
                if tentative[2] + distances[m, n] < min_distances[i, j]:
                    min_distances[m, n] = tentative[2] + distances[m, n]

            else:
                min_distances[m, n] = tentative[2] + distances[m, n]
                remembering_array[m, n] = 1
                tentative_indices.append([m, n, min_distances[m, n]])

        del tentative_indices[tentative_indices.index(tentative)]
        remembering_array[i, j] = 2
    print(min_distances[-1, -1])

def sol_82():
    # See Dijkstras algorithm

    def make_array(file):
        distances = np.zeros((80, 80))
        for i, row in enumerate(file):
            x = row.split(',')
            for j, s in enumerate(x):
                distances[i, j] = int(s)
        return distances

    def get_proximate_indices(i, j, size):
        proximate_indices = []
        if i == (size-1):
            if j == (size-1):
                proximate_indices.append([i-1, j])
            else:
                proximate_indices.append([i, j+1])
                proximate_indices.append([i-1, j])
        elif i == 0:
            if j == (size-1):
                proximate_indices.append([i+1, j])
            else:
                proximate_indices.append([i, j+1])
                proximate_indices.append([i+1, j])
        else:
            if j == (size-1):
                proximate_indices.append([i+1, j])
                proximate_indices.append([i-1, j])
            else:
                proximate_indices.append([i, j+1])
                proximate_indices.append([i+1, j])
                proximate_indices.append([i-1, j])

        return proximate_indices

    distances = make_array(open('82.txt', 'r'))

    minimal_sum = 1000000

    for starting_point in range(0, distances.shape[0]):
        print(starting_point)

        min_distances = np.zeros(distances.shape)
        remembering_array = np.zeros(distances.shape)
        tentative_indices = []

        i, j = starting_point, 0
        min_distances[i, j] = distances[i, j]
        remembering_array[i, j] = 2

        if starting_point == 0:
            print('here....')
            min_distances[i+1, j] = distances[i, j] + distances[i+1, j]
            min_distances[i, j+1] = distances[i, j] + distances[i, j+1]

            remembering_array[i+1, j] = 1
            remembering_array[i, j+1] = 1

            tentative_indices.append([i+1, j, min_distances[i+1, j]])
            tentative_indices.append([i, j+1, min_distances[i, j+1]])

        elif starting_point == (distances.shape[0]-1):
            print('here!')
            min_distances[i-1, j] = distances[i, j] + distances[i-1, j]
            min_distances[i, j+1] = distances[i, j] + distances[i, j+1]

            remembering_array[i-1, j] = 1
            remembering_array[i, j+1] = 1

            tentative_indices.append([i, j+1, min_distances[i, j+1]])
            tentative_indices.append([i-1, j, min_distances[i-1, j]])
            print(tentative_indices)
            input()

        else:
            min_distances[i-1, j] = distances[i, j] + distances[i-1, j]
            min_distances[i+1, j] = distances[i, j] + distances[i+1, j]
            min_distances[i, j+1] = distances[i, j] + distances[i, j+1]

            remembering_array[i-1, j] = 1
            remembering_array[i+1, j] = 1
            remembering_array[i, j+1] = 1

            tentative_indices.append([i+1, j, min_distances[i+1, j]])
            tentative_indices.append([i, j+1, min_distances[i, j+1]])
            tentative_indices.append([i-1, j, min_distances[i-1, j]])

        print(tentative_indices)

        while len(tentative_indices) > 1:
            tentative = sorted(tentative_indices, key=lambda x: x[2])[0]
            i, j, _ = tentative

            if starting_point ==(distances.shape[0]-1):
                print(min_distances)

            #print(i)
            #print(j)
            proximate_indices = get_proximate_indices(i, j, distances.shape[0])

            for m, n in proximate_indices:
                if remembering_array[m, n] == 2:
                    continue

                elif remembering_array[m, n] == 1:
                    if tentative[2] + distances[m, n] < min_distances[i, j]:
                        min_distances[m, n] = tentative[2] + distances[m, n]

                else:
                    min_distances[m, n] = tentative[2] + distances[m, n]
                    remembering_array[m, n] = 1
                    tentative_indices.append([m, n, min_distances[m, n]])

            del tentative_indices[tentative_indices.index(tentative)]
            remembering_array[i, j] = 2

        print(min_distances.shape)
        minimum_distance = min(min_distances[:, -1])

        print(minimum_distance)
        if minimum_distance < minimal_sum:
            minimal_sum = minimum_distance

    return minimal_sum

def sol_83():
    # See Dijkstras algorithm

    def make_array(file):
        distances = np.zeros((80, 80))
        for i, row in enumerate(file):
            x = row.split(',')
            for j, s in enumerate(x):
                distances[i, j] = int(s)
        return distances

    def get_proximate_indices(i, j, size):
        proximate_indices = []
        if i == 0:
            if j == 0:
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j+1])
            elif j == (size-1):
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j-1])
            else:
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j-1])
                proximate_indices.append([i, j+1])

        elif i == (size-1):
            if j == 0:
                proximate_indices.append([i-1, j])
                proximate_indices.append([i, j+1])
            elif j == (size-1):
                proximate_indices.append([i-1, j])
                proximate_indices.append([i, j-1])
            else:
                proximate_indices.append([i-1, j])
                proximate_indices.append([i, j-1])
                proximate_indices.append([i, j+1])
        else:
            if j == 0:
                proximate_indices.append([i-1, j])
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j+1])
            elif j == (size-1):
                proximate_indices.append([i-1, j])
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j-1])
            else:
                proximate_indices.append([i-1, j])
                proximate_indices.append([i+1, j])
                proximate_indices.append([i, j-1])
                proximate_indices.append([i, j+1])
        return proximate_indices

    distances = make_array(open('83.txt', 'r'))

    min_distances = np.zeros(distances.shape)
    remembering_array = np.zeros(distances.shape)
    tentative_indices = []

    i, j = 0, 0
    min_distances[i, j] = distances[i, j]
    remembering_array[i, j] = 2

    min_distances[i+1, j] = distances[i, j] + distances[i+1, j]
    min_distances[i, j+1] = distances[i, j] + distances[i, j+1]
    remembering_array[i+1, j] = 1
    remembering_array[i, j+1] = 1

    tentative_indices.append([i+1, j, min_distances[i+1, j]])
    tentative_indices.append([i, j+1, min_distances[i, j+1]])

    print(tentative_indices)

    while len(tentative_indices) > 1:
        tentative = sorted(tentative_indices, key=lambda x: x[2])[0]
        i, j, _ = tentative
        print(i)
        print(j)
        proximate_indices = get_proximate_indices(i, j, distances.shape[0])

        for m, n in proximate_indices:
            print(m)
            print(n)
            if remembering_array[m, n] == 2:
                continue

            elif remembering_array[m, n] == 1:
                if tentative[2] + distances[m, n] < min_distances[i, j]:
                    min_distances[m, n] = tentative[2] + distances[m, n]

            else:
                min_distances[m, n] = tentative[2] + distances[m, n]
                remembering_array[m, n] = 1
                tentative_indices.append([m, n, min_distances[m, n]])

        del tentative_indices[tentative_indices.index(tentative)]
        remembering_array[i, j] = 2
    print(min_distances[-1, -1])

def sol_84():
    board = ['GO', 'A1', 'CC1', 'A2', 'T1', 'R1', 'B1', 'CH1', 'B2', 'B3',
             'JAIL', 'C1', 'U1', 'C2', 'C3', 'R2', 'D1', 'CC2', 'D2', 'D3',
             'FP', 'E1', 'CH2', 'E2', 'E3', 'R3', 'F1', 'F2', 'U2', 'F3',
             'G2J', 'G1', 'G2', 'CC3', 'G3', 'R4', 'CH3', 'H1', 'T2', 'H2']
    board_len = len(board)

    cc_counter = 0
    ch_counter = 0
    double_count = 0

    counts = [0 for b in board]

    position = 0
    counts[position] += 1

    for roll in range(0, 10000000):
        r1 = np.random.randint(1, 5)
        r2 = np.random.randint(1, 5)
        if r1 == r2:
            double_count += 1
            if double_count == 3:
                position == board.index('JAIL')
                double_count = 0
                continue

        r = r1+r2
        position = (position + r) % board_len

        if position == board.index('CC2') or position == board.index('CC1')  or position==board.index('CC3'):
            if cc_counter == 1:
                position = board.index('JAIL')
            elif cc_counter == 2:
                position = board.index('GO')
            cc_counter = (cc_counter +1)% 16

        elif position == board.index('CH1') or position == board.index('CH2') or position == board.index('CH3'):
            if ch_counter == 1:
                position = board.index('GO')
            elif ch_counter == 2:
                position = board.index('JAIL')
            elif ch_counter == 3:
                position = board.index('C1')
            elif ch_counter == 4:
                position = board.index('E3')
            elif ch_counter == 5:
                position = board.index('H2')
            elif ch_counter == 6:
                position = board.index('R1')
            elif ch_counter == 7:
                if position == board.index('CH1'):
                    position = board.index('R2')
                elif position == board.index('CH2'):
                    position = board.index('R3')
                elif position == board.index('CH3'):
                    position = board.index('R1')
            elif ch_counter == 8:
                if position == board.index('CH1'):
                    position = board.index('R2')
                elif position == board.index('CH2'):
                    position = board.index('R3')
                elif position == board.index('CH3'):
                    position = board.index('R1')
            elif ch_counter == 9:
                if position == board.index('CH1'):
                    position = board.index('U1')
                elif position == board.index('CH2'):
                    position = board.index('U2')
                elif position == board.index('CH3'):
                    position = board.index('U1')
            elif ch_counter == 10:
                position = position - 3
            ch_counter = (ch_counter + 1)%16

        elif position == board.index('G2J'):
            position = board.index('JAIL')

        counts[position] += 1
    print(counts)
    max_indices = np.argpartition(counts, -4)[-4::]
    print(max_indices)
    print([counts[m] for m in max_indices])
    print([board[m] for m in max_indices])

def sol_85():
    least = 100
    best_m, best_n = 0, 0
    for m in range(1, 1000):
        for n in range(1, m+1):
            print(m)
            print(n)
            count = m*n*(m*n+m+n+1) + (m+1)*(n+1)*m*n/4 - m*m*(n+1)*n/2 - n*n*(m+1)*m/2 - (n+1)*n*m/2 - (m+1)*m*n/2
            print(count)
            proximity = 2000000-count
            if abs(proximity) < least:
                least = abs(proximity)
                best_m = m
                best_n = n

    return best_m*best_n

def sol_86():
    count = 0
    a = 1
    while count < 1000000:
        print(a)
        for b in range(1, a+1):
            for c in range(1, b+1):
                l = np.sqrt(a*a+(b+c)**2)
                #l = get_shortest_path(a, b, c)
                if l == int(l):
                    count += 1
                continue
        a += 1
        print(count)
    print(a)
    print(count)

def sol_87():
    limit = 50000000

    primes = prime.prime_sieve(limit)
    numbers = np.zeros(limit+1)

    primes_1 = [p for p in primes if p < (limit)**(1/2)]
    primes_2 = [p for p in primes if p < (limit)**(1/3)]
    primes_3 = [p for p in primes if p < (limit)**(1/4)]

    for p1 in primes_1:
        for p2 in primes_2:
            for p3 in primes_3:
                p = p1**2 + p2**3 + p3**4

                if p > limit:
                    continue
                else:
                    print(p)
                    numbers[int(p)] = 1
                print(p)

    return len((np.where(numbers==1))[0])

def sol_88():
    def partition(collection):
        if len(collection) == 1:
            yield [ collection ]
            return

        first = collection[0]
        for smaller in partition(collection[1:]):
            # insert `first` in each of the subpartition's subsets
            for n, subset in enumerate(smaller):
                yield smaller[:n] + [[ first ] + subset]  + smaller[n+1:]
            # put `first` in its own subset
            yield [ [ first ] ] + smaller

    minimal_product_sum_numbers = [2*n for n in range(0, 12001)]
    print(minimal_product_sum_numbers[2:20])
    input()

    for product in range(2, 24002):
        factors = prime.get_prime_factors(product)
        print(product)

        for part in partition(factors):
            s = 0
            p = product
            for factor in part:
                s += reduce(lambda x,y: x*y, factor, 1)

            if p == s:
                l = len(part)
            elif s < p:
                n = p-s
                l = len(part) + n
            else:
                continue

            if l < 12001 and p < minimal_product_sum_numbers[l]:
                minimal_product_sum_numbers[l] = p

    print(minimal_product_sum_numbers[2:20])
    return sum(list(set(minimal_product_sum_numbers[2:12001])))

def sol_89():
    def get_dict():
        roman_to_int = {
            'I': 1,
            'V': 5,
            'X': 10,
            'L': 50,
            'C': 100,
            'D': 500,
            'M': 1000
        }
        return roman_to_int

    def represent(number):
        if number >= 1000:
            return 'M' + represent(number-1000)
        elif number >= 900:
            return 'CM' + represent(number-900)
        #elif number >= 800:
        #    return 'CCM' + represent(number-800) ##
        elif number >= 500:
            return 'D' + represent(number-500)
        elif number >= 400:
            return 'CD' + represent(number-400)
        #elif number >= 300:
        #    return 'CCD' + represent(number-300) ##
        elif number >= 100:
            return 'C' + represent(number-100)
        elif number >= 90:
            return 'XC' + represent(number-90)
        #elif number >= 80:
        #    return 'XXC' + represent(number-80) #
        elif number >= 50:
            return 'L' + represent(number-50)
        elif number >= 40:
            return 'XL' + represent(number-40)
        elif number >= 10:
            return 'X' + represent(number-10)
        elif number >= 9:
            return 'IX' + represent(number-9)
        #elif number >= 8:
        #    return 'IIX' + represent(number-8)
        elif number >= 5:
            return 'V' + represent(number-5)
        elif number >= 4:
            return 'IV' + represent(number-4)
        elif number >= 1:
            return 'I' + represent(number-1)
        else:
            return ''

    def parse_roman(roman):
        roman_to_int = get_dict()
        s = roman[::-1]

        value = 0
        current_value = 0
        current_token = None
        for token in s:
            if not current_token == None:
                if roman_to_int[token] < current_token:
                    current_value -= roman_to_int[token]
                elif roman_to_int[token] == current_token:
                    current_value += current_token
                else:
                    value += current_value
                    current_token = roman_to_int[token]
                    current_value = current_token
                continue
            current_token = roman_to_int[token]
            current_value += current_token
        value += current_value
        return value

    saving = 0
    f = open('89.txt', 'r')
    for line in f:
        numeral = line.split('\n')[0]
        print(numeral)
        print(parse_roman(numeral))
        shortest_roman = represent(parse_roman(numeral))
        print(shortest_roman)
        saving += (len(numeral) - len(shortest_roman))
    return saving

def sol_90():
    def valid(dice_1, dice_2, squares):
        dice_1 = list(dice_1)
        dice_2 = list(dice_2)
        if '6' in dice_1:
            dice_1.append('9')
        elif '9' in dice_1:
            dice_1.append('6')
        if '6' in dice_2:
            dice_2.append('9')
        elif '9' in dice_2:
            dice_2.append('6')

        for s in squares:
            if s[0] in dice_1:
                if s[1] in dice_2:
                    continue
                elif s[1] in dice_1:
                    if s[0] in dice_2:
                        continue
                    else:
                        return False
                else:
                    return False

            elif s[1] in dice_1:
                if s[0] in dice_2:
                    continue
                else:
                    return False
            else:
                return False

        return True

    square_numbers = []
    sq = [str(s**2) for s in range(1, 10)]
    for s in sq:
        if len(s) == 1:
            square_numbers.append('0'+s)
        else:
            square_numbers.append(s)

    count = 0
    numbers = [str(a) for a in range(0, 10)]
    for dice_1 in itertools.combinations(numbers, 6):
        for dice_2 in itertools.combinations(numbers, 6):
            #if '6' in dice_1 and '9' in dice_1:
            #    continue
            #elif '6' in dice_2 and '9' in dice_2:
            #    continue
            print(square_numbers)
            if valid(dice_1, dice_2, square_numbers):
                count += 1
    return count/2

def sol_91():
    def vis_triangle(x1, y1, x2, y2):
        pass

    def check_triangle(x1, y1, x2, y2):
        l1 = x1**2 + y1**2
        l2 = x2**2 + y2**2
        l3 = (x2-x1)**2+(y2-y1)**2
        if l1 + l2 == l3 or l2 + l3 == l1 or l3 + l1 == l2:
            return True
        return False

    num_triangles = 0

    limit = 50
    x1 = 0
    for y1 in range(1, limit+1):
        for x2 in range(1, limit+1):
            for y2 in range(0, limit+1):
                if check_triangle(x1, y1, x2, y2):
                    num_triangles += 1

    for x1 in range(1, limit+1):
        for y1 in range(1, limit+1):
            for x2 in range(1, limit+1):
                for y2 in range(0, int(math.floor(y1*x2/x1))):
                    if check_triangle(x1, y1, x2, y2):
                        num_triangles += 1

    return num_triangles

def sol_92():
    def recursive_chain(i, arrival):
        if i > (len(arrival)-1):
            i = sum([int(x)**2 for x in map(int, str(i))])
            res = recursive_chain(i, arrival)
            return res
        if arrival[i] == 1 or i==1:
            return 1
        elif arrival[i] == 89 or i==89:
            return 89
        else:
            i = sum([int(x)**2 for x in map(int, str(i))])
            res = recursive_chain(i, arrival)
            arrival[i] = res
            return res

    sys.setrecursionlimit(5000)

    arrival = np.zeros(10000000)
    for i in range(1, 10000000):
        print(i)
        arrival[i] = recursive_chain(i, arrival)

    return len(np.where(arrival==89)[0])

def sol_93():
    def get_combination_length(result):
        for i in range(1, len(result)):
            if i in result:
                continue
            else:
                break
        return i-1

    def recursive_generator(iterlist, results):
        if len(iterlist) == 2:
            for x1, x2 in itertools.permutations(iterlist, 2):
                #print([x1, x2])
                results.append(x1+x2)
                results.append(x1-x2)
                results.append(x1*x2)
                if x2 != 0:
                    results.append(x1/x2)
        else:
            for x1, x2 in itertools.permutations(iterlist, 2):
                new_iterlist = [i for i in iterlist]
                new_iterlist.remove(x1)
                new_iterlist.remove(x2)

                iterlists = []

                iterlist_ = [x for x in new_iterlist]
                iterlist_.append(x1+x2)
                iterlists.append(iterlist_)

                iterlist_ = [x for x in new_iterlist]
                iterlist_.append(x1-x2)
                iterlists.append(iterlist_)

                iterlist_ = [x for x in new_iterlist]
                iterlist_.append(x1*x2)
                iterlists.append(iterlist_)
                if x2 != 0:
                    iterlist_ = [x for x in new_iterlist]
                    iterlist_.append(x1/x2)
                    iterlists.append(iterlist_)

                # print([x1, x2])
                # print(iterlists)
                # input()
                for iterlist_ in iterlists:
                    recursive_generator(iterlist_, results)

    max_len = 0
    limit = 20
    best_combo = []

    for a in range(1, limit+1):
        for b in range(a+1, limit+1):
            for c in range(b+1, limit+1):
                for d in range(c+1, limit+1):
                    #generate_combinations(a, b, c, d)
                    #print([a, b, c, d])
                    x = []
                    recursive_generator([a, b, c, d], x)
                    #print(set(x))
                    l = get_combination_length(x)
                    #print(l)
                    #input()
                    if l > max_len:
                        max_len = l
                        best_combo = [a, b, c, d]
                        print(best_combo)
                        print(max_len)
    print(best_combo)
    return max_len

def sol_94():
    def check_triangle(n, sign):
        if sign=='+':
            s = np.sqrt((3/4)*n*n+2*n+1)
        else:
            s = np.sqrt((3/4)*n*n-2*n+1)
        if ((n/2)*s).is_integer():
            return True
        else:
            return False

    import equation_utils
    from equation_utils import solve_diophantine, solve_pell

    true_sols = []
    base_sol = solve_pell(3)
    sols = equation_utils.generate_solutions_from_minimal_solution(base_sol[0], base_sol[1], 3, 50)

    positive_perimeters = []
    negative_perimeters = []

    for x, y in sols:
        a1 = (2*x+1)//2
        a2 = (2*x-1)//2
        A1 = (x+2)*y//3
        A2 = (x-2)*y//3
        if a1 > 300000000:
            continue
        if y<= 0:
            continue
        if isinstance(a1, int) and isinstance(A1, int) and A1>0:
            negative_perimeters.append(a1)
        if isinstance(a2, int) and isinstance(A2, int) and A2>0:
            positive_perimeters.append(a2)

    perimeter_sum = 0
    for p in positive_perimeters:
        if 3*p - 1 > 1e9:
            continue
        else:
            print(p)
            perimeter_sum += 3*p - 1
    for p in negative_perimeters:
        if 3*p + 1 > 1e9:
            continue
        else:
            print(p)
            perimeter_sum += 3*p + 1
    return 518408346
    #num_triangles = 0
    #for i in range(1, 1e9+1:
    #    s = i*i
    #    r = np.sqrt(16/9 + (4/3)*s*s-4/3)
    #    n1 = -4/3 + r
    #    n2 = 4/3 + r
    #    if (n1/2)*np.sqrt(3/4*n1*n1 + 2*n1 + 1).is_integer():
    #        num_triangles += 1
    #        if 3*n1 + 2 > 1e9:
    #            num_triangles -= 1
    #    if(n2/2)*np.sqrt(3/4*n2*n2 - 2*n2 + 1).is_integer():
    #        num_triangles += 1
    #        if 3*n2 - 2 > 1e9:
    #            num_triangles -= 1
    #return num_triangles
    def count_positive():
        count = 0
        for s in range(1, int(np.sqrt(1e9))+1):
            n = (8*s*s - 2)/3
            print(n)
            #input()
            if n.is_integer():
                print(n)
                #input()
                r = int(round(np.sqrt((n+2)/2)))
                #print(n)
                #input()
                if 4*r*r - 2 == n:
                    area = n/4*(3*n+2)*(n+2)
                    count += area
                    if 3*n + 2 > 1e9:
                        count -= area
        return count

    def count_negative():
        count = 0
        for s in range(0, int(np.sqrt(1e9))+1):
            n = (8*s*s + 2)/3
            print(n)
            input()
            #input()
            if n.is_integer():
                print(n)
                r = int(round(np.sqrt((n-2)/2)))
                if 4*r*r + 2 == n:
                    area = n/4*(3*n-2)*(n-2)
                    count += area
                    if 3*n - 2 > 1e9:
                        count -= area
        return count

    def brute_forcer():
        result = []
        for n in range(2, int((1e9-2)/3) + 1):
            r1 = int(round(np.sqrt(3*n*n+8*n+4)))
            if r1**2 == 3*n*n+8*n+4:
                if (n*r1/4).is_integer():
                    print(n)
                    print('positive')
                    result.append([n, 'positive'])

            r2 = int(round(np.sqrt(3*n*n-8*n+4)))
            if r2**2 == 3*n*n-8*n+4:
                if (n*r2/4).is_integer():
                    print(n)
                    print('negative')
                    result.append([n, 'negative'])
            if n % 10000000 == 0:
                print(n)
    #result = brute_forcer()
    #area_sum = 0
    #backup_sum = 0
    #for res in result:
    #    if res[1] == 'positive':
    #        area_sum += 3*res[0]+2
    #        backup_sum += 3*res[0]-2
    #    else:
    #        area_sum += 3*res[0]-2
    #        backup_sum += 3*res[0]+2
    #print(backup_sum)
    #return area_sum

    area_sum = 0
    area_sum += count_positive()
    area_sum += count_negative()
    return area_sum

    for s in range(1, int(np.sqrt(1e9+1))):
        n1 = (2*s*s - 2)/3
        n2 = (2*s*s + 2)/3

        if np.sqrt((n1+2)/2).is_integer():
            num_triangles += 1
            print(n1)
            if 3*n1 + 2 > 1e9:
                num_triangles -= 1
        if n2/2 == 0:
            if np.sqrt((n-2)/2).is_integer():
                num_triangles += 1
                if 3*n1 + 2 > 1e9:
                    num_triangles -= 1
    return num_triangles
    num_triangles = 0

    for n in range(2, int((1e9-2)/3) + 1):
        if check_triangle(n, '+'):
            num_triangles += 1
        if check_triangle(n, '-'):
            num_triangles += 1

            #print('Double detected!')
        if n % 10000000 == 0:
            print(n)
    print(num_triangles)
    return num_triangles

def sol_95():
    pass

def sol_96():
    pass

def sol_97():
    last_10 = 1
    for i in range(0, 7830457):
        last_10 = last_10*2 % 100000000000

    last_10 = 28433*last_10+1
    last_10 = str(last_10)[-10:]
    print(len(last_10))
    print(last_10)

def sol_99():
    import functools
    def exp_compare(n, m):
        b1 = n[0]
        e1 = n[1]
        b2 = m[0]
        e2 = m[1]
        if b1**(e1/e2) > b2:
            print('ok')
            return 1
        else:
            return -1

    f = open('99.txt', 'r')
    exponentials = []
    for i, line in enumerate(f):
        exponentials.append([float(line.split(',')[0]),float(line.split(',')[1]), i])

    m = max(exponentials, key=functools.cmp_to_key(exp_compare))
    print(m)
    print(len(exponentials))
    print(exponentials[0])
    #exponentials.append(float(line.split(',')[0])**float(line.split(',')[1]))
    # print np.argmax(exponentials)

def sol_100():
    #limit = 21
    #for b in range(1, limit):
    #    print(b)
    #    t = 1/2 + np.sqrt(2*b*b - 2*b + 1/4)

    #    t = int(t)
    #    if b**2-b == (t**2-t)/2:
    #        return b

    ### Init b0 ###
    b0 = 1/2 + np.sqrt(1/2-1/8 +(10**12-1/2)**2/2)
    print(b0)
    b = int(b0)
    s0 = int(np.sqrt(2*b*b-2*b+1/4))
    s = s0
    while True:
        print(b)
        b = 1/2 + np.sqrt(1/4-1/8+s*s/2)
        b = int(b)
        if b*b-b+1/8 == s*s/2:
            return b

        print(s)
        print(b)

        s += 1
        #t = 1/2 + np.sqrt(2*b*b - 2*b + 1/4)

        #t = int(t)
        #if b**2-b == (t**2-t)/2:
        #    return b
        #else:
        #    b += 1
        #print(t)


import os
import unittest

import torch
import torch.distributed as dist
from torch.multiprocessing import Process
import torch.nn as nn

from machina.optims import DistributedSGD

def init_processes(rank, world_size,
                   function, backend='tcp'):
    os.environ['MASTER_ADDR'] = '127.0.0.1'
    os.environ['MASTER_PORT'] = '29500'
    dist.init_process_group(backend, rank=rank,
                            world_size=world_size)
    function(rank, world_size)

class TestDistributedSGD(unittest.TestCase):

    def test_step(self):

        def _run(rank, world_size):
            model = nn.Linear(10, 1)
            optimizer = DistributedSGD(
                model.parameters())

            optimizer.zero_grad()
            loss = model(torch.ones(10).float())
            loss.backward()
            optimizer.step()

        processes = []
        world_size = 4
        for rank in range(world_size):
            p = Process(target=init_processes,
                        args=(rank,
                              world_size,
                              _run))
            p.start()
            processes.append(p)
        for p in processes:
            p.join()


from ControlParameters import *
from CommandIndex import *
from collections import deque

# =============================================================================
# === Dequeue and ML Functions ================================================
# =============================================================================

def extractQueue(queue):
    modelInput = []
    for moment in queue:
        modelInput += moment
    return(modelInput)

def feedModel(modelInput):
    # feeds list of features to model and returns decision
    return()

def outputUI(result):
    # sort provide ordered UI list to model
    return()

def extractOrderlist(queue):
    modelInput = extractQueue(queue)
    result = feedModel(modelInput)
    outputUI(result)
    return()

# =============================================================================
# === Read ExoDataScript ======================================================
# =============================================================================

EXODATA_RATE = 100      # seconds to discrete time = 100 samples a second
FIRST_DELAY = 10        # seconds: first exit transition state delay
STATIONARY_DELAY = 5    # seconds: loop in stationary intent  delay
#TRANSITION_DELAY = 3   # if we need to optimise computation

tick = 0
firstDelay = (10 - RECORD_TIME)*EXODATA_RATE
stationaryDelay = (5 - RECORD_TIME)*EXODATA_RATE

MAX_Q_LENGTH = RECORD_TIME*100*SAMPLE_RATE/100
queue = deque(maxlen=MAX_Q_LENGTH)
exoReading = [0,0,0]
inStationary = False

while(True):
    # queue automatically pops data
    # investigate how to do sampling (i.e. ignore certain features)
    queue.appendleft(exoReading)

    # left transition state
    if ~inStationary and exoReading[ID_CURRSTATE] in STATIONARY_STATES:
        extractOrderlist(queue)
        inStationary = True
        tick = 0
        while tick < FIRST_DELAY:
            tick += 1

    # loop readings in stationary
    if inStationary:
        tick +=1
        if tick >= STATIONARY_DELAY:
            extractOrderlist(queue)
            tick = 0

    if exoReading[ID_CURRSTATE] in TRANSITION_STATES:
        inStationary = False


#!/usr/bin/env python3.7

"""
    could be expanded
    and robustified to include
    more connection options
    such as https
"""

import argparse
from io import BytesIO
import pycurl

def pycurl_get():

    """
        just a simple pycurl get
        to aide in troubleshooting
        basic RDS (or potentially
        other AWS service) instances
    """

    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--target',
                        help='curl host or address target',
                        type=str, required=True)
    parser.add_argument('-p', '--port',
                        help="target host's port",
                        type=str, required=True)
    args = parser.parse_args()

    host_target_url_and_port = f"{args.target}:{args.port}"
    buffer = BytesIO()
    c = pycurl.Curl()
    c.setopt(c.VERBOSE, True)
    c.setopt(c.URL, host_target_url_and_port)
    c.setopt(c.WRITEDATA, buffer)
    c.perform()
    c.close()

    body = buffer.getvalue()

    # from pycurl docs:
    # Body is a byte string.
    # We have to know the encoding in order to print it to a text file
    # such as standard output.

    print(body.decode('iso-8859-1'))

pycurl_get()


import torch
import torch.nn as nn
import torchvision.transforms as transforms
from torch.utils.data import DataLoader, Dataset

class ImgDataset(Dataset):

    def __init__(self, x, y=None, transform=None):
        self.x = x
        # label is required to be a LongTensor
        self.y = y
        if y is not None:
            self.y = torch.LongTensor(y)
        self.transform = transform

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

    def __getitem__(self, index):
        X = self.x[index]
        if self.transform is not None:
            X = self.transform(X)
        if self.y is not None:
            Y = self.y[index]
            return X, Y
        else:
            return X


"""
conway.py
A simple Python/matplotlib implementation of Conway's Game of Life.
"""

import sys, argparse
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import os

ON = 255
OFF = 0
vals = [ON, OFF]
gen = 1
datas = ""

def randomGrid(N):
    """returns a grid of NxN random values"""
    return np.random.choice(vals, N*N, p=[0.2, 0.8]).reshape(N, N)

'''def addGlider(i, j, grid):
    """adds a glider with top left cell at (i, j)"""
    glider = np.array([[0, 0, 0, 0, 0, 0, 0],
                             [0, 0, 255, 255, 255, 255, 0],
                             [0, 255, 0, 0, 0, 255, 0],
                             [0, 0, 0, 0, 0, 255, 0],
                             [0, 255, 0, 0, 255, 0, 0],
                             [0, 0, 0, 0, 0, 0, 0]])
    #glider = np.array([[0,    0, 255],
     #                  [255,  0, 255],
      #                 [0,  255, 255]])
    grid[i:i+6, j:j+7] = glider'''

#STILL LIFES
def block(newGrid, N):
    cBlock = 0
    auxGrid = newGrid.copy()
    isBlock = False
    block = np.array([[0, 0, 0, 0],
                      [0, 255, 255, 0],
                      [0, 255, 255, 0],
                      [0, 0, 0, 0]])

    auxBlock = np.zeros((4,4))

    for i in range(N):
        for j in range(N):
            for auxX in range(4):
                for auxY in range(4):
                    try:
                        auxBlock[auxX][auxY] = newGrid[i+auxX][j+auxY]
                    except IndexError:
                        auxBlock[auxX][auxY] = 0
                        pass

            #print("aux Block", auxBlock)
            isBlock = (auxBlock == block).all()
            #print("is block", isBlock)
            if(isBlock):
                cBlock = cBlock + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(4):
                            for auxY in range(4):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

        isBlock = False
        #print("cBlock: ", cBlock)
    return cBlock #, auxGrid

def beehive(newGrid, N):
    cBeehive = 0
    auxGrid = newGrid.copy()
    isBeehive = False
    beehive = np.array([[0, 0, 0, 0, 0, 0],
                        [0, 0, 255, 255, 0, 0],
                        [0, 255, 0, 0, 255, 0],
                        [0, 0, 255, 255, 0, 0],
                        [0, 0, 0, 0, 0, 0]])

    auxBeeh = np.zeros((5, 6))

    for i in range(N):
        for j in range(N):
            for auxX in range(5):
                for auxY in range(6):
                    try:
                        auxBeeh[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxBeeh[auxX][auxY] = 0
                        pass

            isBeehive = (auxBeeh == beehive).all()
            if (isBeehive):
                cBeehive = cBeehive + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(5):
                            for auxY in range(6):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

        isBeehive = False
    return cBeehive #, auxGrid

def loaf(newGrid, N):
    cLoaf = 0
    auxGrid = newGrid.copy()
    isLoaf = False
    loaf = np.array([[0, 0, 0, 0, 0, 0],
                     [0, 0, 255, 255, 0, 0],
                     [0, 255, 0, 0, 255, 0],
                     [0, 0, 255, 0, 255, 0],
                     [0, 0, 0, 255, 0, 0],
                     [0, 0, 0, 0, 0, 0]])

    auxLoaf = np.zeros((6, 6))

    for i in range(N):
        for j in range(N):
            for auxX in range(6):
                for auxY in range(6):
                    try:
                        auxLoaf[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxLoaf[auxX][auxY] = 0
                        pass

            isLoaf = (auxLoaf == loaf).all()
            if (isLoaf):
                cLoaf = cLoaf + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(6):
                            for auxY in range(6):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''
        isLoaf = False
    return cLoaf #, auxGrid

def boat(newGrid, N):
    cBoat = 0
    auxGrid = newGrid.copy()
    isBoat = False
    boat = np.array([[0, 0, 0, 0, 0],
                     [0, 255, 255, 0, 0],
                     [0, 255, 0, 255, 0],
                     [0, 0, 255, 0, 0],
                     [0, 0, 0, 0, 0]])

    auxBoat = np.zeros((5, 5))

    for i in range(N):
        for j in range(N):
            for auxX in range(5):
                for auxY in range(5):
                    try:
                        auxBoat[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxBoat[auxX][auxY] = 0
                        pass

            isBoat = (auxBoat == boat).all()
            if (isBoat):
                cBoat = cBoat + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(5):
                            for auxY in range(5):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

        isBoat = False
    return cBoat #, auxGrid

def tub(newGrid, N):
    cTub = 0
    auxGrid = newGrid.copy()
    isTub = False
    tub = np.array([[0, 0, 0, 0, 0],
                    [0, 0, 255, 0, 0],
                    [0, 255, 0, 255, 0],
                    [0, 0, 255, 0, 0],
                    [0, 0, 0, 0, 0]])

    auxTub = np.zeros((5, 5))

    for i in range(N):
        for j in range(N):
            for auxX in range(5):
                for auxY in range(5):
                    try:
                        auxTub[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxTub[auxX][auxY] = 0
                        pass

            isTub = (auxTub == tub).all()
            if (isTub):
                cTub = cTub + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(5):
                            for auxY in range(5):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

        isTub = False
    return cTub #, auxGrid

#OSCILATORS
def blinker(newGrid, N):
    cBlink = 0
    auxGrid = newGrid.copy()
    blinker1 = np.array([[0, 0, 0, 0, 0],
                         [0, 0, 255, 0, 0],
                         [0, 0, 255, 0, 0],
                         [0, 0, 255, 0, 0],
                         [0, 0, 0, 0, 0]])

    blinker2 = np.array([[0, 0, 0, 0, 0],
                         [0, 255, 255, 255, 0],
                         [0, 0, 0, 0, 0]])

    auxBlink1 = np.zeros((5, 5))
    auxBlink2 = np.zeros((3, 5))

    for i in range(N):
        for j in range(N):
            for auxX in range(5):
                for auxY in range(5):
                    try:
                        auxBlink1[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxBlink1[auxX][auxY] = 0
                        pass

            for auxX in range(3):
                for auxY in range(5):
                    try:
                        auxBlink2[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxBlink2[auxX][auxY] = 0
                        pass

            if ((auxBlink1 == blinker1).all() or (auxBlink2 == blinker2).all()):
                cBlink = cBlink + 1
                '''if(auxBlink1 == blinker1).all():
                    for i in range(N):
                        for j in range(N):
                            for auxX in range(5):
                                for auxY in range(5):
                                    try:
                                        auxGrid[i + auxX][j + auxY] = 0
                                    except IndexError:
                                        pass
                elif (auxBlink2==blinker2).all():
                    for i in range(N):
                        for j in range(N):
                            for auxX in range(3):
                                for auxY in range(5):
                                    try:
                                        auxGrid[i + auxX][j + auxY] = 0
                                    except IndexError:
                                        pass'''

    return cBlink #, auxGrid

def toad(newGrid, N):
    cToad = 0
    auxGrid = newGrid.copy()
    toad1 = np.array([[0, 0, 0, 0, 0, 0],
                      [0, 0, 0, 255, 0, 0],
                      [0, 255, 0, 0, 255, 0],
                      [0, 255, 0, 0, 255, 0],
                      [0, 0, 255, 0, 0, 0],
                      [0, 0, 0, 0, 0, 0]])

    toad2 = np.array([[0, 0, 0, 0, 0, 0],
                      [0, 0, 255, 255, 255, 0],
                      [0, 255, 255, 255, 0, 0],
                      [0, 0, 0, 0, 0, 0]])

    auxToad1 = np.zeros((6, 6))
    auxToad2 = np.zeros((4, 6))

    for i in range(N):
        for j in range(N):
            for auxX in range(6):
                for auxY in range(6):
                    try:
                        auxToad1[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxToad1[auxX][auxY] = 0
                        pass

            for auxX in range(4):
                for auxY in range(6):
                    try:
                        auxToad2[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxToad2[auxX][auxY] = 0
                        pass

            if ((auxToad1 == toad1).all() or (auxToad2 == toad2).all()):
                cToad = cToad + 1
                '''if(auxToad1==toad1).all():
                    for i in range(N):
                        for j in range(N):
                            for auxX in range(6):
                                for auxY in range(6):
                                    try:
                                        auxGrid[i + auxX][j + auxY] = 0
                                    except IndexError:
                                        pass
                elif(auxToad2==toad2).all():
                    for i in range(N):
                        for j in range(N):
                            for auxX in range(4):
                                for auxY in range(6):
                                    try:
                                        auxGrid[i + auxX][j + auxY] = 0
                                    except IndexError:
                                        pass'''

    return cToad #, auxGrid

def beacon(newGrid, N):
    cBeacon = 0
    auxGrid = newGrid.copy()
    beacon1 = np.array([[0, 0, 0, 0, 0, 0],
                        [0, 255, 255, 0, 0, 0],
                        [0, 255, 255, 0, 0, 0],
                        [0, 0, 0, 255, 255, 0],
                        [0, 0, 0, 255, 255, 0],
                        [0, 0, 0, 0, 0, 0]])

    beacon2 = np.array([[0, 0, 0, 0, 0, 0],
                        [0, 255, 255, 0, 0, 0],
                        [0, 255, 0, 0, 0, 0],
                        [0, 0, 0, 0, 255, 0],
                        [0, 0, 0, 255, 255, 0],
                        [0, 0, 0, 0, 0, 0]])

    auxBeac1 = np.zeros((6, 6))
    auxBeac2 = np.zeros((6, 6))

    for i in range(N):
        for j in range(N):
            for auxX in range(6):
                for auxY in range(6):
                    try:
                        auxBeac1[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxBeac2[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxBeac1[auxX][auxY] = 0
                        auxBeac2[auxX][auxY] = 0
                        pass

            if ((auxBeac1 == beacon1).all() or (auxBeac2 == beacon2).all()):
                cBeacon = cBeacon + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(6):
                            for auxY in range(6):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

    return cBeacon #, auxGrid

#SPACESHIPS
def glider(newGrid, N):
    cGlider = 0
    isGlid = False
    auxGrid = newGrid.copy()
    glider1 = np.array([[0, 0, 0, 0, 0],
                        [0, 0, 255, 0, 0],
                        [0, 0, 0, 255, 0],
                        [0, 255, 255, 255, 0],
                        [0, 0, 0, 0, 0]])

    glider2 = np.array([[0, 0, 0, 0, 0],
                        [0, 255, 0, 255, 0],
                        [0, 0, 255, 255, 0],
                        [0, 0, 255, 0, 0],
                        [0, 0, 0, 0, 0]])

    glider3 = np.array([[0, 0, 0, 0, 0],
                        [0, 0, 0, 255, 0],
                        [0, 255, 0, 255, 0],
                        [0, 0, 255, 255, 0],
                        [0, 0, 0, 0, 0]])

    glider4 = np.array([[0, 0, 0, 0, 0],
                        [0, 255, 0, 0, 0],
                        [0, 0, 255, 255, 0],
                        [0, 255, 255, 0, 0],
                        [0, 0, 0, 0, 0]])

    auxGlid1 = np.zeros((5, 5))
    auxGlid2 = np.zeros((5, 5))
    auxGlid3 = np.zeros((5, 5))
    auxGlid4 = np.zeros((5, 5))

    for i in range(N):
        for j in range(N):
            for auxX in range(5):
                for auxY in range(5):
                    try:
                        auxGlid1[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxGlid2[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxGlid3[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxGlid4[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxGlid1[auxX][auxY] = 0
                        auxGlid2[auxX][auxY] = 0
                        auxGlid3[auxX][auxY] = 0
                        auxGlid4[auxX][auxY] = 0
                        pass

            if ((auxGlid1 == glider1).all() or (auxGlid2 == glider2).all() or (auxGlid3 == glider3).all() or (auxGlid4 == glider4).all()):
                cGlider = cGlider + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(5):
                            for auxY in range(5):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

    return cGlider #, auxGrid

def lightWeight(newGrid, N):
    cLight = 0
    auxGrid = newGrid.copy()
    lightWeight1 = np.array([[0, 0, 0, 0, 0, 0, 0],
                             [0, 255, 0, 0, 255, 0, 0],
                             [0, 0, 0, 0, 0, 255, 0],
                             [0, 255, 0, 0, 0, 255, 0],
                             [0, 0, 255, 255, 255, 255, 0],
                             [0, 0, 0, 0, 0, 0, 0]])

    lightWeight2 = np.array([[0, 0, 0, 0, 0, 0, 0],
                             [0, 0, 0, 255, 255, 0, 0],
                             [0, 255, 255, 0, 255, 255, 0],
                             [0, 255, 255, 255, 255, 0, 0],
                             [0, 0, 255, 255, 0, 0, 0],
                             [0, 0, 0, 0, 0, 0, 0]])

    lightWeight3 = np.array([[0, 0, 0, 0, 0, 0, 0],
                             [0, 0, 255, 255, 255, 255, 0],
                             [0, 255, 0, 0, 0, 255, 0],
                             [0, 0, 0, 0, 0, 255, 0],
                             [0, 255, 0, 0, 255, 0, 0],
                             [0, 0, 0, 0, 0, 0, 0]])

    lightWeight4 = np.array([[0, 0, 0, 0, 0, 0, 0],
                             [0, 0, 255, 255, 0, 0, 0],
                             [0, 255, 255, 255, 255, 0, 0],
                             [0, 255, 255, 0, 255, 255, 0],
                             [0, 0, 0, 255, 255, 0, 0],
                             [0, 0, 0, 0, 0, 0, 0]])

    auxLight1 = np.zeros((6, 7))
    auxLight2 = np.zeros((6, 7))
    auxLight3 = np.zeros((6, 7))
    auxLight4 = np.zeros((6, 7))

    for i in range(N):
        for j in range(N):
            for auxX in range(6):
                for auxY in range(7):
                    try:
                        auxLight1[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxLight2[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxLight3[auxX][auxY] = newGrid[i + auxX][j + auxY]
                        auxLight4[auxX][auxY] = newGrid[i + auxX][j + auxY]
                    except IndexError:
                        auxLight1[auxX][auxY] = 0
                        auxLight2[auxX][auxY] = 0
                        auxLight3[auxX][auxY] = 0
                        auxLight4[auxX][auxY] = 0
                        pass

            if (auxLight1 == lightWeight1).all() or (auxLight2 == lightWeight2).all() or (auxLight3 == lightWeight3).all() or (auxLight4 == lightWeight4).all():
                cLight = cLight + 1
                '''for i in range(N):
                    for j in range(N):
                        for auxX in range(6):
                            for auxY in range(7):
                                try:
                                    auxGrid[i + auxX][j + auxY] = 0
                                except IndexError:
                                    pass'''

    return cLight #, auxGrid

def others(newGrid, N):
    others = 0
    for i in range(N):
        for j in range(N):
            if newGrid[i][j] == 255:
                others = others + 1

    return others

def update(frameNum, img, grid, N):
    global gen
    print("Generation: ", gen)
    # copy grid since we require 8 neighbors for calculation
    # and we go line by line
    newGrid = grid.copy()
    # TODO: Implement the rules of Conway's Game of Life
    for i in range(N):
        for j in range(N):
            alive = 0

            try:
                if (grid[i + 1][j - 1] == 255):
                    alive = alive + 1
                if (grid[i][j - 1] == 255):
                    alive = alive + 1
                if (grid[i - 1][j - 1] == 255):
                    alive = alive + 1
                if (grid[i - 1][j] == 255):
                    alive = alive + 1
                if (grid[i - 1][j + 1] == 255):
                    alive = alive + 1
                if (grid[i][j + 1] == 255):
                    alive = alive + 1
                if (grid[i + 1][j + 1] == 255):
                    alive = alive + 1
                if (grid[i + 1][j] == 255):
                    alive = alive + 1
            except IndexError:
                pass

            if (newGrid[i][j] == 255):
                if (alive < 2):
                    newGrid[i][j] = 0
                elif (alive > 3):
                    newGrid[i][j] = 0
            elif(newGrid[i][j] == 0):
                if (alive == 3):
                    newGrid[i][j] = 255

    #Counting blinkers, gliders, etc.
    tBlock = block(newGrid, N)
    tBeehi = beehive(newGrid, N)
    tLoaf = loaf(newGrid, N)
    tBoat = boat(newGrid, N)
    tTub = tub(newGrid, N)

    tBlink = blinker(newGrid, N)
    tToad = toad(newGrid, N)
    tBeacon = beacon(newGrid, N)

    tGlider = glider(newGrid, N)
    tLight = lightWeight(newGrid, N)

    '''tBlock, newGrid1 = block(newGrid, N)
    tBeehi, newGrid2 = beehive(newGrid1, N)
    tLoaf, newGrid3 = loaf(newGrid2, N)
    tBoat, newGrid4 = boat(newGrid3, N)
    tTub, newGrid5 = tub(newGrid4, N)

    tBlink, newGrid6 = blinker(newGrid5, N)
    tToad, newGrid7 = toad(newGrid6, N)
    tBeacon, newGrid8 = beacon(newGrid7, N)

    tGlider, newGrid9 = glider(newGrid8, N)
    tLight, newGrid10 = lightWeight(newGrid9, N)'''

    #tOthers = others(newGrid10, N)

    sumConfig = tBlock+tBeehi+tLoaf+tBoat+tTub+tBlink+tToad+tBeacon+tGlider+tLight #+tOthers

    perBlock = (tBlock*100)/sumConfig
    perBeehi = (tBeehi*100)/sumConfig
    perLoaf = (tLoaf*100)/sumConfig
    perBoat = (tBoat*100)/sumConfig
    perTub = (tTub*100)/sumConfig

    perBlink = (tBlink*100)/sumConfig
    perToad = (tToad*100)/sumConfig
    perBeacon = (tBeacon*100)/sumConfig

    perGlider = (tGlider*100)/sumConfig
    perLight = (tLight*100)/sumConfig

    #perOthers = (tOthers*100)/sumConfig

    global datas
    datas = datas + "\n###### Generation: " + str(gen) + " ######\n" + "Total Blocks: " + str(
        tBlock) + " Percentage: "+str(perBlock)+ "\nTotal Beehive: " + str(tBeehi) + " Percentage: "+str(perBeehi) + "\nTotal Loafs: " + str(tLoaf) + " Percentage: "+str(perLoaf)+ \
            "\nTotal Boats: " + str(tBoat)+ " Percentage: "+str(perBoat) + "\nTotal Tubs: " + str(tTub)+ " Percentage: "+str(perTub) + "\nTotal Blinkers: " + str(
        tBlink)+ " Percentage: "+str(perBlink) + "\nTotal Toads: " + str(tToad) + " Percentage: "+str(perToad)+ "\nTotal Beacons: " + str(tBeacon) + " Percentage: "+str(perBeacon)+ \
            "\nTotal Gliders: " + str(tGlider)+ " Percentage: "+str(perGlider) + "\nTotal Light-weight spaceship: " + str(tLight)+ " Percentage: "+str(perLight) +"\n" #+ "\nTotal others: " + str(tOthers) + " Percentage: "+str(perOthers)+ "\n"
    #print(datas)

    # update data
    img.set_data(newGrid)
    grid[:] = newGrid[:]
    gen = gen + 1

    #if frameNum == gen-1:
    info = open("info.txt", 'w')
    n = info.write(datas)
    info.close()

    return img,

# main() function
def main():
    # Command line args are in sys.argv[1], sys.argv[2] ..
    # sys.argv[0] is the script name itself and can be ignored
    # parse arguments
    parser = argparse.ArgumentParser(description="Runs Conway's Game of Life system.py.")
    # TODO: add arguments

    file = open('inputC3.txt', 'r')
    lines = file.readlines()
    datas = ""

    # set grid size
    N = 0

    # set animation update interval
    updateInterval = 50

    # declare grid
    grid = np.array([])
    # populate grid with random on/off - more off than on
    #grid = randomGrid(N) #Creates a random grid
    # Uncomment lines to see the "glider" demo
    #addGlider(1, 1, grid)

    for i in range(len(lines)):
        if i==0:
            N=int(lines[i])
            grid = np.zeros(N * N).reshape(N, N)
            continue
        data = lines[i].split(',')
        coordX = int(data[0])
        coordY = int(data[1])
        grid[coordX][coordY] = 255

    # set up animation
    fig, ax = plt.subplots()
    img = ax.imshow(grid, interpolation='nearest')
    ani = animation.FuncAnimation(fig, update, fargs=(img, grid, N, ),
                                  frames = 200,
                                  interval=updateInterval,
                                  save_count=50,
                                  repeat=False )

    plt.show()

# call main
if __name__ == '__main__':
    main()


from django.shortcuts import render
from .models import Product
from .models import AAA
# Create your views here.
from django.http import HttpResponseRedirect
from django.urls import reverse

def index(request):
    object_list = Product.objects.all()
    if("busca" in request.GET):
        object_list = object_list.filter(name__icontains=request.GET["busca"])
    return render(request, 'product/product.html', {'objetos':object_list})

def add(request):
    if request.POST:
        logo = request.FILES['image']
        description = request.POST['description']
        name = request.POST['name']
        value = request.POST['value']
        athletic = AAA.objects.get(id=1)
        product = Product.objects.create(name = name, image = logo, value= value, description = description,athletic= athletic )
        product.save()
        return HttpResponseRedirect(reverse('product:index'))
    return render(request, 'product/add.html')

def edit(request, id):
    if request.POST:
        description = request.POST['description']
        name = request.POST['name']
        value = request.POST['value']
        product = Product.objects.get(id =id)
        product.name = name
        product.description = description
        product.value = value
        product.save()
        return HttpResponseRedirect(reverse('product:index'))
    return render(request, 'product/edit.html', {'objeto':Product.objects.get(id=id)} )

def delete(request, id):
    product = Product.objects.get(id=id)
    product.delete()
    return HttpResponseRedirect(reverse('product:index'))

def views_product(request,id):
    product = Product.objects.get(id=id)
    res = {
        'objeto':product
    }
    return render(request,'product/product_detail.html',res)