#################################################
# Basic Problem Set #2
# Loops (For and While Loops)
#################################################

# Many of these problems come directly from
# the course notes. So, try to complete them without
# looking at the notes! Then, if there are
# problems that you couldn't figure out, 
# take another look at the course notes!

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

import math

def almostEqual(d1, d2, epsilon=10**-7): #helper-fn
    # note: use math.isclose() outside 15-112 with Python version 3.5 or later
    return (abs(d2 - d1) < epsilon)

import decimal
def roundHalfUp(d): #helper-fn
    # 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))

#####################################################
# sumFromAToB(a, b)
#####################################################

# Write the function sumFromAToB(a, b), which takes
# in two nonnegative integers a and b and determines
# the sum of the numbers between a and b. 
# a is guaranteed to be less than or equal to b.

# This should be inclusive of both a and b, so 
# sumFromAToB(3, 5) should equal 3 + 4 + 5. 
# Always pay attention to whether or not the upper
# bound (b in this case) is inclusive or noninclusive!

# write sumFromAToB here!
def sumFromAToB(a, b):
  return 42

def testSumFromAToB():
  print('Testing sumFromAToB...', end='')
  assert(sumFromAToB(3, 4) == 7)
  assert(sumFromAToB(3, 3) == 3)
  assert(sumFromAToB(1, 3) == 6)
  assert(sumFromAToB(2, 5) == (2 + 3 + 4 + 5))
  print('Passed!')

# testSumFromAToB() # uncomment to test

# ###################################################
# leftmostDigit(n)
# ###################################################

# Write the function leftmostDigit(n), which takes in
# a possibly negative integer n and returns the leftmost
# digit of n.
# If n is negative, what builtin function can we use
# so that we can do the same math on it that we would
# use in the case where it is positive? 

# write leftmostDigit here!
def leftmostDigit(n):
  return 42

def testLeftmostDigit():
    print('Testing leftmostDigit()... ', end='')
    assert(leftmostDigit(3723) == 3)
    assert(leftmostDigit(47294794) == 4)
    assert(leftmostDigit(-27362637272) == 2)
    print('Passed!')

# testLeftmostDigit() # uncomment to test!

#######################################################
# isSinclairNumber(n)
#######################################################

# Write the function isSinclairNumber(n)
# that takes in a possibly negative integer n
# and returns True if it is a Sinclair number and
# False if it isn't.

# A Sinclair number is a number that contains exactly
# 4 odd digits and is divisible by 3. For example,
# 3333 is a Sinclair number. 2223366 is also a Sinclair
# number. 

# write isSinclairNumber here!
def isSinclairNumber(n):
  return 42

def testIsSinclairNumber(n):
  print('Testing isSinclairNumber...', end='')
  assert(isSinclairNumber(3333) == True)
  assert(isSinclairNumber(2223366) == True)
  assert(isSinclairNumber(333) == False)
  assert(isSinclairNumber(4444) == False)
  assert(isSinclairNumber(6666) == True)
  print('Passed!')

# testIsSinclairNumber() # uncomment to test!

#######################################################
# nthMultipleOfTwo(n)
#######################################################

# Write the function nthMultipleOfTwo(n),
# that takes in a non-negative integer n and returns the nth
# possible multiple of two. For example, if n=2, then the function
# will return 4, because 0 is the 0th multiple of 2, 2 is the 1st 
# multiple of 2, and 4 is the 2nd multiple of 2. (Pay attention to
# the fact that we start counting at 0 here).

# You will need to use a helper function here.
# In addition, you should be using the nth template for this problem.
# If you don't rememeber the nth template, refer back to the notes.

# write nthMultipleOfTwo here!
def nthMultipleOfTwo(n):
  return 42

def testNthMultipleOfTwo():
    print('Testing nthMultipleOfTwo()... ', end='')
    assert(nthMultipleOfTwo(0) == 0)
    assert(nthMultipleOfTwo(1) == 2)
    assert(nthMultipleOfTwo(2) == 4)
    print('Passed!')

# testNthMultipleOfTwo() # uncomment to test!

#######################################################
# isPrime(n)
#######################################################

# Write the function isPrime(n) that takes in a
# possibly negative integer n and returns whether or not
# it is a prime number. For reference, negative numbers
# are never prime numbers. If you are unsure about what makes
# something a prime number, look it up!

# We learn about how there are different ways to write the
# same function - for example, there is a way to write isPrime
# that is very fast! We call this fasterIsPrime in the notes.
# However, it is typically fine to use the slower, simpler
# version of isPrime when we need it in this class. 
# So, here, you should write that version (aka, don't worry about
# function efficiency).

# write isPrime(n) here!

def isPrime(n):
   return 42

def testIsPrime():
    print('Testing isPrime()... ', end='')
    assert(isPrime(25) == False)
    assert(isPrime(23) == True)
    assert(isPrime(27) == False)
    assert(isPrime(2) == True)
    assert(isPrime(-3) == False)
    assert(isPrime(239) == True)
    print('Passed!')

# testIsPrime() # uncomment to check
