'''

Triangle, square, pentagonal, hexagonal, heptagonal, and octagonal numbers are all figurate (polygonal) numbers and are generated by the following formulae:
Triangle 	  	P3,n=n(n+1)/2 	  	1, 3,  6, 10, 15, ...
Square   	  	P4,n=n2 	    	1, 4,  9, 16, 25, ...
Pentagonal 	  	P5,n=n(3n−1)/2 	  	1, 5, 12, 22, 35, ...
Hexagonal 	  	P6,n=n(2n−1) 	  	1, 6, 15, 28, 45, ...
Heptagonal 	  	P7,n=n(5n−3)/2 	  	1, 7, 18, 34, 55, ...
Octagonal 	  	P8,n=n(3n−2) 	  	1, 8, 21, 40, 65, ...

The ordered set of three 4-digit numbers: 8128, 2882, 8281, has three interesting properties.

    The set is cyclic, in that the last two digits of each number is the first two digits of the next number (including the last number with the first).
    Each polygonal type: triangle (P3,127=8128), square (P4,91=8281), and pentagonal (P5,44=2882), is represented by a different number in the set.
    This is the only set of 4-digit numbers with this property.

Find the sum of the only ordered set of six cyclic 4-digit numbers for which each polygonal type: triangle, square, pentagonal, hexagonal, heptagonal, and octagonal, is represented by a different number in the set.
'''

# there are 6! possible orders for the functions to be arranged
# Reduces to 5! once we realize 654321 is the same as 543216

# a method for creating permutations! How useful!
from itertools import permutations

# def makeTri(n):
#     return int(n*(n+1)/2)

# def makeSqare(n):
#     return n**2

# def makePent(n):
#     return int(n*(3*n-1)/2)

# def makeHex(n):
#     return n*(2*n-1)

# def makeHept(n):
#     return int(n*(5*n-3)/2)

# def makeOct(n):
#     return n*(3*n-2)


def isTri(n):
    test = is_square(n*8+1)
    if test[0]:
        if (test[1]-1)%2 ==0:
            return True
        else: return False
    else: return False

# checks for perfect square
# babylonian algorithm
def is_square(apositiveint):
    x = apositiveint // 2
    seen = set([x])
    while x * x != apositiveint:
        x = (x + (apositiveint // x)) // 2
        if x in seen: return [False, None]
        seen.add(x)
    return [True, x]

def isPent(n):
    test = is_square(n*24+1)
    if test[0]:
        if (test[1]+1)%6 == 0:
            return True
        else: return False
    else: return False
    
def isHex(n):
    test = is_square(n*8+1)
    if test[0]:
        if(test[1]+1)%4 == 0:
            return True
        else: return False
    else: return False

def isHept(n):
    test = is_square(40*n+9) 
    if test[0]:
        if (test[1]+3)%10 == 0:
            return True
        else: return False
    else: return False

def isOct(n):
    test = is_square(n*3+1)
    if test[0]:
        if (test[1]+1)%3 ==0:
            return True
        else: return False
    else: return False

tris = []
squares =[]
pents = []
hexs = [] 
hepts =[]
octs = []

for i in range(1000,10000):
    if isTri(i):
        tris.append(i)
    if is_square(i)[0]:
        squares.append(i)
    if isPent(i):
        pents.append(i)
    if isHex(i):
        hexs.append(i)
    if isHept(i):
        hepts.append(i)
    if isOct(i):
        octs.append(i)

megalist = [tris,squares,pents,hexs,hepts,octs]

perms = list(permutations(megalist))

z=0
for perm in perms:
    for i in perm[0]:
        for j in perm[1]:
            if str(i)[2:4] == str(j)[0:2]:

                for k in perm[2]:
                    if str(j)[2:4] == str(k)[0:2]:

                        for l in perm[3]:
                            if str(k)[2:4]==str(l)[0:2]:

                                for m in perm[4]:
                                    if str(l)[2:4] == str(m)[0:2]:

                                        for n in perm[5]:
                                            if str(m)[2:4] == str(n)[0:2] and str(n)[2:4] == str(i)[0:2]:
                                                z+=1
                                                print('{} | {} | {} | {} | {} | {} | {}'.format(i,j,k,l,m,n,z))
                                                print('Sum of permutations = {}\n'.format(i+j+k+l+m+n))
            
