#################################################
# 15-112-n19 hw2-1
# Your Name:
# Your Andrew ID:
# Your Section:
#################################################

import math
import string
import copy

#################################################
# Helper functions
#################################################

def almostEqual(d1, d2, epsilon=10**-7):
    return (abs(d2 - d1) < epsilon)

import decimal
def roundHalfUp(d):
    # Round to nearest with ties going away from zero.
    rounding = decimal.ROUND_HALF_UP
    # See other rounding options here:
    # https://docs.python.org/3/library/decimal.html#rounding-modes
    return int(decimal.Decimal(d).to_integral_value(rounding=rounding))

#################################################
# hw2-1 problems
#################################################

def lookAndSay(a):
    return 42

def inverseLookAndSay(a):
    return 42

def nondestructiveRotateList(a, n):
    return 42

def destructiveRotateList(a, n):
    return 42

def bestScrabbleScore(dictionary, letterScores, hand):
    return 42

def runSimpleProgram(program, args):
    return 42


#################################################
# hw2-1 test cases
# Note:
#   There are fewer test cases than usual below.
#   You'll want to add your own!
#################################################

def _verifyLookAndSayIsNondestructive():
    a = [1,2,3]
    b = copy.copy(a)
    lookAndSay(a) # ignore result, just checking for destructiveness here
    return (a == b)

# add more test cases here!
def testLookAndSay():
    print("Testing lookAndSay()...", end="")
    assert(_verifyLookAndSayIsNondestructive() == True)
    assert(lookAndSay([]) == [])
    assert(lookAndSay([1,1,1]) ==  [(3,1)])
    print("Passed!")

def _verifyInverseLookAndSayIsNondestructive():
    a = [(1,2), (2,3)]
    b = copy.copy(a)
    inverseLookAndSay(a) # ignore result, just checking for destructiveness here
    return (a == b)

# add more test cases here!
def testInverseLookAndSay():
    print("Testing inverseLookAndSay()...", end="")
    assert(_verifyInverseLookAndSayIsNondestructive() == True)
    assert(inverseLookAndSay([]) == [])
    assert(inverseLookAndSay([(3,1)]) == [1,1,1])
    inverseLookAndSay([(2,3),(1,8),(3,-10)]) == [3,3,8,-10,-10,-10]
    print("Passed!")

def _verifynondestructiveRotateListIsNondestructive():
    a = [1,2,3]
    b = copy.copy(a)
    # ignore result, just checking for destructiveness here
    nondestructiveRotateList(a,1)
    return (a == b)

def testNondestructiveRotateList():
    print("Testing nondestructiveRotateList()...", end="")
    _verifynondestructiveRotateListIsNondestructive()
    assert(nondestructiveRotateList([1,2,3,4], 1) == [4,1,2,3])
    print("Passed!")

def testDestructiveRotateList():
    print("Testing destructiveRotateList()...", end="")

    # A single test case
    L = [1,2,3,4]
    r = destructiveRotateList(L,1)
    assert(r == None)
    assert(L == [4,1,2,3])

    print("Passed!")

# there are lots of test cases here :)
def testBestScrabbleScore():
    print("Testing bestScrabbleScore()...", end="")
    def dictionary1(): return ["a", "b", "c"]
    def letterScores1(): return [1] * 26
    def dictionary2(): return ["xyz", "zxy", "zzy", "yy", "yx", "wow"] 
    def letterScores2(): return [1+(i%5) for i in range(26)]
    assert(bestScrabbleScore(dictionary1(), letterScores1(), list("ace")) == 
            (["a", "c"], 1))
    assert(bestScrabbleScore(dictionary1(), letterScores1(), list("b")) == 
            ("b", 1))
    assert(bestScrabbleScore(dictionary1(), letterScores1(), list("z")) == None)
    # x = 4, y = 5, z = 1
    # ["xyz", "zxy", "zzy", "yy", "yx", "wow"]
    #    10     10     7     10    9      -
    assert(bestScrabbleScore(dictionary2(), letterScores2(), list("xyz")) == 
            (["xyz", "zxy"], 10))
    assert(bestScrabbleScore(dictionary2(), letterScores2(), list("xyzy")) == 
            (["xyz", "zxy", "yy"], 10))
    assert(bestScrabbleScore(dictionary2(), letterScores2(), list("xyq")) == 
            ("yx", 9))
    assert(bestScrabbleScore(dictionary2(), letterScores2(), list("yzz")) == 
            ("zzy", 7))
    assert(bestScrabbleScore(dictionary2(), letterScores2(), list("wxz")) == 
            None)
    print("Passed!")

def testRunSimpleProgram():
    print("Testing runSimpleProgram()...", end="")
    largest = """! largest: Returns max(A0, A1)
                   L0 - A0 A1
                   JMP+ L0 a0
                   RTN A1
                   a0:
                   RTN A0"""
    assert(runSimpleProgram(largest, [5, 6]) ==  6)
    assert(runSimpleProgram(largest, [6, 5]) == 6)

    sumToN = """! SumToN: Returns 1 + ... + A0
                ! L0 is a counter, L1 is the result
                L0 0
                L1 0
                loop:
                L2 - L0 A0
                JMP0 L2 done
                L0 + L0 1
                L1 + L1 L0
                JMP loop
                done:
                RTN L1"""
    assert(runSimpleProgram(sumToN, [5]) ==  1+2+3+4+5)
    assert(runSimpleProgram(sumToN, [10]) == 10*11//2)
    print("Passed!")

#################################################
# hw2-1 Main
################################################

def testAll():
    testLookAndSay()
    testInverseLookAndSay()
    testNondestructiveRotateList()
    testDestructiveRotateList()
    testBestScrabbleScore()
    # testRunSimpleProgram() # uncomment this if you want to do the bonus!

def main():
    testAll()

if __name__ == '__main__':
    main()
