def map(fun, items):
    return (None if is_none(items)
            else pair(fun(head(items)),
                      map(fun, tail(items))))
def accumulate(op, initial, sequence):
    return (initial if is_none(sequence)
            else op(head(sequence),
                    accumulate(op, initial, tail(sequence))))
def accumulate_n(op, init, seqs):
    return (None if is_none(head(seqs))
            else pair(accumulate(op, init, map(lambda x: head(x), seqs)),
                      accumulate_n(op, init, map(lambda x: tail(x), seqs))))
def plus(x, y):
    return x + y
def times(x, y):
    return x * y
def dot_product(v, w):
    return accumulate(plus, 0, accumulate_n(times, 1, llist(v, w)))
def matrix_times_vector(m, v):
    return map(lambda row: dot_product(row, v), m)
def transpose(mat):
    return accumulate_n(pair, None, mat)
def matrix_times_matrix(n, m):
    cols = transpose(m)
    return map(lambda x: map(lambda y: dot_product(x, y), cols), n)

v = llist(10, 20, 30)
m1 = llist(llist(1, 2, 3), llist(3, 5, 1), llist(1, 1, 1))
m2 = llist(llist(1, 2, 3), llist(4, 5, 6), llist(7, 8, 9))

print(matrix_times_vector(m1, v))
# transpose(m1)
# matrix_times_matrix(m1, m2)

# expected: [ 140, [ 160, [ 60, None ] ] ]
