Write a function that accepts two square (`NxN`) matrices (two dimensional arrays), and returns the product of the two. Only square matrices will be given.

How to multiply two square matrices: 

We are given two matrices, A and B, of size 2x2 (note: tests are not limited to 2x2). Matrix C, the solution, will be equal to the product of A and B. To fill in cell `[0][0]` of matrix C, you need to compute: `A[0][0] * B[0][0] + A[0][1] * B[1][0]`.

More general: To fill in cell `[n][m]` of matrix C, you need to first multiply the elements in the nth row of matrix A by the elements in the mth column of matrix B, then take the sum of all those products. This will give you the value for cell `[m][n]` in matrix C. 

## Example
```
  A         B          C
|1 2|  x  |3 2|  =  | 5 4|
|3 2|     |1 1|     |11 8|
```

Detailed calculation:
```
C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0] = 1*3 + 2*1 =  5
C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1] = 1*2 + 2*1 =  4
C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0] = 3*3 + 2*1 = 11
C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1] = 3*2 + 2*1 =  8
```
Link to Wikipedia explaining matrix multiplication (look at the square matrix example): 
http://en.wikipedia.org/wiki/Matrix_multiplication

A more visual explanation of matrix multiplication: http://matrixmultiplication.xyz

~~~if:c
**Note:** In **C**, the dimensions of both square matrices `n` will be passed into your function.  However, since the dimensions of your returned "matrix" is expected to be the same as that of the inputs, you will not need to keep track of the dimensions of your matrix in another variable.
~~~

Difficulty: introductory

Starter code:

def matrix_mult(a, b):
	

Solutions:
from numpy import matrix

def matrix_mult(a, b):
  return ( matrix(a) * matrix(b) ).tolist()

def matrix_mult(a, b):
  return [[sum(a[i][k]*b[k][j] for k in range(len(a))) for j in range(len(a))] for i in range(len(a))]

import numpy as np
def matrix_mult(a, b):
  return np.dot(a,b).tolist()

def matrix_mult(a, b):
  n = len(a)
  x = [[0 for i in range(n)] for j in range(n)]
  
  for k in range(n):
      for i in range(n):
          for j in range(n):
              x[i][j] += a[i][k] * b[k][j]
          
  return x

class Matrix:
    def __init__(self, data):
        self._data = data
        self._rows = len(data)
        self._columns = len(data[0])


    @classmethod
    def empty(cls, rows, columns):
        data = [[None for _ in range(columns)] for _ in range(rows)]

        return cls(data)


    def __getitem__(self, key):
        return self._data[key]
        
      
    @property
    def data(self):
        return self._data


    @property
    def rows(self):
        return self._rows


    @property
    def columns(self):
        return self._columns


    def row(self, row_index):
        for elem in self._data[row_index]:
            yield elem


    def column(self, column_index):
        for row in self._data:
            yield row[column_index]


    def __str__(self):
        str_rows = []
        for row in self._data:
            str_rows.append('  '.join(map(str, row)))

        return "\n".join(str_rows)


    def __mul__(self, matrix):
        result = Matrix.empty(self.rows, self.columns)

        for result_row in range(result.rows):
            for result_column in range(result.columns):
                current_row = self.row(result_row)
                current_column = matrix.column(result_column)

                result_element = 0
                while True:
                    try:
                        row_element = next(current_row)
                        column_element = next(current_column)
                    except StopIteration:
                        break
                    result_element += row_element * column_element

                result[result_row][result_column] = result_element

        return result


def matrix_mult(a, b):
  product = Matrix(a) * Matrix(b)
  return product.data
  
  
  


def matrix_mult(a, b):
    b = list(zip(*b))
    return [[ sum(n*m for n,m in zip(a[x], b[y])) for y in range(len(a))] for x in range(len(a))]

def matrix_mult(a, b):
  C = []
  for i in range(len(a)):
      row=[]
      for j in range(len(a)):
          z=0
          for k in range(len(a)):
              z += a[i][k]*b[k][j]
          row.append(z)     
      C.append(row)
  return C

import numpy as np
def matrix_mult(a, b):
  a = np.array(a)
  b = np.array(b)
  return np.matmul(a,b).tolist()


def matrix_mult(A, B):
  # Multiply two lists index by index
  item_multiply = lambda x, y: sum(x[i]*y[i] for i in range(len(x)))
  # Reverse the matrix to allow index by index multiplication
  B = [[j[i] for j in B] for i in range(len(B[0]))]
  result = []
  # Go line by line on matrix a
  cycles = list(range(len(A)))
  for k in cycles:
      items = []
      # Add all lists multiplications to a list
      for l in cycles:
          items.append(item_multiply(A[k], B[l]))
      # Then add these multiplications list to the result
      result.append(items)
  return result


def matrix_mult(a, b):
  c = [[0]*len(a) for i in range(len(a))]
  for i in range(len(a)):
      for j in range(len(a)):
          for n in range(len(a)):
              c[i][j]+=a[i][n]*b[n][j]
  return c