import numpy as np

"""
Explanation: <a href="https://colab.research.google.com/github/OSGeoLabBp/tutorials/blob/master/english/python/numpy_tutor.ipynb" target="_parent"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>
Numpy in a Nutshell
Numpy is a very popular Python package for numerical calculations (matrices, linear algebra, etc.)
Numpy, as other Python packages have to be imported. Usually an np alias is used.
End of explanation
"""


a = np.zeros(9).reshape(3,3)  # 3 x 3 matrix filled with zeros
print(a)
print(a.dtype)
a1 = np.zeros((3, 3))         # same matrix, note the tuple parameter

"""
Explanation: Creating and filling arrays
Matrix filled with zeros.
End of explanation
"""


b = np.random.rand(6)   # random numbers between 0-1
print(b)

"""
Explanation: Vector filled with random number.
End of explanation
"""


a1 = np.full((3, 4), 8)
print(a1)

"""
Explanation: Matrix filled with constant.
End of explanation
"""


i = np.eye(4)
print(i)

"""
Explanation: Identity matrix.
End of explanation
"""


c = np.array([[1, 2, 3], [2, 4, 6]], dtype=np.int32)    # default data type is float64

"""
Explanation: Create matrix from list and set data type.
End of explanation
"""


print(c.shape)      # return a tuple
print(c.size)

"""
Explanation: Dimensions and size of a matrix.
End of explanation
"""


d = np.arange(10)   # integer values from 0 to 9
print(d)
e = np.arange(2, 11, 2)   # even numbers from 2 to 10
print(e)
f = np.arange(0.1, 1, 0.1)   # for float numbers
print(f)
f1 = np.linspace(0.1, 0.9, 9) # same as above but start, end, number of items
print(f1)

"""
Explanation: Fill matrix with a series of numbers.
End of explanation
"""


t1 = np.arange(80).reshape(10, 8)
print(t1)

print(t1[0, 0])   # first row, first column
print(t1[0][0])   # same as above
print(t1[2])      # third row
print(t1[:,1])    # second column
print(t1[::2])    # odd rows (every second)
print(t1[t1 % 3 == 0])  # elements divisible by three

"""
Explanation: Accessing elements of an array
Indexing similar to list indexing. First element is at zero index.
End of explanation
"""


a1 = np.full((3, 4), 8)
a2 = np.arange(12).reshape(3, 4)
print(a1 * 2)       # scalar times matrix
print(np.sqrt(a2))  # square root of all elements
print(a1 - a2)      # difference of two matrices
print(a1 * a2)      # element wise multiplication!!!

"""
Explanation: Operations with matrices
Operations element by element
End of explanation
"""


b1 = np.arange(12).reshape(4, 3)
print(b1.transpose().dot(b1))           # matrix multiplication with tranpose
print(b1.T.dot(b1))                     # same as above
print(np.linalg.inv(b1.T.dot(b1)))      # matrix inverse

"""
Explanation: Matrix operations
End of explanation
"""


import matplotlib.pyplot as plt
from math import sqrt
pnts = np.array([[1.1, 0.4], [2.6, 1.9], [4.2, 3.0], [7.0, 3.1], [8.2, 2.4], [9.6, 1.2]])
plt.plot(pnts[:,0], pnts[:,1], "o")

c = np.polyfit(pnts[:,0], pnts[:,1], 2)                 # parabola fitting
v = np.polyval(c, pnts[:,0]) - pnts[:,1]               # corrections for y coordinates
rms = sqrt(np.sum(v**2) / pnts.shape[0])                # RMS error
print(c)
x = np.linspace(np.min(pnts[:,0]), np.max(pnts[:,0]), 100)
plt.plot(x, np.polyval(c, x))
plt.plot(pnts[:,0], pnts[:,1], "o")

"""
Explanation: Practical examples
Polynomial fitting
Let's fit a polynomial on 2D points using least squares method. 
For visualization, we will use another python module called matplotlib.
End of explanation
"""


A = np.array([[3, 4, 2], [-1, 1, 3], [3, -4, 1]])
b = np.array([21, -6, -7])
x = np.linalg.solve(A, b)
print(x)

"""
Explanation: Linear equation system
Let's solve the following linear system
3x + 4y + 2z = 21
-x +  y + 3z = -6
3x - 4y +  z = -7
End of explanation
"""


