#HW4_part1.py Windows
import re

opstack = []  #assuming top of the stack is the end of the list
dictstack = [{}]

def opPop():
    return opstack.pop()

def opPush(value):
    opstack.append(value)

def dictPop():
    return dictstack.pop()

def dictPush():
    newDict = {}
    dictstack.append(newDict)

def define(name,value):
    temp = dictPop()
    temp[name[1:]] = value
    dictstack.append(temp)

def psDef():
    value = opPop()
    name = opPop()
    define(name,value)

def searchDictStack(dic, k):
    if k in dic.keys():
        return dic[k]

def lookup(name): 
    for dic in dictstack:
        if name in dic.keys():
            value = dic[name]
    if value == None:
        print("name not found")
    return value

def add():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float:
        opPush(num2 + num1)
    else:
        opPush(num2)
        opPush(num1)
 

def sub():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float:
        opPush(num2 - num1)
    else:
        opPush(num2)
        opPush(num1)
    
def mul():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float:
        opPush(num2 * num1)
    else:
        opPush(num2)
        opPush(num1)

def div():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float:
        opPush(num2 / num1)
    else:
        opPush(num2)
        opPush(num1)

def get():
    index = opPop()
    array = opPop()
    opPush(array[index])

def length():
    array = opPop()
    opPush(len(array))

def eq():
    num1 = opPop()
    num2 = opPop()
    opPush(num2 == num1)

def lt():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float or str:
        opPush(num2 < num1)
    else:
        opPush(num2)
        opPush(num1)

def gt():
    num1 = opPop()
    num2 = opPop()
    if type(num1) and type(num2) is int or float or str:
        opPush(num2 > num1)
    else:
        opPush(num2)
        opPush(num1)

def psAnd():
    bool1 = opPop()
    bool2 = opPop()
    if type(bool1) and type(bool2) is bool:
        opPush(bool2 and bool1)
    else:
        opPush(bool2)
        opPush(bool1)

def psOr():
    bool1 = opPop()
    bool2 = opPop()
    if type(bool1) and type(bool2) is bool:
        opPush(bool2 or bool1)
    else:
        opPush(bool2)
        opPush(bool1)

def psNot():
    bool1 = opPop()
    if type(bool1) is bool:
        opPush(not bool1)
    else:
        opPush(bool1)

def psDict():
    opPop()
    opPush({})

def begin():
    opPop()
    dictPush()

def end():
    dictPop()

def dup():
    temp = opPop()
    opPush(temp)
    opPush(temp)

def exch():
    temp1 = opPop()
    temp2 = opPop()
    opPush(temp1)
    opPush(temp2)

def copy():
    newstack = []
    times = opPop()
    if type(times) is int:
        for i in range(times):
            newstack.append(opPop())

        while len(newstack) != 0:
            opPush(newstack.pop())
    

def clear():
    opstack.clear()

#------- Part 1 TEST CASES--------------
def testDefine():
    define("/n1", 4)
    if lookup("n1") != 4:
        return False
    return True

def testLookup():
    opPush("/n1")
    opPush(3)
    psDef()
    if lookup("n1") != 3:
        return False
    return True

#Arithmatic operator tests
def testAdd():
    opPush(1)
    opPush(2)
    add()
    if opPop() != 3:
        return False
    return True

def testSub():
    opPush(10)
    opPush(4.5)
    sub()
    if opPop() != 5.5:
        return False
    return True

def testMul():
    opPush(2)
    opPush(4.5)
    mul()
    if opPop() != 9:
        return False
    return True

def testDiv():
    opPush(10)
    opPush(4)
    div()
    if opPop() != 2.5:
        return False
    return True
    
#Comparison operators tests
def testEq():
    opPush(6)
    opPush(6)
    eq()
    if opPop() != True:
        return False
    return True

def testLt():
    opPush(3)
    opPush(6)
    lt()
    if opPop() != True:
        return False
    return True

def testGt():
    opPush(3)
    opPush(6)
    gt()
    if opPop() != False:
        return False
    return True

#boolean operator tests
def testPsAnd():
    opPush(True)
    opPush(False)
    psAnd()
    if opPop() != False:
        return False
    return True

def testPsOr():
    opPush(True)
    opPush(False)
    psOr()
    if opPop() != True:
        return False
    return True

def testPsNot():
    opPush(True)
    psNot()
    if opPop() != False:
        return False
    return True

#Array operator tests
def testLength():
    opPush([1,2,3,4,5])
    length()
    if opPop() != 5:
        return False
    return True

def testGet():
    opPush([1,2,3,4,5])
    opPush(4)
    get()
    if opPop() != 5:
        return False
    return True

#stack manipulation functions
def testDup():
    opPush(10)
    dup()
    if opPop()!=opPop():
        return False
    return True

def testExch():
    opPush(10)
    opPush("/x")
    exch()
    if opPop()!=10 and opPop()!="/x":
        return False
    return True

def testPop():
    l1 = len(opstack)
    opPush(10)
    opPop()
    l2= len(opstack)
    if l1!=l2:
        return False
    return True

def testCopy():
    opPush(1)
    opPush(2)
    opPush(3)
    opPush(4)
    opPush(5)
    opPush(2)
    copy()
    if opPop()!=5 and opPop()!=4 and opPop()!=5 and opPop()!=4 and opPop()!=3 and opPop()!=2:
        return False
    return True

def testClear():
    opPush(10)
    opPush("/x")
    clear()
    if len(opstack)!=0:
        return False
    return True

#dictionary stack operators
def testDict():
    opPush(1)
    psDict()
    if opPop()!={}:
        return False
    return True

def testBeginEnd():
    opPush("/x")
    opPush(3)
    psDef()
    opPush({})
    begin()
    opPush("/x")
    opPush(4)
    psDef()
    end()
    if lookup("x")!=3:
        return False
    return True

def testpsDef():
    opPush("/x")
    opPush(10)
    psDef()
    if lookup("x")!=10:
        return False
    return True

def testpsDef2():
    opPush("/x")
    opPush(10)
    psDef()
    opPush(1)
    psDict()
    begin()
    if lookup("x")!=10:
        end()
        return False
    end()
    return True


def main_part1():
    testCases = [('define',testDefine),('lookup',testLookup),('add', testAdd), ('sub', testSub),('mul', testMul),('div', testDiv), \
                 ('eq',testEq),('lt',testLt),('gt', testGt), ('psAnd', testPsAnd),('psOr', testPsOr),('psNot', testPsNot), \
                 ('length', testLength),('get', testGet), ('dup', testDup), ('exch', testExch), ('pop', testPop), ('copy', testCopy), \
                 ('clear', testClear), ('dict', testDict), ('begin', testBeginEnd), ('psDef', testpsDef), ('psDef2', testpsDef2)]
    # add you test functions to this list along with suitable names
    failedTests = [testName for (testName, testProc) in testCases if not testProc()]
    if failedTests:
        print ('Some tests failed', failedTests)
    else:
        print ('All part-1 tests OK')

if __name__ == '__main__':
    main_part1()