CMSI 386
Homework #2
Partial Answers
warmup.py
"""A collection of functions illustrating some basic Python."""

import random
import re
import json
import math
import requests
from cryptography.fernet import Fernet

def change(amount):
    """
    Return a tuple with the minimum number of U.S. quarters, dimes, nickels,
    and pennies, respectively, that make the given amount.
    """
    if amount < 0:
        raise ValueError('amount cannot be negative')
    quarters, rest = divmod(amount, 25)
    dimes, rest = divmod(rest, 10)
    nickels, pennies = divmod(rest, 5)
    return (quarters, dimes, nickels, pennies)

def strip_quotes(s):
    """
    Return the string like s but with apostrophes and quotation marks removed.
    """
    return re.sub('[\'"]', '', s)

def scramble(s):
    """
    Return a random permutation of a string.
    """
    return ''.join(random.sample(s, len(s)))

def triples(n):
    """
    Return a list of integer Pythagorean triples with max hypotenuse n.
    """
    return [(x, y, z)
            for x in range(1, n + 1)
            for y in range(x, n + 1)
            for z in range(y, n + 1)
            if x * x + y * y == z * z]

def powers(base, limit):
    """Return a generator for powers of base up to limit"""
    result = 1
    while result <= limit:
        yield result
        result *= base

def say(first_word=None):
    """The famous chainable function problem."""
    words = []
    def say_more(word=None):
        if word is None:
            return ' '.join(words)
        words.append(word)
        return say_more
    return say_more(first_word)

def interleave(a, *b):
    """
    Returns the interleaving of an array with a bunch of values. The lengths
    of the array and the number of values do not need to be the same.
    """
    return [item for pair in zip(a, b) for item in pair] + a[len(b):] + list(b)[len(a):]

class Cylinder:
    def __init__(self, radius=1, height=1):
        self.radius = radius
        self.height = height

    @property
    def cap_area(self):
        return math.pi * self.radius * self.radius

    @property
    def surface_area(self):
        return (2 * self.cap_area) + (2 * math.pi * self.radius * self.height)

    @property
    def volume(self):
        return math.pi * self.radius * self.radius * self.height

    def stretch(self, factor):
        self.height *= factor

    def widen(self, factor):
        self.radius *= factor

    def __str__(self):
        return f'Cylinder with radius {self.radius} and height {self.height}'

def make_crypto_functions(key):
    """
    Return a tuple of two functions, one for encryption and one for decryption
    """
    cipher = Fernet(key)
    return (cipher.encrypt, cipher.decrypt)

def random_name(*, region, gender):
    """
    Return a random name with the given region and geneder using the uinames API.
    """
    params = {'region': region, 'gender': gender, 'amount': 1}
    headers = {'User-Agent': 'Homework Assignment from LMU'}
    response = requests.get('https://uinames.com/api/', params=params, headers=headers)
    if response.status_code in range(200, 300):
        person = json.loads(response.text)
        return f'{person["surname"]}, {person["name"]}'
    else:
        raise ValueError(response.text)