import sqlite3

def choose(n, k):
    'Gives the value of n choose k by building Pascals triangle row by row, (much faster than calculating factorials)'
    vec = [1]
    for _ in range(n):
        vec = [
                v1 + v2
                for i, (v1, v2) in enumerate(zip([0] + vec, vec + [0]))
                if i <= k
        ]
    return vec[-1]
def valid(a, b, c, d):
    return nonRedundant(a, b, c, d) and relativelyPrime(a, b, c, d)
def apery(a, b, c, d):
    'Takes a quad as input and returns the Apery set assuming were working mod the minimum element'
    workingMod = min(a,b,c,d)
    intList = [0 for _ in range(workingMod)]
    p = 0
    while any(x == 0 for x in intList[1:]): # while at least one element of the aspery set has not been found
        if isAttainable4(a, b, c, d, p): # if p is attainable by <a,b,c,d>
            eqClass = p%workingMod
            for val in range(1, workingMod): # val will take on every integer value from 1 to workingMod - 1
                if eqClass == val and intList[val] == 0: # if the attainable is in val eq class and is the first one found
                    intList [val] = p # we alter its index in intList and that effectively marks it when revisited by the above line
        p += 1
    return tuple(sorted(intList))  # not sorting will result in elements being ordered according to equivilent class instead of ascending order
def isAttainable4(a, b, c, d, val):
    for i in range(int(val/a) + 1):
        for j in range(int(val/b) + 1):
            for k in range(int(val/c) + 1):
                for l in range(int(val/d) + 1):
                    if i*a + j*b + k*c + l*d == val:
                        return True
    return False
def relativelyPrime(a, b, c, d):
    lst = [a, b, c, d]
    bol = False
    for i in lst:
        for j in lst:
            if i != j and relPrimePair(i, j):
                bol = True
    return bol
def relPrimePair(a, b):
    if a < b:
        minn = a
    else:
        minn = b
    gcf = 1
    for val in range(2, minn):
        if a%val == 0 and b%val == 0:
            gcf = val
    if val == 1:
        return True
    return False
def nonRedundant(a, b, c, d):
    if not isAttainable(a, b, c, d) and not isAttainable(a, b, d, c) and not isAttainable(a, c, d, b) and not isAttainable(b, c, d, a):
        return True
def isAttainable(a, b, c, val):
    for i in range(int(val/a)):
        for j in range(int(val/b)):
            for k in range(int(val/c)):
                if i*a + j*b + k*c == val:
                    return True
    return False
def progress(ctime, ttime, p):
    'Responsible for printing the percentage of data that has been loaded into memoty'
    if time.time() > p:
        p = time.time() + 1
        print(f'{100*ctime/ttime:.2f}%')
    return p
def duplicates(a, b, c, d):
    tup = (a,b,c,d)
    for i in range(len(tup)):
        for j in range(len(tup)):
            if i != j and tup[i] == tup[j]:
                return True
def prettyListOfStrs(someLst):
    if len(someLst) == 0:
        return '<>'
    toret = '<'
    for elem in someLst:
        toret += elem + ', '
    toret = toret[:-2] + '>'
    return toret

def main(database):
    conn = sqlite3.connect(database)
    c = conn.cursor()
    ui = ''
    print("Enter 'done' or 'exit' to quit")
    while ui.lower() not in ('done', 'exit'):
        ui = input('Enter four unique integers <= 200, seperated by spaces:\t')
        uiList = ui.split()
        if len(uiList) != 4:
            # Not four elems
            if ui.lower() not in ('done', 'exit'):
                print('\n' + 8 * '\t' + f'The set {prettyListOfStrs(uiList)} does not have four elements')
        elif any([not elem.isnumeric() for elem in uiList]):
            # At least one elem is not a repr of an int
            print('\n' + 8 * '\t' + f'The set {prettyListOfStrs(uiList)} is not all integers')
        elif sorted(uiList) != sorted(set(uiList)):
            # Not unique/repeated value 
            print('\n' + 8 * '\t' + 'All elements must be unique')
        else:
            intList = []
            for val in uiList:
                intList.append(int(val))
            if True: 
                intList.sort()
                c.execute(f"SELECT * FROM _{intList[0]} WHERE a = {intList[0]} and b = {intList[1]} and c = {intList[2]} and d = {intList[3]}")
                allVals = c.fetchall()
                if allVals:
                    ans = allVals[0][4]
                    if 'all' not in ans.lower():
                        print('\n' + 8 * '\t' + ans + f'\nThe Apery set of <{str(intList)[1:-1]}> is <{str(apery(intList[0],intList[1],intList[2],intList[3]))[1:-1]}>')
                    else:
                        print('\n' + 8 * '\t' + ans)
                else:
                    print('\n' + 8 * '\t' + 'That quad is not in the database')
            else:
                print('\n' + 8 * '\t' + 'All values must be positive')
    conn.close()


main('frob.db')
