height = [70,  # inches,
          170, # pounds,
          40]  # years

grades = [95, # exam1,
          80, # exam2,
          75, # exam3,
          62] # exam4

def vector_add(v, w):
    '''adds corresponding elements'''
    return [v_i + w_i
            for v_i, w_i in zip(v, w)]

def vector_substract(v, w):
    """substracts corresponding elements"""
    return [v_i - w_i
            for v_i, w_i in zip(v, w)]

def vector_sum(vectors):
    """sums all corresponding elements"""
    result = vectors[0]                            # start with the first vector
    for vector in vectors[1: ]:                    # then loop over the others
        result = vector_add(result, vector)        # and add them to the result
    return result

def vector_sum2(vectors):
    return reduce(vector_add, vectors)

def scalar_multiply(c, v):
    """c is a number and v is a vector"""
    return [c * v_i for v_i in v]

def vector_mean(vectors):
    """compute the vector whose ith element is the mean 
    of the ith elements of the input vectors"""
    n = len(vectors)
    return scalar_multiply(1/n, vector_sum(vectors))

def dot(v, w):
    """v_1 * w_1 + v_2 * w_2 + ... + v_n * w_n"""
    return sum(v_i * w_i 
               for v_i, w_i in zip(v, w))

def sum_of_squares(v):
    """v_1 * v_1 + v_2 * v_2 + v_3 * v_3 + ... + v_n * v_n"""
    return dot(v, v)

import math

def magnitude(v):
    return math.sqrt(sum_of_squares(v))

def squared_distance(v, w):
    """(v_1 - w_1)^2 + (v_2 - w_2)^2 + ... + (v_n - w_n)^2"""
    return sum_of_squares(substract(v, w))

def distance1(v, w):
    return math.sqrt(squared_distance(v, w))

def distance2(v, w):
    return magnitude(substract(v, w))

"""
Explanation: Chapter 4 Linear Algebra
Vectors
End of explanation
"""


A = [[1, 2, 3],   # A has 2 rows and 3 columns
     [4, 5, 6]]

B = [[1, 2],      # B has 3 rows and 2 columns
     [3, 4],
     [5, 6]]

def shape(A):
    num_rows = len(A)
    num_cols = len(A[0]) if A else 0
    return num_rows, num_cols

def get_row(A, i):
    return A[i]

def get_column(A, j):
    return [A_i[j] for A_i in A]

def make_matrix(num_rows, num_cols, entry_fn):
    """returns a num_rows x num_cols matrix
    whose (i, j)th entry is generated by function entry_fn(i, j)"""
    return [[entry_fn(i, j) 
             for j in range(num_cols)]
             for i in range(num_rows)]

def is_diagonal(i, j):
    return 1 if i == j else 0

identity_matrix = make_matrix(5, 5, is_diagonal)
identity_matrix

"""
Explanation: Matrices
End of explanation
"""


